import React, { useCallback, memo, useEffect, useState } from "react" import { View, Text, TouchableOpacity, Platform, FlatList } from "react-native" import { storage } from "../lib/storage" import { useMMKVBoolean, useMMKVString } from "react-native-mmkv" import { useStore } from "../lib/state" import Ionicon from "react-native-vector-icons/Ionicons" import { i18n } from "../i18n/i18n" import { getColor } from "../lib/style/colors" import { memoryCache } from "../lib/memoryCache" import { useMountedState } from "react-use" import BackgroundTimer from "react-native-background-timer" import { SheetManager } from "react-native-actions-sheet" export const TransfersScreen = memo(({ navigation, route }) => { const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage) const [lang, setLang] = useMMKVString("lang", storage) const [sortedTransfers, setSortedTransfers] = useState([]) const isMounted = useMountedState() const [currentView, setCurrentView] = useState("ongoing") const [ongoingTransfers, setOngoingTransfers] = useState(0) const [finishedTransfers, setFinishedTransfers] = useState(0) const [ongoingTransfersList, setOngoingTransfersList] = useState([]) const [finishedTransfersList, setFinishedTransfersList] = useState([]) const bottomBarHeight = useStore(useCallback(state => state.bottomBarHeight)) const contentHeight = useStore(useCallback(state => state.contentHeight)) const [topBarHeight, setTopBarHeight] = useState(useStore.getState().topBarHeight) const updateTransfers = useCallback(() => { if(isMounted()){ const transfers = memoryCache.get("transfers") || {} for(let prop in transfers){ if(prop.indexOf("upload") !== -1){ if(typeof transfers['upload:' + transfers[prop].id] == "undefined"){ transfers['upload:' + transfers[prop].id] = transfers[prop] } } else{ if(typeof transfers['download:' + transfers[prop].id] == "undefined"){ transfers['download:' + transfers[prop].id] = transfers[prop] } } } const sortedTransfers = [] let finished = 0 let ongoing = 0 for(let prop in transfers){ const transferDone = transfers[prop].chunksDone >= transfers[prop].file.chunks ? 1 : 0 sortedTransfers.push({ id: prop, type: prop.indexOf("upload") !== -1 ? "upload" : "download", done: transferDone, transfer: transfers[prop] }) if(transferDone){ finished += 1 } else{ ongoing += 1 } } setOngoingTransfers(ongoing) setFinishedTransfers(finished) const finishedList = [] const ongoingList = [] if(sortedTransfers.length > 0){ //const sorted = sortedTransfers.sort((a, b) => { // return a.done > b.done //}) const sorted = sortedTransfers.sort((a, b) => { return (isNaN(Math.round((a.transfer.chunksDone / a.transfer.file.chunks) * 100)) ? 0 : Math.round((a.transfer.chunksDone / a.transfer.file.chunks) * 100)) < (isNaN(Math.round((b.transfer.chunksDone / b.transfer.file.chunks) * 100)) ? 0 : Math.round((b.transfer.chunksDone / b.transfer.file.chunks) * 100)) }) setSortedTransfers(sorted) for(let i = 0; i < sorted.length; i++){ if(sorted[i].done){ finishedList.push(sorted[i]) } else{ ongoingList.push(sorted[i]) } } } setFinishedTransfersList(finishedList) setOngoingTransfersList(ongoingList) BackgroundTimer.setTimeout(updateTransfers, 100) } }) useEffect(() => { updateTransfers() }, []) return ( <View style={{ height: "100%", width: "100%", backgroundColor: darkMode ? "black" : "white" }}> <View style={{ width: "100%", height: Platform.OS == "android" ? 75 : 87 }} onLayout={(e) => setTopBarHeight(e.nativeEvent.layout.height)}> <View style={{ flexDirection: "row", justifyContent: "space-between", backgroundColor: darkMode ? "black" : "white" }}> <View style={{ flexDirection: "row" }}> <TouchableOpacity style={{ marginTop: Platform.OS == "ios" ? 16 : 3, marginLeft: 15, }} onPress={() => navigation.goBack()}> <Ionicon name="chevron-back" size={24} color={darkMode ? "white" : "black"}></Ionicon> </TouchableOpacity> <Text style={{ color: darkMode ? "white" : "black", fontWeight: "bold", fontSize: 22, marginLeft: 10, marginTop: Platform.OS == "ios" ? 15 : 0 }}> {i18n(lang, "transfers")} </Text> </View> <TouchableOpacity hitSlop={{ top: 10, right: 10, left: 10, bottom: 10 }} style={{ alignItems: "flex-end", flexDirection: "row", backgroundColor: "transparent", height: "100%", paddingLeft: 0, paddingRight: 15 }} onPress={() => SheetManager.show("TopBarActionSheet")}> { ongoingTransfers > 0 && ( <View> <Ionicon name="ellipsis-horizontal-sharp" size={24} color={darkMode ? "white" : "black"}></Ionicon> </View> ) } </TouchableOpacity> </View> <View style={{ height: "auto", width: "100%", flexDirection: "row", justifyContent: "space-between", marginTop: 20 }}> <TouchableOpacity style={{ borderBottomWidth: currentView == "ongoing" ? Platform.OS == "ios" ? 1.5 : 2 : 1, borderBottomColor: currentView == "ongoing" ? "#0A84FF" : getColor(darkMode, "primaryBorder"), height: 27, paddingLeft: 15, paddingRight: 15, width: "50%", alignItems: "center" }} hitSlop={{ top: 20 }} onPress={() => setCurrentView("ongoing")}> <Text style={{ color: currentView == "ongoing" ? "#0A84FF" : "gray", fontWeight: "bold", fontSize: 14 }}> {i18n(lang, "ongoing")} </Text> </TouchableOpacity> <TouchableOpacity style={{ borderBottomWidth: currentView == "finished" ? Platform.OS == "ios" ? 1.5 : 2 : 1, borderBottomColor: currentView == "finished" ? "#0A84FF" : getColor(darkMode, "primaryBorder"), height: 27, paddingLeft: 15, paddingRight: 15, width: "50%", alignItems: "center" }} hitSlop={{ top: 20 }} onPress={() => setCurrentView("finished")}> <Text style={{ color: currentView == "finished" ? "#0A84FF" : "gray", fontWeight: "bold", fontSize: 14 }}> {i18n(lang, "finished")} </Text> </TouchableOpacity> </View> </View> <View style={{ width: "100%", height: (contentHeight - topBarHeight - bottomBarHeight + 30) }}> { currentView == "ongoing" && ( <FlatList data={ongoingTransfersList} keyExtractor={(item, index) => index} key="ongoing" windowSize={10} initialNumToRender={32} removeClippedSubviews={true} numColumns={1} renderItem={({ item, index }) => { const transfer = item return ( <View key={index} style={{ width: "100%", height: 40, paddingTop: 10, paddingBottom: 10, paddingLeft: 15, paddingRight: 15, flexDirection: "row", justifyContent: "space-between", alignItems: "center", borderBottomColor: getColor(darkMode, "primaryBorder"), borderBottomWidth: 1 }}> <View style={{ flexDirection: "row", width: "50%" }}> <Ionicon name={transfer.transfer.paused ? "pause-circle-outline" : transfer.type == "upload" ? "arrow-up-outline" : "arrow-down-outline"} size={20} color={darkMode ? "white" : "black"} /> <Text style={{ color: darkMode ? "white" : "black", marginLeft: 10, paddingTop: 2 }} numberOfLines={1}> {transfer.transfer.file.name} </Text> </View> <View style={{ marginLeft: 20 }}> <Text style={{ color: darkMode ? "white" : "black" }}> { transfer.transfer.chunksDone == 0 ? ( <>{i18n(lang, "queued")}</> ) : ( <>{!isNaN(Math.round((transfer.transfer.chunksDone / transfer.transfer.file.chunks) * 100)) ? (Math.round((transfer.transfer.chunksDone / transfer.transfer.file.chunks) * 100) >= 100 ? 100 : Math.round((transfer.transfer.chunksDone / transfer.transfer.file.chunks) * 100)) : 0}%</> ) } </Text> </View> { transfer.transfer.paused ? ( <TouchableOpacity onPress={() => { if(transfer.type == "upload"){ const currentUploads = useStore.getState().uploads let didChange = false for(let prop in currentUploads){ if(transfer.transfer.id == currentUploads[prop].id){ currentUploads[prop].paused = false didChange = true } } if(didChange){ useStore.setState({ uploads: currentUploads }) } } else{ const currentDownloads = useStore.getState().downloads let didChange = false for(let prop in currentDownloads){ if(transfer.transfer.id == currentDownloads[prop].id){ currentDownloads[prop].paused = false didChange = true } } if(didChange){ useStore.setState({ downloads: currentDownloads }) } } }}> <Text style={{ color: "#0A84FF" }}> {i18n(lang, "resume")} </Text> </TouchableOpacity> ) : ( <TouchableOpacity onPress={() => { if(transfer.type == "upload"){ const currentUploads = useStore.getState().uploads let didChange = false for(let prop in currentUploads){ if(transfer.transfer.id == currentUploads[prop].id){ currentUploads[prop].paused = true didChange = true } } if(didChange){ useStore.setState({ uploads: currentUploads }) } } else{ const currentDownloads = useStore.getState().downloads let didChange = false for(let prop in currentDownloads){ if(transfer.transfer.id == currentDownloads[prop].id){ currentDownloads[prop].paused = true didChange = true } } if(didChange){ useStore.setState({ downloads: currentDownloads }) } } }}> <Text style={{ color: "#0A84FF" }}> {i18n(lang, "pause")} </Text> </TouchableOpacity> ) } <TouchableOpacity onPress={() => { if(transfer.type == "upload"){ const currentUploads = useStore.getState().uploads let didChange = false for(let prop in currentUploads){ if(transfer.transfer.id == currentUploads[prop].id){ currentUploads[prop].stopped = true didChange = true } } if(didChange){ useStore.setState({ uploads: currentUploads }) } } else{ const currentDownloads = useStore.getState().downloads let didChange = false for(let prop in currentDownloads){ if(transfer.transfer.id == currentDownloads[prop].id){ currentDownloads[prop].stopped = true didChange = true } } if(didChange){ useStore.setState({ downloads: currentDownloads }) } } }}> <Text style={{ color: "#0A84FF" }}> {i18n(lang, "stop")} </Text> </TouchableOpacity> </View> ) }} getItemLayout={(data, index) => ( {length: 40, offset: 40 * index, index} )} style={{ height: "100%", width: "100%" }} ListEmptyComponent={() => { return ( <View style={{ marginTop: "60%", justifyContent: "center", alignItems: "center" }}> <Ionicon name="repeat-outline" size={70} color="gray" /> <Text style={{ color: "gray", marginTop: 5 }}> {i18n(lang, "noTransfers")} </Text> </View> ) }} /> ) } { currentView == "finished" && ( <FlatList data={finishedTransfersList} keyExtractor={(item, index) => index} key="ongoing" windowSize={10} initialNumToRender={32} removeClippedSubviews={true} numColumns={1} renderItem={({ item, index }) => { const transfer = item return ( <View key={index} style={{ width: "100%", height: 40, paddingTop: 10, paddingBottom: 10, paddingLeft: 15, paddingRight: 15, flexDirection: "row", justifyContent: "space-between", alignItems: "center", borderBottomColor: getColor(darkMode, "primaryBorder"), borderBottomWidth: 1 }}> <View style={{ flexDirection: "row", width: "90%" }}> <Ionicon name={transfer.type == "upload" ? "arrow-up-outline" : "arrow-down-outline"} size={20} color={darkMode ? "white" : "black"} /> <Text style={{ color: darkMode ? "white" : "black", marginLeft: 10, paddingTop: 2 }} numberOfLines={1}> {transfer.transfer.file.name} </Text> </View> </View> ) }} getItemLayout={(data, index) => ( {length: 40, offset: 40 * index, index} )} style={{ height: "100%", width: "100%" }} ListEmptyComponent={() => { return ( <View style={{ marginTop: "60%", justifyContent: "center", alignItems: "center" }}> <Ionicon name="repeat-outline" size={70} color="gray" /> <Text style={{ color: "gray", marginTop: 5 }}> {i18n(lang, "noFinishedTransfers")} </Text> </View> ) }} /> ) } </View> </View> ) })