@apollo/client#useSubscription TypeScript Examples
The following examples show how to use
@apollo/client#useSubscription.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Simulator.tsx From glific-frontend with GNU Affero General Public License v3.0 | 4 votes |
Simulator: React.FC<SimulatorProps> = ({
showSimulator,
setSimulatorId,
simulatorIcon = true,
message,
flowSimulator,
isPreviewMessage,
resetMessage,
getFlowKeyword,
interactiveMessage,
showHeader = true,
hasResetButton = false,
}: SimulatorProps) => {
const [inputMessage, setInputMessage] = useState('');
const [simulatedMessages, setSimulatedMessage] = useState<any>();
const [isOpen, setIsOpen] = useState(false);
// Template listing
const [isDrawerOpen, setIsDrawerOpen] = useState<Boolean>(false);
const [selectedListTemplate, setSelectedListTemplate] = useState<any>(null);
const variables = { organizationId: getUserSession('organizationId') };
let messages: any[] = [];
let simulatorId = '';
const sender = {
name: '',
phone: '',
};
// chat messages will be shown on simulator
const isSimulatedMessage = true;
const sendMessage = (senderDetails: any, interactivePayload?: any, templateValue?: any) => {
const sendMessageText = inputMessage === '' && message ? message : inputMessage;
// check if send message text is not empty
if (!sendMessageText && !interactivePayload && !templateValue) return;
let type = 'text';
let payload: any = {};
let context: any = {};
if (interactivePayload) {
type = interactivePayload.payload.type;
payload = interactivePayload.payload;
delete payload.type;
context = interactivePayload.context;
} else if (templateValue) {
payload.text = templateValue;
} else {
payload.text = sendMessageText;
}
axios
.post(GUPSHUP_CALLBACK_URL, {
type: 'message',
payload: {
id: uuidv4(),
type,
payload,
sender: senderDetails,
context,
},
})
.catch((error) => {
// add log's
setLogs(
`sendMessageText:${sendMessageText} GUPSHUP_CALLBACK_URL:${GUPSHUP_CALLBACK_URL}`,
'info'
);
setLogs(error, 'error');
});
setInputMessage('');
// reset the message from floweditor for the next time
if (resetMessage) {
resetMessage();
}
};
const [loadSimulator, { data: allConversations, subscribeToMore }] = useLazyQuery(
SIMULATOR_SEARCH_QUERY,
{
fetchPolicy: 'network-only',
nextFetchPolicy: 'cache-only',
onCompleted: ({ search }) => {
if (subscribeToMore) {
const subscriptionVariables = { organizationId: getUserSession('organizationId') };
// message received subscription
subscribeToMore({
document: SIMULATOR_MESSAGE_RECEIVED_SUBSCRIPTION,
variables: subscriptionVariables,
updateQuery: (prev, { subscriptionData }) =>
updateSimulatorConversations(prev, subscriptionData, 'RECEIVED'),
});
// message sent subscription
subscribeToMore({
document: SIMULATOR_MESSAGE_SENT_SUBSCRIPTION,
variables: subscriptionVariables,
updateQuery: (prev, { subscriptionData }) =>
updateSimulatorConversations(prev, subscriptionData, 'SENT'),
});
if (search.length > 0) {
sendMessage({ name: search[0].contact.name, phone: search[0].contact.phone });
}
}
},
}
);
const { data: simulatorSubscribe }: any = useSubscription(SIMULATOR_RELEASE_SUBSCRIPTION, {
variables,
});
useEffect(() => {
if (simulatorSubscribe) {
try {
const userId = JSON.parse(simulatorSubscribe.simulatorRelease).simulator_release.user_id;
if (userId.toString() === getUserSession('id')) {
setSimulatorId(0);
}
} catch (error) {
setLogs('simulator release error', 'error');
}
}
}, [simulatorSubscribe]);
const [getSimulator, { data }] = useLazyQuery(GET_SIMULATOR, {
fetchPolicy: 'network-only',
onCompleted: (simulatorData) => {
if (simulatorData.simulatorGet) {
loadSimulator({ variables: getSimulatorVariables(simulatorData.simulatorGet.id) });
setSimulatorId(simulatorData.simulatorGet.id);
} else {
setNotification(
'Sorry! Simulators are in use by other staff members right now. Please wait for it to be idle',
'warning'
);
}
},
});
const [releaseSimulator]: any = useLazyQuery(RELEASE_SIMULATOR, {
fetchPolicy: 'network-only',
});
if (allConversations && data && data.simulatorGet) {
// currently setting the simulated contact as the default receiver
const simulatedContact = allConversations.search.filter(
(item: any) => item.contact.id === data.simulatorGet.id
);
if (simulatedContact.length > 0) {
messages = simulatedContact[0].messages;
simulatorId = simulatedContact[0].contact.id;
sender.name = simulatedContact[0].contact.name;
sender.phone = simulatedContact[0].contact.phone;
}
}
const getStyleForDirection = (direction: string, isInteractive: boolean): string => {
const simulatorClasses = [styles.ReceivedMessage, styles.InteractiveReceivedMessage];
if (isInteractive && direction === 'received') {
return simulatorClasses.join(' ');
}
if (direction === 'send') {
return styles.SendMessage;
}
return styles.ReceivedMessage;
};
const releaseUserSimulator = () => {
releaseSimulator();
setSimulatorId(0);
};
const handleOpenListReplyDrawer = (items: any) => {
setSelectedListTemplate(items);
setIsDrawerOpen(true);
};
const sendMediaMessage = (type: string, payload: any) => {
axios
.post(GUPSHUP_CALLBACK_URL, {
type: 'message',
payload: {
id: uuidv4(),
type,
payload,
sender: {
// this number will be the simulated contact number
phone: data ? data.simulatorGet?.phone : '',
name: data ? data.simulatorGet?.name : '',
},
},
})
.catch((error) => {
// add log's
setLogs(`sendMediaMessage:${type} GUPSHUP_CALLBACK_URL:${GUPSHUP_CALLBACK_URL}`, 'info');
setLogs(error, 'error');
});
};
const renderMessage = (
messageObject: any,
direction: string,
index: number,
isInteractive: boolean = false
) => {
const { insertedAt, type, media, location, interactiveContent, bspMessageId, templateType } =
messageObject;
const messageType = isInteractive ? templateType : type;
const { body, buttons } = WhatsAppTemplateButton(isInteractive ? '' : messageObject.body);
// Checking if interactive content is present then only parse to JSON
const content = interactiveContent && JSON.parse(interactiveContent);
let isInteractiveContentPresent = false;
let template;
if (content) {
isInteractiveContentPresent = !!Object.entries(content).length;
if (isInteractiveContentPresent && messageType === INTERACTIVE_LIST) {
template = (
<>
<ListReplyTemplate
{...content}
bspMessageId={bspMessageId}
showHeader={showHeader}
component={SimulatorTemplate}
onGlobalButtonClick={handleOpenListReplyDrawer}
/>
<TimeComponent direction={direction} insertedAt={insertedAt} />
</>
);
}
if (isInteractiveContentPresent && messageType === INTERACTIVE_QUICK_REPLY) {
template = (
<QuickReplyTemplate
{...content}
isSimulator
showHeader={showHeader}
disabled={isInteractive}
bspMessageId={bspMessageId}
onQuickReplyClick={(value: any) => sendMessage(sender, value)}
/>
);
}
}
return (
<div key={index}>
<div className={getStyleForDirection(direction, isInteractiveContentPresent)}>
{isInteractiveContentPresent && direction !== 'send' ? (
template
) : (
<>
<ChatMessageType
type={messageType}
media={media}
body={body}
location={location}
isSimulatedMessage={isSimulatedMessage}
/>
<TimeComponent direction={direction} insertedAt={insertedAt} />
</>
)}
</div>
<div className={styles.TemplateButtons}>
<TemplateButtons
template={buttons}
callbackTemplateButtonClick={(value: string) => sendMessage(sender, null, value)}
isSimulator
/>
</div>
</div>
);
};
const getChatMessage = () => {
const chatMessage = messages
.map((simulatorMessage: any, index: number) => {
if (simulatorMessage.receiver.id === simulatorId) {
return renderMessage(simulatorMessage, 'received', index);
}
return renderMessage(simulatorMessage, 'send', index);
})
.reverse();
setSimulatedMessage(chatMessage);
};
const getPreviewMessage = () => {
if (message && message.type) {
const previewMessage = renderMessage(message, 'received', 0);
if (['STICKER', 'AUDIO'].includes(message.type)) {
setSimulatedMessage(previewMessage);
} else if (message.body || message.media?.caption) {
setSimulatedMessage(previewMessage);
} else {
// To get rid of empty body and media caption for preview HSM
setSimulatedMessage('');
}
}
if (interactiveMessage) {
const { templateType, interactiveContent } = interactiveMessage;
const previewMessage = renderMessage(interactiveMessage, 'received', 0, true);
setSimulatedMessage(previewMessage);
if (templateType === INTERACTIVE_LIST) {
const { items } = JSON.parse(interactiveContent);
setSelectedListTemplate(items);
} else {
setIsDrawerOpen(false);
}
}
};
// to display only preview for template
useEffect(() => {
if (isPreviewMessage) {
getPreviewMessage();
}
}, [message]);
// for loading conversation
useEffect(() => {
if (allConversations && data) {
getChatMessage();
}
}, [data, allConversations]);
// for sending message to Gupshup
useEffect(() => {
if (!isPreviewMessage && message && data) {
sendMessage(sender);
}
}, [message]);
useEffect(() => {
if (isPreviewMessage && interactiveMessage) {
getPreviewMessage();
}
// Cleaning up simulator when switching between templates
if (!interactiveMessage) {
setSimulatedMessage(null);
setIsDrawerOpen(false);
}
}, [interactiveMessage]);
const messageRef = useCallback(
(node: any) => {
if (node) {
const nodeCopy = node;
setTimeout(() => {
nodeCopy.scrollTop = node.scrollHeight;
}, 100);
}
},
[messages]
);
const handleAttachmentClick = (media: any) => {
const { name: type, payload } = media;
const mediaUrl = document.querySelector('#media');
if (mediaUrl) {
const url = mediaUrl.getAttribute('data-url');
if (url) {
payload.url = url;
}
}
sendMediaMessage(type, payload);
setIsOpen(false);
};
const handleListReplyDrawerClose = () => {
setIsDrawerOpen(false);
setSelectedListTemplate(null);
};
const handleListDrawerItemClick = (payloadObject: any) => {
sendMessage(sender, payloadObject);
handleListReplyDrawerClose();
};
const dropdown = (
<ClickAwayListener onClickAway={() => setIsOpen(false)}>
<div className={styles.Dropdown} id="media">
{SAMPLE_MEDIA_FOR_SIMULATOR.map((media: any) => (
<Button
onClick={() => handleAttachmentClick(media)}
key={media.id}
className={styles.AttachmentOptions}
>
<MessageType type={media.id} color="dark" />
</Button>
))}
</div>
</ClickAwayListener>
);
const simulator = (
<Draggable>
<div className={styles.SimContainer}>
<div>
<div id="simulator" className={styles.Simulator}>
{!isPreviewMessage && (
<>
<ClearIcon
className={styles.ClearIcon}
onClick={() => {
releaseUserSimulator();
}}
data-testid="clearIcon"
/>
{hasResetButton && (
<ResetIcon
data-testid="resetIcon"
className={styles.ResetIcon}
onClick={() => {
if (getFlowKeyword) {
getFlowKeyword();
}
}}
/>
)}
</>
)}
<div className={styles.Screen}>
<div className={styles.Header}>
<ArrowBackIcon />
<img src={DefaultWhatsappImage} alt="default" />
<span data-testid="beneficiaryName">Beneficiary</span>
<div>
<VideocamIcon />
<CallIcon />
<MoreVertIcon />
</div>
</div>
<div className={styles.Messages} ref={messageRef} data-testid="simulatedMessages">
{simulatedMessages}
</div>
{isDrawerOpen && <div className={styles.BackgroundTint} />}
<div className={styles.Controls}>
<div>
<InsertEmoticonIcon className={styles.Icon} />
<input
type="text"
data-testid="simulatorInput"
onKeyPress={(event: any) => {
if (event.key === 'Enter') {
sendMessage(sender);
}
}}
value={inputMessage}
placeholder="Type a message"
disabled={isPreviewMessage}
onChange={(event) => setInputMessage(event.target.value)}
/>
<AttachFileIcon
data-testid="attachment"
className={styles.AttachFileIcon}
onClick={() => setIsOpen(!isOpen)}
/>
{isOpen ? dropdown : null}
<CameraAltIcon className={styles.Icon} />
</div>
<Button
variant="contained"
color="primary"
className={styles.SendButton}
disabled={isPreviewMessage}
onClick={() => sendMessage(sender)}
>
<MicIcon />
</Button>
</div>
{isDrawerOpen && (
<ListReplyTemplateDrawer
drawerTitle="Items"
items={selectedListTemplate}
disableSend={!!interactiveMessage}
onItemClick={handleListDrawerItemClick}
onDrawerClose={handleListReplyDrawerClose}
/>
)}
</div>
</div>
</div>
</div>
</Draggable>
);
const handleSimulator = () => {
// check for the flowkeyword from floweditor
if (getFlowKeyword) {
getFlowKeyword();
}
getSimulator();
};
return (
<>
{showSimulator && simulator}
{simulatorIcon && (
<SimulatorIcon
data-testid="simulatorIcon"
className={showSimulator ? styles.SimulatorIconClicked : styles.SimulatorIconNormal}
onClick={() => {
if (showSimulator) {
releaseUserSimulator();
} else {
handleSimulator();
}
}}
/>
)}
{flowSimulator && (
<div className={styles.PreviewButton}>
<FormButton
variant="outlined"
color="primary"
data-testid="previewButton"
className={styles.Button}
onClick={() => {
if (showSimulator) {
releaseUserSimulator();
} else {
handleSimulator();
}
}}
>
Preview
{showSimulator && <CancelOutlinedIcon className={styles.CrossIcon} />}
</FormButton>
</div>
)}
</>
);
}
Example #2
Source File: SavedSearchToolbar.tsx From glific-frontend with GNU Affero General Public License v3.0 | 4 votes |
SavedSearchToolbar: React.SFC<SavedSearchToolbarProps> = (props) => {
const { searchMode, refetchData, savedSearchCriteriaCallback, onSelect } = props;
const [selectedSavedSearch, setSelectedSavedSearch] = useState<number | null>(null);
const [optionsSelected, setOptionsSelected] = useState(false);
const [fixedSearches, setFixedSearches] = useState<any>([]);
const [searchesCount, setSearchesCount] = useState<any>({});
const [anchorEl, setAnchorEl] = useState(null);
const Ref = useRef(null);
const open = Boolean(anchorEl);
const variables = { organizationId: getUserSession('organizationId') };
const { data: collectionCount } = useSubscription(COLLECTION_COUNT_SUBSCRIPTION, { variables });
const { data: countData } = useQuery<any>(SEARCHES_COUNT, {
variables,
});
useEffect(() => {
if (countData) {
const collectionStats = JSON.parse(countData.collectionStats);
if (collectionStats[variables.organizationId]) {
setSearchesCount(collectionStats[variables.organizationId]);
}
}
}, [countData]);
useEffect(() => {
if (collectionCount) {
const countDataSubscription = JSON.parse(collectionCount.collectionCount);
setSearchesCount(countDataSubscription.collection);
}
}, [collectionCount]);
// default query variables
const queryVariables = {
filter: { isReserved: true },
opts: {},
};
// remove selected searches on search
if (searchMode && selectedSavedSearch) {
setSelectedSavedSearch(null);
}
const { loading, error, refetch } = useQuery<any>(SAVED_SEARCH_QUERY, {
variables: queryVariables,
onCompleted: (data) => {
setFixedSearches(data.savedSearches);
},
});
const handlerSavedSearchCriteria = (
savedSearchCriteria: string | null,
savedSearchId: number | null
) => {
savedSearchCriteriaCallback(savedSearchCriteria, savedSearchId);
setSelectedSavedSearch(savedSearchId);
};
const handleAdditionalSavedSearch = (search: any) => {
const replaceSearchIndex = fixedSearches
.map((savedSearch: any) => savedSearch.id)
.indexOf(search.id);
const fixedSearchesCopy = JSON.parse(JSON.stringify(fixedSearches));
if (replaceSearchIndex !== -1) {
[fixedSearchesCopy[replaceSearchIndex], fixedSearchesCopy[2]] = [
fixedSearches[2],
fixedSearches[replaceSearchIndex],
];
setFixedSearches(fixedSearchesCopy);
}
handlerSavedSearchCriteria(search.args, search.id);
};
useEffect(() => {
// display created searches
if (refetchData.savedSearches) {
refetch();
handleAdditionalSavedSearch(refetchData.savedSearches);
}
}, [refetchData.savedSearches]);
if (loading) return <Loading />;
if (error) {
setErrorMessage(error);
return <div>error</div>;
}
const savedSearchList = fixedSearches.slice(0, 3).map((savedSearch: any) => {
// set the selected class if the button is clicked
const labelClass = [styles.SavedSearchItemLabel];
const countClass = [styles.SavedSearchCount];
if (savedSearch.id === selectedSavedSearch) {
labelClass.push(styles.SavedSearchItemSelected);
countClass.push(styles.SavedSearchSelectedCount);
}
const count = searchesCount[savedSearch.shortcode] ? searchesCount[savedSearch.shortcode] : 0;
return (
<div
data-testid="savedSearchDiv"
className={styles.SavedSearchItem}
key={savedSearch.id}
onClick={() => {
handlerSavedSearchCriteria(savedSearch.args, savedSearch.id);
onSelect();
}}
onKeyDown={() => {
handlerSavedSearchCriteria(savedSearch.args, savedSearch.id);
onSelect();
}}
aria-hidden="true"
>
<div className={labelClass.join(' ')}>{savedSearch.shortcode}</div>
<Tooltip title={count} placement="right">
<div className={countClass.join(' ')}>{numberToAbbreviation(count)}</div>
</Tooltip>
</div>
);
});
const handleClickAway = () => {
setAnchorEl(null);
setOptionsSelected(false);
};
const additionalOptions = (
<Popper
open={open}
anchorEl={anchorEl}
placement="bottom"
transition
className={styles.PopperContainer}
>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Paper elevation={3} className={styles.Popper}>
{fixedSearches.slice(3, 6).map((search: any) => {
const count = searchesCount[search.shortcode] ? searchesCount[search.shortcode] : 0;
return (
<div
key={search.id}
className={styles.LabelContainer}
onClick={() => handleAdditionalSavedSearch(search)}
aria-hidden="true"
>
<span className={styles.Label}>{search.shortcode}</span>
<span className={styles.Count}>{numberToAbbreviation(count)}</span>
</div>
);
})}
</Paper>
</Fade>
)}
</Popper>
);
return (
<div className={styles.SavedSearchToolbar}>
<div className={styles.SaveSearchContainer}>{savedSearchList}</div>
<div className={styles.MoreLink}>
<ClickAwayListener onClickAway={handleClickAway}>
<IconButton
onClick={() => {
setAnchorEl(Ref.current);
setOptionsSelected(true);
}}
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
size="small"
ref={Ref}
>
{optionsSelected ? (
<OptionsIconSelected className={styles.OptionsIcon} />
) : (
<OptionsIcon className={styles.OptionsIcon} />
)}
</IconButton>
</ClickAwayListener>
{additionalOptions}
</div>
</div>
);
}