fastcomments-react-native-sdk
Version:
React Native FastComments Components. Add live commenting to any React Native application.
172 lines (171 loc) • 7.45 kB
JavaScript
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] }) })] }) });
}