UNPKG

replyke-rn

Version:

Replyke React Native components: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.

277 lines 15.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = GiphyContainer; var jsx_runtime_1 = require("react/jsx-runtime"); var react_1 = require("react"); var react_native_gesture_handler_1 = require("react-native-gesture-handler"); var react_native_1 = require("react-native"); var icons_1 = require("../icons"); var MAX_ITEMS = 60; // Define the maximum number of items to load var FETCH_LIMIT = 30; function GiphyContainer(_a) { var _this = this; var onClickBack = _a.onClickBack, onSelectGif = _a.onSelectGif, giphyApiKey = _a.giphyApiKey, visible = _a.visible; var _b = (0, react_1.useState)([]), gifs = _b[0], setGifs = _b[1]; var _c = (0, react_1.useState)(false), loading = _c[0], setLoading = _c[1]; var _d = (0, react_1.useState)(false), loadingMore = _d[0], setLoadingMore = _d[1]; var _e = (0, react_1.useState)(0), currentOffset = _e[0], setCurrentOffset = _e[1]; var _f = (0, react_1.useState)(0), totalCount = _f[0], setTotalCount = _f[1]; var _g = (0, react_1.useState)(0), flatListWidth = _g[0], setFlatListWidth = _g[1]; // Track the width of the FlatList // Search states & debouncing var _h = (0, react_1.useState)(""), query = _h[0], setQuery = _h[1]; var _j = (0, react_1.useState)(""), debouncedQuery = _j[0], setDebouncedQuery = _j[1]; // Refs to track fetching state and gifs length var isFetchingRef = (0, react_1.useRef)(false); var gifsLengthRef = (0, react_1.useRef)(0); // Debounce effect (like in the web version) (0, react_1.useEffect)(function () { var handler = setTimeout(function () { setDebouncedQuery(query); }, 500); // 500ms debounce return function () { return clearTimeout(handler); }; }, [query]); // Update gifsLengthRef when gifs change (0, react_1.useEffect)(function () { gifsLengthRef.current = gifs.length; }, [gifs]); // Fetch function that decides between trending or search var fetchGifs = (0, react_1.useCallback)(function () { var args_1 = []; for (var _i = 0; _i < arguments.length; _i++) { args_1[_i] = arguments[_i]; } return __awaiter(_this, __spreadArray([], args_1, true), void 0, function (offset) { var url, trimmed, res, json, remainingItems, fetchedItems_1, err_1; if (offset === void 0) { offset = 0; } return __generator(this, function (_a) { switch (_a.label) { case 0: // Prevent multiple fetches if (isFetchingRef.current) { return [2 /*return*/]; } // Check if we've reached the maximum limit if (Math.max(gifsLengthRef.current, offset) >= MAX_ITEMS) { return [2 /*return*/]; } isFetchingRef.current = true; if (offset === 0) { setLoading(true); setLoadingMore(false); } else { setLoadingMore(true); } url = ""; trimmed = debouncedQuery.trim(); if (trimmed.length === 0) { // Fetch trending if no query url = "https://api.giphy.com/v1/gifs/trending?api_key=".concat(giphyApiKey, "&limit=").concat(FETCH_LIMIT, "&offset=").concat(offset); } else { // Fetch search results url = "https://api.giphy.com/v1/gifs/search?api_key=".concat(giphyApiKey, "&q=").concat(encodeURIComponent(trimmed), "&limit=").concat(FETCH_LIMIT, "&offset=").concat(offset); } _a.label = 1; case 1: _a.trys.push([1, 4, 5, 6]); return [4 /*yield*/, fetch(url)]; case 2: res = _a.sent(); return [4 /*yield*/, res.json()]; case 3: json = (_a.sent()); if (json.meta.status !== 200) { console.error("API Error:", json.meta.msg); return [2 /*return*/]; } setTotalCount(json.pagination.total_count); setCurrentOffset(json.pagination.offset + json.pagination.count); remainingItems = MAX_ITEMS - gifsLengthRef.current; fetchedItems_1 = json.data.slice(0, remainingItems); if (offset === 0) { setGifs(fetchedItems_1); } else { setGifs(function (prevGifs) { return __spreadArray(__spreadArray([], prevGifs, true), fetchedItems_1, true); }); } return [3 /*break*/, 6]; case 4: err_1 = _a.sent(); console.error("Fetch Error:", err_1); return [3 /*break*/, 6]; case 5: if (offset === 0) { setLoading(false); } else { setLoadingMore(false); } isFetchingRef.current = false; return [7 /*endfinally*/]; case 6: return [2 /*return*/]; } }); }); }, [debouncedQuery, giphyApiKey]); // Fetch more GIFs when reaching the end var fetchMoreGifs = (0, react_1.useCallback)(function () { if (loading || loadingMore || gifsLengthRef.current >= totalCount || gifsLengthRef.current >= MAX_ITEMS || isFetchingRef.current) { return; } fetchGifs(currentOffset); }, [loading, loadingMore, totalCount, currentOffset, fetchGifs]); // Reset search and pagination when visibility or query changes (0, react_1.useEffect)(function () { if (!visible) { // Reset all states when not visible setQuery(""); setDebouncedQuery(""); setGifs([]); setCurrentOffset(0); setTotalCount(0); setLoading(false); setLoadingMore(false); isFetchingRef.current = false; } else { // When visibility changes to true, reset offset and fetch initial gifs setCurrentOffset(0); setTotalCount(0); setGifs([]); fetchGifs(0); } }, [visible, debouncedQuery, fetchGifs]); // Handle ScrollView layout to dynamically calculate column width var handleLayout = function (event) { var width = event.nativeEvent.layout.width; setFlatListWidth(width); }; var renderMasonryColumns = function () { if (!flatListWidth) return null; var padding = 16; // Total horizontal padding var columnSpacing = 8; // Spacing between columns var columnWidth = (flatListWidth - padding - columnSpacing) / 2; // Two columns // Split data into two columns var columns = [[], []]; gifs.forEach(function (gif, index) { columns[index % 2].push(gif); // Alternately add to each column }); return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { flexDirection: "row", justifyContent: "space-between", paddingHorizontal: 4, }, children: columns.map(function (column, colIndex) { return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { flex: 1, marginHorizontal: 4 }, children: column.map(function (item) { var aspectRatio = parseInt(item.images.fixed_width.height) / parseInt(item.images.fixed_width.width); return ((0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: { marginBottom: 8 }, onPress: function () { return onSelectGif({ id: item.id, url: item.url, aspectRatio: aspectRatio, gifUrl: item.images.fixed_width.webp, gifPreviewUrl: item.images.preview_gif.webp, altText: item.title, }); }, children: (0, jsx_runtime_1.jsx)(react_native_1.Image, { source: { uri: item.images.fixed_width.webp }, style: { width: columnWidth, // Set fixed width height: columnWidth * aspectRatio, // Calculate height using aspect ratio borderRadius: 4, }, resizeMode: "cover" // Equivalent to contentFit="cover" }) }, item.id)); }) }, colIndex)); }) })); }; return ((0, jsx_runtime_1.jsxs)(react_native_1.Animated.View, { style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "white", zIndex: 999, opacity: visible ? 1 : 0, pointerEvents: visible ? "auto" : "none", }, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { flexDirection: "row", padding: 8, alignItems: "stretch", gap: 8, }, children: [(0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: { backgroundColor: "#e5e7eb", aspectRatio: 1, // Ensures width equals height alignItems: "center", justifyContent: "center", borderRadius: 8, // Keeps it rounded }, onPress: onClickBack, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: { color: "#888", fontSize: 22, lineHeight: 22 }, children: "\u2190" }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { flex: 1, flexDirection: "row", backgroundColor: "#e5e7eb", borderRadius: 8, paddingHorizontal: 16, alignItems: "center", gap: 12, }, children: [(0, jsx_runtime_1.jsx)(icons_1.MagnifyingGlassIcon, { width: 16, color: "#888" }), (0, jsx_runtime_1.jsx)(react_native_1.TextInput, { style: { flex: 1, paddingVertical: 12, fontSize: 15, }, placeholder: "Search GIPHY", onChangeText: function (value) { return setQuery(value); }, value: query })] })] }), loading && gifs.length === 0 ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: { textAlign: "center", marginTop: 16 }, children: "Loading..." })) : ((0, jsx_runtime_1.jsxs)(react_native_gesture_handler_1.ScrollView, { onLayout: handleLayout, onScroll: function (_a) { var nativeEvent = _a.nativeEvent; var layoutMeasurement = nativeEvent.layoutMeasurement, contentOffset = nativeEvent.contentOffset, contentSize = nativeEvent.contentSize; var currentScroll = contentOffset.y + layoutMeasurement.height; var threshold = contentSize.height * 0.8; // 80% scroll if (currentScroll >= threshold) { fetchMoreGifs(); } }, scrollEventThrottle: 16, children: [renderMasonryColumns(), loadingMore && ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: { textAlign: "center", marginVertical: 16 }, children: "Loading more..." }))] }))] })); } //# sourceMappingURL=GiphyContainer.js.map