react-use#useMountedState JavaScript Examples
The following examples show how to use
react-use#useMountedState.
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: GDPRScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 5 votes |
GDPRScreen = memo(({ navigation, route }) => {
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [lang, setLang] = useMMKVString("lang", storage)
const [gdpr, setGdpr] = useState("")
const [isLoading, setIsLoading] = useState(true)
const isMounted = useMountedState()
useEffect(() => {
fetchGDPRInfo().then((info) => {
if(isMounted()){
setGdpr(JSON.stringify(info, null, 2))
setIsLoading(false)
}
}).catch((err) => {
console.log(err)
showToast({ message: err.toString() })
})
}, [])
return (
<>
<View style={{
flexDirection: "row",
justifyContent: "flex-start",
backgroundColor: darkMode ? "black" : "white"
}}>
<TouchableOpacity style={{
marginTop: Platform.OS == "ios" ? 17 : 4,
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: 24,
marginLeft: 10,
marginTop: Platform.OS == "ios" ? 15 : 0
}}>
{i18n(lang, "showGDPR")}
</Text>
</View>
<ScrollView style={{
height: "100%",
width: "100%",
backgroundColor: darkMode ? "black" : "white"
}}>
{
isLoading ? (
<ActivityIndicator size={"small"} color={darkMode ? "white" : "black"} style={{
marginTop: "70%"
}} />
) : (
<Text style={{
color: darkMode ? "white" : "black",
padding: 22
}}>
{striptags(gdpr)}
</Text>
)
}
</ScrollView>
</>
)
})
Example #2
Source File: EventsScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
EventsInfoScreen = memo(({ navigation, route }) => {
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [lang, setLang] = useMMKVString("lang", storage)
const [eventInfo, setEventInfo] = useState(undefined)
const [isLoading, setIsLoading] = useState(true)
const [eventText, setEventText] = useState("")
const isMounted = useMountedState()
const uuid = route?.params?.uuid
useEffect(() => {
setIsLoading(true)
fetchEventInfo({ uuid }).then((info) => {
if(isMounted()){
setEventInfo(info)
getEventText({ item: info, masterKeys: getMasterKeys(), lang }).then((text) => {
if(isMounted()){
setEventText(text)
setIsLoading(false)
}
})
}
}).catch((err) => {
console.log(err)
showToast({ message: err.toString() })
})
}, [])
return (
<>
<View style={{
flexDirection: "row",
justifyContent: "flex-start",
backgroundColor: darkMode ? "black" : "white"
}}>
<TouchableOpacity style={{
marginTop: Platform.OS == "ios" ? 17 : 4,
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: 24,
marginLeft: 10,
marginTop: Platform.OS == "ios" ? 15 : 0
}}>
{i18n(lang, "eventInfo")}
</Text>
</View>
<ScrollView style={{
height: "100%",
width: "100%",
backgroundColor: darkMode ? "black" : "white"
}}>
{
isLoading ? (
<ActivityIndicator size="small" color={darkMode ? "white" : "black"} style={{
marginTop: 100
}} />
) : (
<>
<SettingsGroup>
<SettingsButton title={eventText} />
</SettingsGroup>
<SettingsGroup marginTop={10}>
<SettingsButton title={new Date(eventInfo.timestamp * 1000).toLocaleDateString() + " " + new Date(eventInfo.timestamp * 1000).toLocaleTimeString()} />
</SettingsGroup>
<SettingsGroup marginTop={10}>
<SettingsButton title={eventInfo.info.userAgent} />
</SettingsGroup>
<SettingsGroup marginTop={10}>
<SettingsButton title={eventInfo.info.ip} />
</SettingsGroup>
</>
)
}
<View style={{ height: 25 }}></View>
</ScrollView>
</>
)
})
Example #3
Source File: EventsScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
EventsScreen = memo(({ navigation, route }) => {
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [lang, setLang] = useMMKVString("lang", storage)
const [events, setEvents] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [filter, setFilter] = useState("all")
const [limit, setLimit] = useState(0)
const [refreshing, setRefreshing] = useState(false)
const dimensions = useStore(useCallback(state => state.dimensions))
const [masterKeys, setMasterKeys] = useState(getMasterKeys())
const isMounted = useMountedState()
const [topHeight, setTopHeight] = useState(0)
const bottomBarHeight = useStore(useCallback(state => state.bottomBarHeight))
const contentHeight = useStore(useCallback(state => state.contentHeight))
const onEndReachedCalledDuringMomentum = useRef(false)
const lastEventId = useRef(0)
const getEvents = useCallback((lastId) => {
setIsLoading(true)
fetchEvents({ lastId, filter }).then((data) => {
if(isMounted()){
setIsLoading(false)
setRefreshing(false)
const newEvents = data.events
const limit = data.limit
setEvents(prev => [...prev, ...newEvents])
setLimit(limit)
newEvents.map(event => console.log(event.id))
lastEventId.current = newEvents[newEvents.length - 1].id
}
}).catch((err) => {
console.log(err)
showToast({ message: err.toString() })
})
})
useEffect(() => {
setEvents([])
getEvents(lastEventId.current)
}, [])
return (
<>
<View style={{
flexDirection: "row",
justifyContent: "flex-start",
backgroundColor: darkMode ? "black" : "white"
}} onLayout={(e) => setTopHeight(e.nativeEvent.layout.height)}>
<TouchableOpacity style={{
marginTop: Platform.OS == "ios" ? 17 : 4,
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: 24,
marginLeft: 10,
marginTop: Platform.OS == "ios" ? 15 : 0
}}>
{i18n(lang, "events")}
</Text>
</View>
<View style={{
height: Math.floor(contentHeight - topHeight - bottomBarHeight + 31),
width: "100%",
backgroundColor: darkMode ? "black" : "white",
paddingTop: 15
}}>
<FlatList
data={events}
keyExtractor={(item, index) => index}
key="events"
windowSize={10}
initialNumToRender={32}
removeClippedSubviews={true}
numColumns={1}
renderItem={({ item, index }) => <EventRow item={item} index={index} darkMode={darkMode} lang={lang} navigation={navigation} masterKeys={masterKeys} />}
onMomentumScrollBegin={() => onEndReachedCalledDuringMomentum.current = false}
onEndReachedThreshold={0.1}
onEndReached={() => {
if(limit <= events.length && limit > 0 && events.length > 0 && !onEndReachedCalledDuringMomentum.current){
onEndReachedCalledDuringMomentum.current = true
console.log("called")
getEvents(lastEventId.current)
}
}}
getItemLayout={(data, index) => (
{length: 45, offset: 45 * index, index}
)}
ListEmptyComponent={() => {
return (
<View style={{
width: "100%",
height: Math.floor(dimensions.screen.height - 255),
justifyContent: "center",
alignItems: "center",
alignContent: "center"
}}>
{
isLoading ? (
<View>
<ActivityIndicator color={darkMode ? "white" : "black"} size="small" />
<Text style={{
color: darkMode ? "white" : "black",
marginTop: 15
}}>
{i18n(lang, "loading")}
</Text>
</View>
) : (
<ListEmpty route={route} searchTerm={""} />
)
}
</View>
)
}}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={() => {
if(!isLoading){
return false
}
setRefreshing(true)
getEvents(0)
}}
tintColor={darkMode ? "white" : "black"}
size="default"
/>
}
style={{
height: "100%",
width: "100%"
}}
ListFooterComponent={
isLoading ? (
<></>
) : (
limit <= events.length && limit > 0 && events.length > 0 ? (
<View style={{
height: 50,
marginTop: 15
}}>
<ActivityIndicator color={darkMode ? "white" : "black"} size="small" />
</View>
) : (
<></>
)
)
}
/>
</View>
</>
)
})
Example #4
Source File: ImageViewerScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
ImageViewerScreen = memo(({ navigation, route }) => {
const screenDimensions = Dimensions.get("screen")
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [lang, setLang] = useMMKVString("lang", storage)
const [imagePreviewModalItems, setImagePreviewModalItems] = useState(route.params.items || [])
const [imagePreviewModalIndex, setImagePreviewModalIndex] = useState(route.params.index || 0)
const setCurrentActionSheetItem = useStore(useCallback(state => state.setCurrentActionSheetItem))
const [images, setImages] = useState({})
const [currentName, setCurrentName] = useState("")
const [isZooming, setIsZooming] = useState(false)
const [isSwiping, setIsSwiping] = useState(false)
const zoomLevel = useRef(minZoom)
const thumbnailListRef = useRef()
const listRef = useRef()
const [currentIndex, setCurrentIndex] = useState(imagePreviewModalIndex)
const [showControls, setShowControls] = useState(true)
const insets = useSafeAreaInsets()
const viewRefs = useRef({}).current
const isMounted = useMountedState()
const tapCount = useRef(0)
const tapTimer = useRef(undefined)
const [portrait, setPortrait] = useState(screenDimensions.height >= screenDimensions.width)
const didNavBack = useRef(false)
const currentImagePreviewDownloads = useRef({}).current
const setListScrollAgain = useRef(false)
const imageActionsContainerHeight = new Animated.Value(120)
const imageActionsVisible = useRef(false)
const loadImage = useCallback((image, index) => {
if(!isMounted()){
return false
}
zoomLevel.current = minZoom
setCurrentName(image.file.name)
setCurrentActionSheetItem(image.file)
setCurrentIndex(index)
thumbnailListRef?.current?.scrollToIndex({
animated: true,
index,
viewPosition: 0.5
})
if(setListScrollAgain.current){
setListScrollAgain.current = false
listRef?.current?.scrollToIndex({
animated: false,
index
})
}
const currentImages = {...images}
if(typeof currentImages[image.uuid] == "string"){
return false
}
if(typeof currentImagePreviewDownloads[image.uuid] !== "undefined"){
return false
}
currentImagePreviewDownloads[image.uuid] = true
downloadWholeFileFSStream({
file: image.file
}).then((path) => {
delete currentImagePreviewDownloads[image.uuid]
if(!isMounted()){
return false
}
generateItemThumbnail({
item: image.file,
skipInViewCheck: true,
callback: (err, thumbPath) => {
if(!isMounted()){
return false
}
if(!err && typeof thumbPath == "string"){
updateItemThumbnail(image.file, thumbPath)
}
return setImages(prev => ({
...prev,
[image.uuid]: path
}))
}
})
}).catch((err) => {
delete currentImagePreviewDownloads[image.uuid]
console.log(err)
return showToast({ message: err.toString() })
})
})
const onViewableItemsChangedRef = useRef(useCallback(({ viewableItems }) => {
const indexItem = viewableItems[viewableItems.length - 1]
if(typeof indexItem !== "object"){
return false
}
if(typeof indexItem.item !== "object"){
return false
}
loadImage(indexItem.item, indexItem.index)
}))
const updateItemThumbnail = useCallback((item, path) => {
if(typeof path !== "string"){
return false
}
if(path.length < 4){
return false
}
if(isMounted()){
setImagePreviewModalItems(prev => [...prev.map(mapItem => mapItem.file.uuid == item.uuid && typeof mapItem.thumbnail == "undefined" ? {...mapItem, thumbnail: item.uuid + ".jpg" } : mapItem)])
}
})
const viewabilityConfigRef = useRef({
minimumViewTime: 0,
viewAreaCoveragePercentThreshold: 95,
viewAreaCoveragePercentThreshold: 95,
waitForInteraction: false
})
useEffect(() => {
if(!isMounted()){
return false
}
setShowControls(isZooming)
}, [isZooming])
useEffect(() => {
if(imagePreviewModalItems.length == 0){
return navigation.goBack()
}
if(Platform.OS == "ios"){
setStatusBarStyle(true)
}
if(Platform.OS == "android"){
hideNavigationBar()
StatusBar.setHidden(true)
}
if(typeof imagePreviewModalItems[imagePreviewModalIndex] !== "undefined"){
setTimeout(() => {
loadImage(imagePreviewModalItems[imagePreviewModalIndex], imagePreviewModalIndex)
}, 50)
}
const dimensionsListener = Dimensions.addEventListener("change", ({ screen }) => {
if(!isMounted()){
return false
}
setPortrait(screen.height >= screen.width)
})
return () => {
dimensionsListener.remove()
if(Platform.OS == "ios"){
setStatusBarStyle(darkMode)
setTimeout(() => setStatusBarStyle(darkMode), 500)
setTimeout(() => setStatusBarStyle(darkMode), 1000)
}
if(Platform.OS == "android"){
showNavigationBar()
StatusBar.setHidden(false)
}
}
}, [])
const renderImage = useCallback((item, index) => {
const image = item
if(typeof image.thumbnail !== "string"){
return (
<View
key={image.uuid}
style={{
width: screenDimensions.width,
height: screenDimensions.height
}}
>
<ActivityIndicator size={"small"} color={"white"} style={{
margin: "auto",
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0
}} />
</View>
)
}
return (
<ReactNativeZoomableView
key={image.uuid}
ref={(ref) => viewRefs[image.uuid] = ref}
maxZoom={3}
minZoom={minZoom}
zoomStep={2}
initialZoom={minZoom}
bindToBorders={true}
contentWidth={screenDimensions.width}
contentHeight={screenDimensions.height}
style={{
width: screenDimensions.width,
height: screenDimensions.height
}}
onZoomBefore={(e, state, view) => {
setIsZooming(view.zoomLevel > 1)
zoomLevel.current = view.zoomLevel
}}
onZoomAfter={(e, state, view) => {
setIsZooming(view.zoomLevel > 1)
zoomLevel.current = view.zoomLevel
if(view.zoomLevel <= 1.05){
listRef?.current?.scrollToIndex({
animated: false,
index
})
thumbnailListRef?.current?.scrollToIndex({
animated: false,
index,
viewPosition: 0.5
})
}
}}
onShiftingBefore={(e, state, view) => {
setIsZooming(view.zoomLevel > 1)
zoomLevel.current = view.zoomLevel
}}
onShiftingAfter={(e, state, view) => {
setIsZooming(view.zoomLevel > 1)
if((view.distanceTop >= 50 || view.distanceBottom >= 50) && !didNavBack.current && zoomLevel.current <= 1 && !isSwiping && !isZooming){
didNavBack.current = true
navigation.goBack()
return true
}
zoomLevel.current = view.zoomLevel
}}
captureEvent={true}
>
<Pressable
onPress={() => {
if(isSwiping){
return false
}
tapCount.current += 1
if(tapCount.current >= 2){
if(zoomLevel.current >= 1.01){
viewRefs[image.uuid]?.zoomTo(1)
zoomLevel.current = 1
setIsZooming(false)
}
else{
viewRefs[image.uuid]?.zoomTo(2)
zoomLevel.current = 2
setIsZooming(true)
}
tapCount.current = 0
return clearTimeout(tapTimer.current)
}
clearTimeout(tapTimer.current)
tapTimer.current = setTimeout(() => {
if(tapCount.current >= 2){
if(zoomLevel.current >= 2){
viewRefs[image.uuid]?.zoomTo(1)
zoomLevel.current = 1
setIsZooming(false)
}
else{
viewRefs[image.uuid]?.zoomTo(2)
zoomLevel.current = 2
setIsZooming(true)
}
}
else{
setShowControls(prev => !prev)
}
tapCount.current = 0
}, 300)
}}
>
<>
<ImageBackground
source={{
uri: decodeURIComponent("file://" + THUMBNAIL_BASE_PATH + image.thumbnail)
}}
resizeMode="contain"
style={{
width: screenDimensions.width,
height: screenDimensions.height
}}
>
{
typeof images[image.uuid] == "string" && (
<Image
source={{
uri: decodeURIComponent(images[image.uuid].startsWith("file://") ? images[image.uuid] : "file://" + images[image.uuid])
}}
resizeMode="contain"
style={{
width: screenDimensions.width,
height: screenDimensions.height
}}
/>
)
}
</ImageBackground>
{
typeof images[image.uuid] !== "string" && (
<ActivityIndicator size={"small"} color={"white"} style={{
margin: "auto",
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0
}} />
)
}
</>
</Pressable>
</ReactNativeZoomableView>
)
})
const renderThumb = useCallback((item, index) => {
const image = item
return (
<View
style={{
width: 30,
height: 50
}}
>
<TouchableOpacity
key={image.uuid}
style={{
width: 30,
height: 50,
backgroundColor: "transparent",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "center"
}}
onPress={async () => {
try{
await viewRefs[imagePreviewModalItems[currentIndex].uuid]?.zoomTo(1)
}
catch(e){
console.log(e)
}
setListScrollAgain.current = true
thumbnailListRef?.current?.scrollToIndex({
animated: false,
index,
viewPosition: 0.5
})
listRef?.current?.scrollToOffset({
animated: false,
offset: screenDimensions.width * index + 1
})
loadImage(imagePreviewModalItems[index], index)
}}
>
{
typeof image.thumbnail !== "string" ? (
<Image
source={getImageForItem(image.file)}
resizeMode="cover"
style={{
width: 25,
height: 35,
marginTop: 2.5,
marginLeft: 2.5
}}
/>
) : (
<Image
source={{
uri: decodeURIComponent("file://" + THUMBNAIL_BASE_PATH + image.thumbnail)
}}
resizeMode="cover"
style={{
width: 30,
height: 40
}}
/>
)
}
<View style={{
backgroundColor: currentIndex == index ? "gray" : "transparent",
width: 15,
height: 5,
borderRadius: 20
}}></View>
</TouchableOpacity>
</View>
)
})
return (
<View style={{
backgroundColor: "black",
height: screenDimensions.height,
width: screenDimensions.width,
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0
}}>
<View style={{
opacity: showControls ? 0 : 1,
flexDirection: "row",
height: "auto",
width: screenDimensions.width,
justifyContent: "space-between",
alignItems: "center",
position: "absolute",
top: 0,
zIndex: showControls ? 0 : 1000,
backgroundColor: "rgba(0, 0, 0, 0.6)",
paddingLeft: 10,
paddingRight: 15,
paddingTop: Platform.OS == "android" ? (insets.top + 5) : ((!portrait ? 10 : insets.top) + 5),
paddingBottom: 10,
marginTop: 0
}}>
<View style={{
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center"
}}>
<TouchableOpacity style={{
marginTop: Platform.OS == "android" ? 1 : 0,
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center"
}} hitSlop={{
top: 10,
left: 10,
bottom: 10,
right: 10
}} onPress={() => {
navigationAnimation({ enable: true }).then(() => {
navigation.goBack()
})
}}>
<Ionicon name="chevron-back" size={24} color={"white"}></Ionicon>
</TouchableOpacity>
<Text numberOfLines={1} style={{
color: "white",
width: "93%",
fontSize: 17,
paddingLeft: 10,
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center",
fontWeight: "bold"
}}>
{currentName}
</Text>
</View>
<View style={{
display: "none"
}}>
<TouchableOpacity style={{
marginTop: Platform.OS == "android" ? 1 : 0
}} hitSlop={{
top: 10,
left: 10,
bottom: 10,
right: 10
}} onPress={() => {
imageActionsVisible.current = !imageActionsVisible.current
Animated.timing( // Animate over time
imageActionsContainerHeight, // The animated value to drive, this would be a new Animated.Value(0) object.
{
toValue: imageActionsVisible.current ? 300 : 120, // Animate the value
duration: 100, // Make it take a while
useNativeDriver: false
}
).start()
}}>
<Ionicon name="ellipsis-horizontal-sharp" size={24} color={"white"}></Ionicon>
</TouchableOpacity>
</View>
</View>
<FlatList
style={{
position: "absolute",
width: screenDimensions.width,
height: screenDimensions.height,
zIndex: 10
}}
ref={listRef}
data={imagePreviewModalItems}
initialScrollIndex={currentIndex}
renderItem={({ item, index }) => {
return renderImage(item, index)
}}
key={portrait ? "portrait" : "landscape"}
extraData={portrait ? "portrait" : "landscape"}
keyExtractor={(item, index) => item.uuid}
windowSize={3}
initialNumToRender={1}
horizontal={true}
bounces={true}
getItemLayout={(data, index) => ({ length: screenDimensions.width, offset: screenDimensions.width * index, index })}
scrollEnabled={!isZooming}
pagingEnabled={true}
onViewableItemsChanged={onViewableItemsChangedRef?.current}
viewabilityConfig={viewabilityConfigRef?.current}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
onScrollBeginDrag={() => setIsSwiping(true)}
onScrollEndDrag={() => setIsSwiping(false)}
removeClippedSubviews={false}
/>
<Animated.View
style={{
position: "absolute",
bottom: -30,
width: screenDimensions.width,
height: imageActionsContainerHeight,
zIndex: showControls ? 0 : 10000,
backgroundColor: "rgba(0, 0, 0, 1)",
paddingTop: 1,
paddingBottom: insets.bottom + insets.top,
opacity: showControls ? 0 : 1,
paddingBottom: insets.bottom
}}
>
<FlatList
style={{
position: "absolute",
width: screenDimensions.width,
height: 120,
paddingTop: 3
}}
ref={thumbnailListRef}
data={imagePreviewModalItems}
renderItem={({ item, index }) => {
return renderThumb(item, index)
}}
getItemLayout={(data, index) => ({ length: 30, offset: 30 * index, index })}
keyExtractor={(item, index) => item.uuid}
horizontal={true}
scrollEnabled={true}
bounces={false}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
removeClippedSubviews={false}
/>
<View style={{
marginTop: 60,
borderTopColor: "gray",
borderTopWidth: 1,
opacity: 0
}}>
<View style={{
width: "100%",
height: 45,
flexDirection: "row",
justifyContent: "space-between",
borderBottomColor: "gray",
borderBottomWidth: 1,
paddingLeft: 15,
paddingRight: 15
}}>
<Text style={{
color: "white",
paddingTop: 12
}}>
{i18n(lang, "publicLinkEnabled")}
</Text>
<View style={{
paddingTop: Platform.OS == "ios" ? 6 : 8
}}>
</View>
</View>
</View>
</Animated.View>
</View>
)
})
Example #5
Source File: InviteScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
InviteScreen = memo(({ navigation, route }) => {
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [lang, setLang] = useMMKVString("lang", storage)
const [isLoading, setIsLoading] = useState(true)
const [accountData, setAccountData] = useState({})
const isMounted = useMountedState()
useEffect(() => {
getAccount().then((data) => {
if(isMounted()){
setAccountData(data)
setIsLoading(false)
}
}).catch((err) => {
console.log(err)
showToast({ message: err.toString() })
})
}, [])
return (
<>
<View style={{
flexDirection: "row",
justifyContent: "flex-start",
backgroundColor: darkMode ? "black" : "white"
}}>
<TouchableOpacity style={{
marginTop: Platform.OS == "ios" ? 17 : 4,
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: 24,
marginLeft: 10,
marginTop: Platform.OS == "ios" ? 15 : 0
}}>
{i18n(lang, "invite")}
</Text>
</View>
<ScrollView style={{
height: "100%",
width: "100%",
backgroundColor: darkMode ? "black" : "white"
}}>
{
isLoading ? (
<ActivityIndicator size={"small"} color={darkMode ? "white" : "black"} style={{
marginTop: "70%"
}} />
) : (
<>
<SettingsGroup>
<View style={{
width: "100%",
height: "auto"
}}>
<View style={{
width: "100%",
height: "auto",
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10
}}>
<View>
<Text style={{
color: darkMode ? "white" : "black"
}}>
{i18n(lang, "inviteInfo")}
</Text>
</View>
</View>
</View>
</SettingsGroup>
<SettingsGroup>
<View style={{
width: "100%",
height: "auto"
}}>
<View style={{
width: "100%",
height: "auto",
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10
}}>
<View>
<Text style={{
color: darkMode ? "white" : "black"
}}>
{i18n(lang, "inviteCount")}
</Text>
</View>
<Text style={{
color: darkMode ? "white" : "black"
}}>
{accountData.referCount > accountData.refLimit ? accountData.refLimit : accountData.referCount}/{accountData.refLimit}
</Text>
</View>
</View>
</SettingsGroup>
<SettingsGroup>
<Pressable style={{
width: "100%",
height: "auto"
}} onPress={() => {
Clipboard.setString("https://filen.io/r/" + accountData.refId)
showToast({ message: i18n(lang, "copiedToClipboard") })
}}>
<View style={{
width: "100%",
height: "auto",
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10
}}>
<Text style={{
color: darkMode ? "white" : "black",
width: "70%"
}} numberOfLines={1}>
https://filen.io/r/{accountData.refId}
</Text>
<TouchableOpacity onPress={() => {
Share.share({
message: i18n(lang, "shareRefLinkMessage"),
url: "https://filen.io/r/" + accountData.refId
})
}}>
<Text style={{
color: "#0A84FF"
}}>
{i18n(lang, "share")}
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => {
Clipboard.setString("https://filen.io/r/" + accountData.refId)
showToast({ message: i18n(lang, "copiedToClipboard") })
}}>
<Text style={{
color: "#0A84FF"
}}>
{i18n(lang, "copy")}
</Text>
</TouchableOpacity>
</View>
</Pressable>
</SettingsGroup>
<View style={{
width: "100%",
height: "auto",
paddingLeft: 8,
paddingRight: 8,
marginTop: 5
}}>
<View style={{
width: "100%",
height: "auto",
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10
}}>
<View>
<Text style={{
color: "gray",
fontSize: 10
}}>
{i18n(lang, "inviteInfo2")}
</Text>
</View>
</View>
</View>
</>
)
}
</ScrollView>
</>
)
})
Example #6
Source File: MainScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
MainScreen = memo(({ navigation, route }) => {
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [userId, setUserId] = useMMKVNumber("userId", storage)
const [routeURL, setRouteURL] = useState(useCallback(getRouteURL(route)))
const cachedItemsRef = useRef(storage.getString("loadItemsCache:" + routeURL)).current
const cachedItemsParsed = useRef(typeof cachedItemsRef == "string" ? JSON.parse(cachedItemsRef) : []).current
const [items, setItems] = useState(Array.isArray(cachedItemsParsed) ? cachedItemsParsed.filter(item => item !== null && typeof item.uuid == "string") : [])
const [searchTerm, setSearchTerm] = useState("")
const [loadDone, setLoadDone] = useState(typeof cachedItemsRef !== "undefined" ? true : false)
const setNavigation = useStore(useCallback(state => state.setNavigation))
const setRoute = useStore(useCallback(state => state.setRoute))
const [masterKeys, setMasterKeys] = useState(useCallback(getMasterKeys()))
const isMounted = useMountedState()
const setCurrentActionSheetItem = useStore(useCallback(state => state.setCurrentActionSheetItem))
const setCurrentItems = useStore(useCallback(state => state.setCurrentItems))
const itemsRef = useRef([])
const setItemsSelectedCount = useStore(useCallback(state => state.setItemsSelectedCount))
const setInsets = useStore(useCallback(state => state.setInsets))
const insets = useSafeAreaInsets()
const [progress, setProgress] = useState({ itemsDone: 0, totalItems: 1 })
const selectedCountRef = useRef(0)
const setIsDeviceReady = useStore(useCallback(state => state.setIsDeviceReady))
const [itemsBeforeSearch, setItemsBeforeSearch] = useState([])
const [photosGridSize, setPhotosGridSize] = useMMKVNumber("photosGridSize", storage)
const bottomBarHeight = useStore(useCallback(state => state.bottomBarHeight))
const topBarHeight = useStore(useCallback(state => state.topBarHeight))
const contentHeight = useStore(useCallback(state => state.contentHeight))
const [photosRange, setPhotosRange] = useMMKVString("photosRange:" + userId, storage)
const netInfo = useStore(useCallback(state => state.netInfo))
const itemsSortBy = useStore(useCallback(state => state.itemsSortBy))
const [initialized, setInitialized] = useState(false)
const isFocused = useIsFocused()
const updateItemThumbnail = useCallback((item, path) => {
if(typeof path !== "string"){
return false
}
if(path.length < 4){
return false
}
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == item.uuid && typeof mapItem.thumbnail == "undefined" ? {...mapItem, thumbnail: item.uuid + ".jpg" } : mapItem))
}
})
const selectItem = useCallback((item) => {
if(getRouteURL(route).indexOf("photos") !== -1){
if(calcPhotosGridSize(photosGridSize) >= 6){
return false
}
}
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == item.uuid ? {...mapItem, selected: true} : mapItem))
}
})
const unselectItem = useCallback((item) => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == item.uuid ? {...mapItem, selected: false} : mapItem))
}
})
const unselectAllItems = useCallback(() => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.selected ? {...mapItem, selected: false} : mapItem))
}
})
const selectAllItems = useCallback(() => {
if(getRouteURL(route).indexOf("photos") !== -1){
if(calcPhotosGridSize(photosGridSize) >= 6){
return false
}
}
if(isMounted()){
setItems(items => items.map(mapItem => !mapItem.selected ? {...mapItem, selected: true} : mapItem))
}
})
const removeItem = useCallback((uuid) => {
if(isMounted()){
setItems(items => items.filter(mapItem => mapItem.uuid !== uuid && mapItem))
}
})
const markOffline = useCallback((uuid, value) => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == uuid ? {...mapItem, offline: value} : mapItem))
}
})
const markFavorite = useCallback((uuid, value) => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == uuid ? {...mapItem, favorited: value} : mapItem))
}
})
const changeFolderColor = useCallback((uuid, color) => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == uuid && mapItem.type == "folder" ? {...mapItem, color} : mapItem))
}
})
const changeItemName = useCallback((uuid, name) => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == uuid ? {...mapItem, name} : mapItem))
}
})
const addItem = useCallback((item, parent) => {
const currentParent = getParent(route)
if(isMounted() && (currentParent == parent || (item.offline && parent == "offline"))){
setItems(items => sortItems({ items: [...items, item], passedRoute: route }))
}
})
const changeWholeItem = useCallback((item, uuid) => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == uuid ? item : mapItem))
}
})
const reloadList = useCallback((parent) => {
const currentParent = getParent(route)
if(isMounted() && currentParent == parent){
fetchItemList({ bypassCache: true, callStack: 1 })
}
})
const updateFolderSize = useCallback((uuid, size) => {
if(isMounted()){
setItems(items => items.map(mapItem => mapItem.uuid == uuid && mapItem.type == "folder" ? {...mapItem, size} : mapItem))
}
})
useEffect(() => {
if(isMounted() && initialized){
if(searchTerm.length == 0){
if(itemsBeforeSearch.length > 0){
setItems(itemsBeforeSearch)
setItemsBeforeSearch([])
}
}
else{
if(itemsBeforeSearch.length == 0){
setItemsBeforeSearch(items)
var filtered = items.filter(item => item.name.toLowerCase().trim().indexOf(searchTerm.toLowerCase().trim()) !== -1 && item)
}
else{
var filtered = itemsBeforeSearch.filter(item => item.name.toLowerCase().trim().indexOf(searchTerm.toLowerCase().trim()) !== -1 && item)
}
setItems(filtered)
}
}
}, [searchTerm])
useEffect(() => {
if(isMounted() && initialized){
const sorted = sortItems({ items, passedRoute: route })
setItems(sorted)
}
}, [itemsSortBy])
useEffect(() => {
if(isFocused){
if(Array.isArray(items) && items.length > 0){
setCurrentItems(items)
itemsRef.current = items
global.items = items
const selected = items.filter(item => item.selected).length
selectedCountRef.current = selected
setItemsSelectedCount(selectedCountRef.current)
}
else{
setCurrentItems([])
itemsRef.current = []
global.items = []
setItemsSelectedCount(0)
}
global.setItems = setItems
}
}, [items, isFocused])
const fetchItemList = useCallback(({ bypassCache = false, callStack = 0, loadFolderSizes = false }) => {
return new Promise((resolve, reject) => {
loadItems({
parent: getParent(route),
setItems,
masterKeys,
setLoadDone,
navigation,
isMounted,
bypassCache,
route,
setProgress,
callStack,
loadFolderSizes
}).then(resolve).catch(reject)
})
})
useEffect(() => {
setNavigation(navigation)
setRoute(route)
setInsets(insets)
fetchItemList({ bypassCache: false, callStack: 0, loadFolderSizes: false }).catch((err) => console.log(err))
global.fetchItemList = fetchItemList
const deviceListener = DeviceEventEmitter.addListener("event", (data) => {
const navigationRoutes = navigation.getState().routes
const isListenerActive = typeof navigationRoutes == "object" ? (navigationRoutes[navigationRoutes.length - 1].key == route.key) : false
if(data.type == "thumbnail-generated"){
updateItemThumbnail(data.data, data.data.path)
}
else if(data.type == "item-onpress" && isListenerActive){
if(data.data.selected){
unselectItem(data.data)
}
else{
if(selectedCountRef.current > 0){
selectItem(data.data)
}
else{
global.currentReceiverId = data.data.receiverId
try{
const currentRouteURL = getRouteURL(route)
if(typeof currentRouteURL == "string"){
if(data.data.type == "folder" && currentRouteURL.indexOf("trash") == -1){
navigationAnimation({ enable: true }).then(() => {
navigation.dispatch(StackActions.push("MainScreen", {
parent: currentRouteURL + "/" + data.data.uuid
}))
})
}
else{
previewItem({ item: data.data, navigation })
}
}
else{
console.log("route url !== string: ", currentRouteURL)
}
}
catch(e){
console.log(e)
}
}
}
}
else if(data.type == "item-onlongpress" && isListenerActive){
selectItem(data.data)
}
else if(data.type == "open-item-actionsheet" && isListenerActive){
setCurrentActionSheetItem(data.data)
SheetManager.show("ItemActionSheet")
}
else if(data.type == "unselect-all-items" && isListenerActive){
unselectAllItems()
}
else if(data.type == "select-all-items" && isListenerActive){
selectAllItems()
}
else if(data.type == "select-item" && isListenerActive){
selectItem(data.data)
}
else if(data.type == "unselect-item" && isListenerActive){
unselectItem(data.data)
}
else if(data.type == "remove-item"){
removeItem(data.data.uuid)
}
else if(data.type == "add-item"){
addItem(data.data.item, data.data.parent)
}
else if(data.type == "mark-item-offline"){
if(!data.data.value && getRouteURL(route).indexOf("offline") !== -1){
removeItem(data.data.uuid)
}
else{
markOffline(data.data.uuid, data.data.value)
}
}
else if(data.type == "mark-item-favorite"){
if(!data.data.value && getRouteURL(route).indexOf("favorites") !== -1){
removeItem(data.data.uuid)
}
else{
markFavorite(data.data.uuid, data.data.value)
}
}
else if(data.type == "change-folder-color"){
changeFolderColor(data.data.uuid, data.data.color)
}
else if(data.type == "change-item-name"){
changeItemName(data.data.uuid, data.data.name)
}
else if(data.type == "change-whole-item"){
changeWholeItem(data.data.item, data.data.uuid)
}
else if(data.type == "reload-list"){
reloadList(data.data.parent)
}
else if(data.type == "remove-public-link"){
if(getRouteURL(route).indexOf("links") !== -1){
removeItem(data.data.uuid)
}
}
else if(data.type == "folder-size"){
updateFolderSize(data.data.uuid, data.data.size)
}
})
setIsDeviceReady(true)
setInitialized(true)
return () => {
deviceListener.remove()
setPhotosRange("all")
}
}, [])
return (
<View style={{
height: "100%",
width: "100%",
backgroundColor: darkMode ? "black" : "white"
}}>
<TopBar navigation={navigation} route={route} setLoadDone={setLoadDone} searchTerm={searchTerm} setSearchTerm={setSearchTerm} />
<View style={{
height: routeURL.indexOf("photos") !== -1 ? (contentHeight - 40 - bottomBarHeight + (Platform.OS == "android" ? 35 : 26)) : (contentHeight - topBarHeight - bottomBarHeight + 30)
}}>
<ItemList navigation={navigation} route={route} items={items} setItems={setItems} showLoader={!loadDone} loadDone={loadDone} searchTerm={searchTerm} isMounted={isMounted} fetchItemList={fetchItemList} progress={progress} setProgress={setProgress} />
</View>
</View>
)
})
Example #7
Source File: SettingsAccountScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
SettingsAccountScreen = memo(({ navigation, route }) => {
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [lang, setLang] = useMMKVString("lang", storage)
const setRedeemCodeDialogVisible = useStore(useCallback(state => state.setRedeemCodeDialogVisible))
const setDeleteAccountTwoFactorDialogVisible = useStore(useCallback(state => state.setDeleteAccountTwoFactorDialogVisible))
const [accountSettings, setAccountSettings] = useState({})
const [isLoading, setIsLoading] = useState(true)
const isMounted = useMountedState()
useEffect(() => {
getSettings().then((settings) => {
if(isMounted()){
setAccountSettings(settings)
setIsLoading(false)
}
}).catch((err) => {
console.log(err)
showToast({ message: err.toString() })
})
}, [])
return (
<>
<View style={{
flexDirection: "row",
justifyContent: "flex-start",
backgroundColor: darkMode ? "black" : "white"
}}>
<TouchableOpacity style={{
marginTop: Platform.OS == "ios" ? 17 : 4,
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: 24,
marginLeft: 10,
marginTop: Platform.OS == "ios" ? 15 : 0
}}>
{i18n(lang, "accountSettings")}
</Text>
</View>
<ScrollView style={{
height: "100%",
width: "100%",
backgroundColor: darkMode ? "black" : "white"
}}>
{
isLoading ? (
<ActivityIndicator size={"small"} color={darkMode ? "white" : "black"} style={{
marginTop: "70%"
}} />
) : (
<>
<SettingsGroup marginTop={15}>
<SettingsButtonLinkHighlight onPress={() => {
navigationAnimation({ enable: true }).then(() => {
navigation.dispatch(StackActions.push("ChangeEmailPasswordScreen"))
})
}} title={i18n(lang, "changeEmailPassword")} />
<SettingsButtonLinkHighlight onPress={() => {
navigationAnimation({ enable: true }).then(() => {
navigation.dispatch(StackActions.push("TwoFactorScreen", {
accountSettings
}))
})
}} title={i18n(lang, accountSettings.twoFactorEnabled ? "disable2FA" : "enable2FA")} />
<SettingsButtonLinkHighlight onPress={() => {
navigationAnimation({ enable: true }).then(() => {
navigation.dispatch(StackActions.push("GDPRScreen"))
})
}} title={i18n(lang, "showGDPR")} />
</SettingsGroup>
<SettingsGroup>
<SettingsButtonLinkHighlight rightText={formatBytes(accountSettings.storageUsed)} onPress={() => {
Alert.alert(i18n(lang, "deleteAllFiles"), i18n(lang, "deleteAllFilesInfo"), [
{
text: i18n(lang, "cancel"),
onPress: () => {
return false
},
style: "cancel"
},
{
text: i18n(lang, "ok"),
onPress: () => {
Alert.alert(i18n(lang, "deleteAllFiles"), i18n(lang, "areYouReallySure"), [
{
text: i18n(lang, "cancel"),
onPress: () => {
return false
},
style: "cancel"
},
{
text: i18n(lang, "ok"),
onPress: () => {
useStore.setState({ fullscreenLoadingModalVisible: true })
deleteAllFilesAndFolders().then(() => {
useStore.setState({ fullscreenLoadingModalVisible: false })
showToast({ message: i18n(lang, "deleteAllFilesSuccess") })
setAccountSettings(prev => ({ ...prev, storageUsed: 0 }))
}).catch((err) => {
console.log(err)
useStore.setState({ fullscreenLoadingModalVisible: false })
showToast({ message: err.toString() })
})
},
style: "default"
}
], {
cancelable: true
})
},
style: "default"
}
], {
cancelable: true
})
}} title={i18n(lang, "deleteAllFiles")} />
<SettingsButtonLinkHighlight rightText={formatBytes(accountSettings.versionedStorage)} onPress={() => {
Alert.alert(i18n(lang, "deleteAllVersionedFiles"), i18n(lang, "deleteAllVersionedFilesInfo"), [
{
text: i18n(lang, "cancel"),
onPress: () => {
return false
},
style: "cancel"
},
{
text: i18n(lang, "ok"),
onPress: () => {
Alert.alert(i18n(lang, "deleteAllVersionedFiles"), i18n(lang, "areYouReallySure"), [
{
text: i18n(lang, "cancel"),
onPress: () => {
return false
},
style: "cancel"
},
{
text: i18n(lang, "ok"),
onPress: () => {
useStore.setState({ fullscreenLoadingModalVisible: true })
deleteAllVersionedFiles().then(() => {
useStore.setState({ fullscreenLoadingModalVisible: false })
showToast({ message: i18n(lang, "deleteAllVersionedFilesSuccess") })
setAccountSettings(prev => ({ ...prev, storageUsed: (prev.storageUsed - prev.versionedStorage) }))
setAccountSettings(prev => ({ ...prev, versionedStorage: 0 }))
}).catch((err) => {
console.log(err)
useStore.setState({ fullscreenLoadingModalVisible: false })
showToast({ message: err.toString() })
})
},
style: "default"
}
], {
cancelable: true
})
},
style: "default"
}
], {
cancelable: true
})
}} title={i18n(lang, "deleteAllVersionedFiles")} />
</SettingsGroup>
<SettingsGroup>
<SettingsButtonLinkHighlight onPress={() => setRedeemCodeDialogVisible(true)} title={i18n(lang, "redeemACode")} />
<SettingsButtonLinkHighlight onPress={() => {
navigationAnimation({ enable: true }).then(() => {
navigation.dispatch(StackActions.push("InviteScreen"))
})
}} title={i18n(lang, "invite")} />
</SettingsGroup>
<SettingsGroup>
<SettingsButtonLinkHighlight onPress={() => {
Alert.alert(i18n(lang, "logout"), i18n(lang, "areYouReallySure"), [
{
text: i18n(lang, "cancel"),
onPress: () => {
return false
},
style: "cancel"
},
{
text: i18n(lang, "ok"),
onPress: () => logout({ navigation }),
style: "default"
}
], {
cancelable: true
})
}} title={i18n(lang, "logout")} />
</SettingsGroup>
<SettingsGroup>
<SettingsButtonLinkHighlight onPress={() => {
Alert.alert(i18n(lang, "deleteAccount"), i18n(lang, "deleteAccountInfo"), [
{
text: i18n(lang, "cancel"),
onPress: () => {
return false
},
style: "cancel"
},
{
text: i18n(lang, "ok"),
onPress: () => {
Alert.alert(i18n(lang, "deleteAccount"), i18n(lang, "areYouReallySure"), [
{
text: i18n(lang, "cancel"),
onPress: () => {
return false
},
style: "cancel"
},
{
text: i18n(lang, "ok"),
onPress: () => {
useStore.setState({ fullscreenLoadingModalVisible: true })
getSettings().then((settings) => {
if(settings.twoFactorEnabled){
useStore.setState({ fullscreenLoadingModalVisible: false })
return setDeleteAccountTwoFactorDialogVisible(true)
}
deleteAccount({ twoFactorKey: "XXXXXX" }).then(() => {
useStore.setState({ fullscreenLoadingModalVisible: false })
logout({ navigation })
}).catch((err) => {
console.log(err)
useStore.setState({ fullscreenLoadingModalVisible: false })
showToast({ message: err.toString() })
})
}).catch((err) => {
console.log(err)
useStore.setState({ fullscreenLoadingModalVisible: false })
showToast({ message: err.toString() })
})
},
style: "default"
}
], {
cancelable: true
})
},
style: "default"
}
], {
cancelable: true
})
}} title={i18n(lang, "deleteAccount")} />
</SettingsGroup>
<View style={{ height: 25 }}></View>
</>
)
}
</ScrollView>
</>
)
})
Example #8
Source File: TransfersScreen.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
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>
)
})