UNPKG

fastcomments-react-native-sdk

Version:

React Native FastComments Components. Add live commenting to any React Native application.

172 lines (171 loc) 7.45 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { ActivityIndicator, Alert, BackHandler, FlatList, Image, Text, TextInput, TouchableOpacity, useWindowDimensions, View } from "react-native"; import { FastCommentsImageAsset } from "../types"; import { useEffect, useRef, useState } from "react"; import { createURLQueryString, getAPIHost, makeRequest } from "../services/http"; import { getMergedTranslations } from "../services/translations"; export function GifBrowser({ cancelled, config, imageAssets, pickedGIF, styles }) { const [isInitialLoad, setIsInitialLoad] = useState(true); const [isFetching, setIsFetching] = useState(false); const [hasMore, setHasMore] = useState(true); const [searchText, setSearchText] = useState(''); const [translations, setTranslations] = useState({}); const [gifs, setGifs] = useState([]); const lastRequestTime = useRef(0); const lastGifsRequest = useRef({ search: null, page: 0, locale: config.locale, rating: config.gifRating }); const { width } = useWindowDimensions(); function setGifsDeDupe(newList) { const pathMap = {}; const result = []; for (const entry of newList) { if (!pathMap[entry[0]]) { pathMap[entry[0]] = true; result.push(entry); } } setGifs(result); } async function getGifs(request) { const isNewTerm = lastGifsRequest.current.search !== request.search; if (!isNewTerm && !hasMore) { return; } if (!isNewTerm && Date.now() - lastRequestTime.current < 5_000) { // incase pagination goes crazy return; } setIsFetching(true); lastGifsRequest.current = request; lastRequestTime.current = Date.now(); const response = await makeRequest({ apiHost: getAPIHost(config), method: 'GET', url: '/gifs/' + (request.search ? 'search/' : 'trending/') + config.tenantId + createURLQueryString(request) }); if (response.status === 'success') { setHasMore(response.images.length >= 25); setIsFetching(false); setIsInitialLoad(false); if (isNewTerm) { setGifsDeDupe(response.images); } else { setGifsDeDupe([ ...gifs, ...response.images ]); } } else { const mergedTranslations = getMergedTranslations(translations, response); Alert.alert(":(", mergedTranslations.ERROR_MESSAGE, [ { text: mergedTranslations.DISMISS } ]); } } async function getGifsTranslations() { let url = '/translations/widgets/comment-ui-gifs?useFullTranslationIds=true'; if (config.locale) { url += '&locale=' + config.locale; } const response = await makeRequest({ apiHost: getAPIHost(config), method: 'GET', url: url }); return response.translations || {}; } async function getCommonTranslations() { let url = '/translations/widgets/comment-ui?useFullTranslationIds=true'; if (config.locale) { url += '&locale=' + config.locale; } const response = await makeRequest({ apiHost: getAPIHost(config), method: 'GET', url: url }); return response.translations || {}; } async function setupTranslations() { const [commonTranslations, gifsTranslations,] = await Promise.all([ getCommonTranslations(), getGifsTranslations(), ]); setTranslations({ ...commonTranslations, ...gifsTranslations, }); } async function initialLoad() { await Promise.all([ setupTranslations(), getGifs(lastGifsRequest.current) ]); } useEffect(() => { const backHandler = BackHandler.addEventListener("hardwareBackPress", () => { cancelled(); return true; }); return () => backHandler.remove(); }, []); useEffect(() => { // noinspection JSIgnoredPromiseFromCall initialLoad(); }, []); const onEndReached = async () => { lastGifsRequest.current.page++; await getGifs(lastGifsRequest.current); }; const handleSelected = async (rawSrc) => { if (rawSrc.includes('fastcomments') || rawSrc.includes('localhost:')) { // support prod and local dev pickedGIF(rawSrc); } else { // TODO show loading const response = await makeRequest({ apiHost: getAPIHost(config), method: 'GET', url: '/gifs/get-large/' + config.tenantId + createURLQueryString({ largeInternalURLSanitized: rawSrc }) }); if (response.status === 'success' && response.src) { pickedGIF(response.src); } else { console.warn('Could not get full version of GIF to use. Error response from API.', response); pickedGIF(rawSrc); // silently pick smaller version :/ } } }; const renderItem = ({ item }) => { return _jsx(TouchableOpacity, { onPressOut: () => handleSelected(item[0]), children: _jsx(Image, { source: { uri: item[0] }, style: [{ width }, styles.gifBrowser?.listImage] }) }); }; const header = _jsx(TextInput, { value: searchText, onChangeText: (newValue) => { setSearchText(newValue); if (Date.now() - lastRequestTime.current > 1_500) { // noinspection JSIgnoredPromiseFromCall getGifs({ ...lastGifsRequest.current, search: searchText }); } }, style: styles.gifBrowser?.searchInput, placeholder: translations.GIF_SEARCH_PLACEHOLDER, onSubmitEditing: () => { // noinspection JSIgnoredPromiseFromCall getGifs({ ...lastGifsRequest.current, search: searchText }); }, returnKeyType: 'search' }); return _jsx(View, { style: styles.gifBrowser?.centeredView, children: _jsxs(View, { style: styles.gifBrowser?.modalView, children: [isInitialLoad ? _jsx(ActivityIndicator, { size: "large" }) : _jsx(FlatList, { style: styles.gifBrowser?.list, data: gifs, keyExtractor: (item) => item[0], inverted: false, maxToRenderPerBatch: 10, onEndReachedThreshold: 0.3, onEndReached: onEndReached, renderItem: renderItem, ListEmptyComponent: _jsx(Text, { style: styles.gifBrowser?.noResultsMessage, children: translations.GIF_SEARCH_NO_RESULTS_FOUND }), ListHeaderComponent: header, ListFooterComponent: _jsx(View, { children: isFetching ? _jsx(ActivityIndicator, { size: "large" }) : null }) }), _jsx(TouchableOpacity, { style: styles.gifBrowser?.modalCancel, onPress: cancelled, children: _jsx(Image, { style: styles.gifBrowser?.modalCancelImage, source: imageAssets[config.hasDarkBackground ? FastCommentsImageAsset.ICON_CROSS_WHITE : FastCommentsImageAsset.ICON_CROSS] }) })] }) }); }