Inserting Middleware(insights) with Instantsearch react - reactjs

So, we have a functional search with Algolia/Instantsearch/React/Nextjs. But the Insights middleware is currently not setup.
Below is a trimmed version of the implementation, we use custom widgets to have more fine control over the display of results.
We use the hooks implementation for the custom widgets like so
const { hits, sendEvent, ... } = useInfiniteHits(props)
import { useState } from 'react'
import algoliasearch from 'algoliasearch/lite'
import { InstantSearch, InstantSearchSSRProvider } from 'react-instantsearch-hooks-web'
import SearchBox from '#components/swatches/algolia/SearchBox'
import Hits from '#components/swatches/algolia/Hits'
import RefinementList from '#components/swatches/algolia/RefinementList'
import CurrentRefinements from '#components/swatches/algolia/CurrentRefinements'
import { getServerState } from 'react-instantsearch-hooks-server'
import Container from 'react-bootstrap/Container'
import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import { history } from 'instantsearch.js/es/lib/routers/index.js'
import styles from '#styles/page.module.scss'
const Page = ({ serverState, url }) => {
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_INDEX_ID,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY
)
return (
<div className={styles.wrapper}>
<InstantSearchSSRProvider {...serverState}>
<InstantSearch
searchClient={searchClient}
indexName={process.env.NEXT_PUBLIC_ALGOLIA_INDEX}
routing={{
router: history({
getLocation: () =>
typeof window === 'undefined' ? new URL(url) : window.location,
}),
}}
>
<Container fluid="lg">
<div className="mb-3">
<SearchBox />
</div>
<CurrentRefinements />
<Hits />
</Container>
</InstantSearch>
</InstantSearchSSRProvider>
</div>
)
}
export default Page
export async function getServerSideProps({ req, res, resolvedUrl}) {
const protocol = req.headers.referer?.split('://')[0] || 'https';
const url = `${protocol}://${req.headers.host}${req.url}`;
const serverState = await getServerState(<Page url={url} />);
return {
props: {
serverState,
url,
},
}
}
So my question is, where do we hook up the insights middleware for this specific implementation?
Reading the docs, (https://www.algolia.com/doc/api-reference/widgets/instantsearch/react-hooks/) I'm not really 100% sure where to start. I can't find anywhere in the instantsearch react docs where it references anyway to configure that sort of thing.
Am I better of just firing events at the API directly instead of with InstantSearch?
Thanks

The trick is finding the InstantSearch instance using useInstantSearch:
const instantSearch = useInstantSearch();
instantSearch.use(middleware)
The docs should tell you what to do from there.

Related

React variable image relative to url

I would like to change the background image depending on the view file/URL. I don't quite know how to do this, I have 3 different images and 3 different views of Mindhunter, Sherlock etc. On "/mindhunter" there should be a mindhunter banner and on "/sherlock" a sherlock banner
Background.js
import React from 'react';
import sherlockBaner from 'assets/SherlockBaner.svg';
const Background = () => {
return (
<>
<div>
<img className="baner" src={sherlockBaner} alt="baner" />
</div>
</>
);
};
export default Background;
Mindhunter.js
import React from 'react';
import Background from 'components/Series/Background/Background';
const Mindhunter = () => {
return (
<>
<Background />
</>
);
};
export default Mindhunter;
Depending on how dynamic you want to make this you have two options:
Passing the "src" as a property to Background, and using props.src to set your img src
Using the path in your router to determine which url you have (take a look at react-router and query params) and then pass that into your component, which will then use some switch to select the correct background.
Let me know if you want more information on 1 or 2.
I came up with something like this, it could probably be improved somehow
import React from 'react';
import mindHunterBaner from 'assets/MindhunterBaner.svg';
import sherlockBaner from 'assets/SherlockBaner.svg';
import breakingBadBaner from 'assets/BreakingBadBaner.svg';
import { useLocation } from 'react-router-dom';
const Background = () => {
const Location = useLocation();
let Baner;
if (Location.pathname == '/mindhunter') {
Baner = mindHunterBaner;
} else if (Location.pathname == '/sherlock') {
Baner = sherlockBaner;
} else if (Location.pathname == '/breakingbad') {
Baner = breakingBadBaner;
}
return (
<>
<div>
<img className="baner" src={Baner} alt="baner" />
</div>
</>
);
};
export default Background;

Creating default chat groups in Sendbird using React

We've integrated a chat UI into a project using Sendbird. The chat interface is now working and what I am trying to do now is implement a feature where there are 2 default chat groups as shown in the mockup below:
I have already gone through the docs but I can’t seem to find the information I need to implement this feature. Can this be implemented? can someone guide me to the right direction, please?
import React, { useEffect, useState, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import { useSelector } from 'react-redux';
import 'sendbird-uikit/dist/index.css';
import { App as SendBirdApp, } from 'sendbird-uikit';
import { getModuleState as getAuthModuleState } from 'services/auth';
import colorSet from './styled/chatPalette';
import { Chat, ChatContainer, List } from './styled/chatPage';
import ChatGroups from './ChatGroups';
function ChatPage(props) {
const { theme } = props;
const history = useHistory();
const authState = useSelector(getAuthModuleState);
const userId = authState.username;
const nickname = authState.username;
const appId = authState.sendbirdData.appId;
const accessToken = authState.sendbirdData.accessToken;
useEffect(() => {
if (!userId || !nickname) {
console.error('Error, empty userId or nickname');
}
}, [userId, nickname, history]);
return (
<ChatContainer>
<SendBirdApp
appId={appId}
userId={userId}
nickname={nickname}
colorSet={colorSet}
/>
</ChatContainer>
);
}
export default ChatPage;
you can use the <SendbirdProvider> component and provide your custom channel preview component (let's say <ChannelPreview>) inside the <ChannelList> component.
Within your custom preview component (<ChannelPreview>) you can choose wether or not to show a specific channel based on its member count (channel.memberCount) as shown below:
import { Channel, ChannelList, SendBirdProvider } from 'sendbird-uikit';
import 'sendbird-uikit/dist/index.css';
import { useState } from 'react';
const CHANNEL_PREVIEW_MODES = [
'1-on-1',
'Group'
]
function ChannelPreview({channel, previewMode}) {
if (
(channel.memberCount <=2 && previewMode !== CHANNEL_PREVIEW_MODES[0]) ||
(channel.memberCount > 2 && previewMode !== CHANNEL_PREVIEW_MODES[1])
) {
return null
}
return (
<div key={channel.url}>
<img height="20px" width="20px" src={channel.coverUrl}/>
{channel.url}
</div>
)
}
function App() {
const [previewMode, setPreviewMode] = useState(CHANNEL_PREVIEW_MODES[0])
const [currentChannel, setCurrentChannel] = useState(null);
return (
<div className="App">
<SendBirdProvider
userId='<USER_ID>'
appId='<APP_ID>'
>
<div>
{CHANNEL_PREVIEW_MODES.map(mode =>
<label className="preview-mode-radio">{mode}
<input
type='radio'
value={mode}
name='preview-mode'
onChange={() => setPreviewMode(mode)}
checked={previewMode === mode}
/>
</label>
)}
</div>
<ChannelList
renderChannelPreview={({channel}) => <ChannelPreview channel={channel} previewMode={previewMode} />}
onChannelSelect={channel => setCurrentChannel(channel)}
/>
<Channel channelUrl={currentChannel?.url} />
</SendBirdProvider>
</div>
);
}
export default App;

How to serve different versions of the same web app based on subdomain - CSR w/ React

I'm currently trying to create an application that will serve a client side web application that will be mostly the same, but slightly different based on the different subdomains. For example, client1.website.com vs client2.website.com -- same base app, slightly different branding and content.
Currently, I have attempted to save this content by fetching static JSON files that contain the differences between the two sites. They are made as a normal GET call to the local server and then applied as a 'content' key in the state object of the application. The problem I am having is that I am unable to reference parts of the content because React attempts to render them before the parent application is finished applying the 'content' state.
Is this architecture the wrong way to go about things or is there a solution that I just haven't found yet?
I've posted my code below to try and show what I'm doing, I've used a third party state library to try and simplify what something like redux would do when full fleshed out:
// Index.js (Contains my store)
import React from "react";
import axios from "axios";
import ReactDOM from "react-dom";
import { StoreProvider, createStore, thunk, action } from "easy-peasy";
import "./index.css";
import App from "./pages/App";
const store = createStore({
company: {
contentType: "default",
baseContent: {},
partnerContent: {},
setContentType: action((state, payload) => {
state.contentType = payload;
}),
setContentData: action((state, payload) => {
state[payload.type] = payload.data;
}),
loadContent: thunk(async (actions, payload) => {
try {
if (payload !== "base") {
actions.setContentType(payload);
}
const partnerData = await axios.get(`content/${payload}.json`);
const baseData = await axios.get(`content/base.json`);
actions.setContentData({
data: partnerData.data,
type: "partnerContent",
});
actions.setContentData({
data: baseData.data,
type: "baseContent",
});
} catch (e) {
throw e;
}
}),
},
});
ReactDOM.render(
<StoreProvider store={store}>
<App />
</StoreProvider>,
document.getElementById("root")
);
//App.js (Where I attempt to suspend until my data is loaded into state)
import React, { useEffect, Suspense } from "react";
import { useStoreActions } from "easy-peasy";
import Header from "../components/Header";
import Content from "../components/Content";
import "./App.css";
const content = "default";
const App = () => {
const { loadContent } = useStoreActions((actions) => ({
loadContent: actions.payforward.loadContent,
}));
useEffect(() => {
async function fetchData() {
await loadContent(content);
}
fetchData();
}, [loadContent]);
return (
<div className='App'>
<Header />
<Content />
</div>
);
};
export default App;
//Header.js (Where I attempt to reference some URLs from the JSON file applied to my state)
import React, { useMemo } from "react";
import { useStoreState } from "easy-peasy";
const Header = () => {
//Unable to access state because it's currently undefined until the JSON is loaded in
const headerURLs = useStoreState(
(state) => state.company.partnerContent.routes.header.links
);
return (
<div>
<h1>This is the Header</h1>
{/* {headerURLs.map((url) => {
return <p>{url}</p>;
})} */}
</div>
);
};
export { Header as default };

Implement Draft JS into Next JS, Text Editor Won't Show

I am trying to implement draft js as text editor into my next.js project. I have implemented all code based on official guide but the text editor won't show. Here is my code
index.js
import React, { Component } from 'react'
import { useRouter, Router } from 'next/router';
import Layout from '../../components/MyLayout';
import Settings from '../../components/Settings';
import MyEditor from '../../components/TextEditor';
import fetch from 'isomorphic-unfetch';
import {Editor, EditorState} from 'draft-js';
const Post = (props) => {
const router = useRouter();
const object = props.post.data[0];
return (
<Layout>
<h1>{object.title}</h1>
<div className="p-2 border bg-light text-right text-dark">Oleh: {object.username}</div>
<MyEditor/>
</Layout>
);
}
Post.getInitialProps = async function(context) {
const {id} = context.query;
const FormData = new URLSearchParams();
FormData.append('slug',`${id}`);
const res = await fetch(Settings.api+'viewPost',{
'method': 'POST',
'body': FormData
});
const post = await res.json();
console.log('aw');
return {
post
};
};
export default Post;
TextEditor.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';
export default function MyEditor() {
const [editorState, setEditorState] = React.useState(
EditorState.createEmpty()
);
return (
<Editor
editorState={editorState}
onChange={setEditorState}
/>
);
}
I really appreciate any answer. Thank you
It looks like you're doing the right thing - Draft-js is very much a low-level editing tool that you can build rich text-editing on top of - I'm guessing that the editor is actually rendering on the page, but you haven't added any toolbars or complex initial state so you're just seeing a blank page.
I reproduced a super basic Next JS example here: https://codesandbox.io/s/naughty-swirles-7nmbf?file=/pages/index.js and you'll see that the editor itself is 'invisible' as there is no styling applied and no toolbar. If you look further into the Draft-js docs you'll see you can apply initial config objects that will give it a basic style (which you can further customize as you wish).

Is this only possible with external URLs and not local?

I'm trying to make a photo gallery using react-images, the URLs are correct but the photos themselves are not loading into my web app. I get the broken image icon when switching themodalIsOpen:false to true.
Ive tried looking up examples of the same problems and alternatives, like if the component was configured right or if I am extending it right in the class.
import React, { Component } from 'react';
import Carousel, { Modal, ModalGateway } from 'react-images';
import blksmith from '../images/gallery/illustration/Blacksmith.jpg';
import mage from '../images/gallery/illustration/Mage.jpg';
const images =
[
{
src:{blksmith}
} ,
{
src:{mage}
}
];
class illuGallery extends Component {
state = { modalIsOpen: false }
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }));
}
render() {
const { modalIsOpen } = this.state;
return (
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel
views={images}
/>
</Modal>
) : null}
</ModalGateway>
);
}
}
export default illuGallery;
This is in the actual gallery.js file, the web page that renders the gallery.
import React from 'react';
import Layout from "../components/layout";
import IlluPhotos from "../components/illustrationGallery";
import SEO from "../components/seo";
import './gallery.scss';
const GalleryPage = () => {
return (
<Layout>
<div style={{width:'100%',height:'250px'}}>
<SEO title="Gallery" />
<IlluPhotos/>
</div>
</Layout>
)
}
export default GalleryPage;
I am seeking some feedback on how to get this to work and what I did wrong, or what I should explore more.
So I ended up adding the pictures I wanted for the gallery to the public folder as mentioned farther down in this post
Since the https://localhost:8000 was appearing in front of the links to the images I wanted to use.
Thank you all for helping me find the answer!!
You don't need to import images.
According to react-images documentation, you just need to pass path to image as a string to <Carousel> component, like in this example below:
import React from 'react';
import Carousel from 'react-images';
const images = [{ src: 'path/to/image-1.jpg' }, { src: 'path/to/image-2.jpg' }];
class Component extends React.Component {
render() {
return <Carousel views={images} />;
}
}

Resources