mirror of
https://github.com/Stef-00012/Zipline-Android-App.git
synced 2025-05-11 10:25:59 +02:00
finish fullscreen file view, added app settings, moved hooks in fileDisplay to avoid lesser/more hooks error, changed text upload mimetypes to x-zipline-...
This commit is contained in:
parent
2fcca6686e
commit
c2f40e667f
12 changed files with 790 additions and 362 deletions
|
@ -1,7 +1,7 @@
|
|||
import { styles } from "@/styles/components/largeFileDisplay";
|
||||
import type { APIFile, APIFoldersNoIncl, APITags, DashURL } from "@/types/zipline";
|
||||
import { type ColorValue, Pressable, Text, TextInput, ToastAndroid, View } from "react-native";
|
||||
import FileDisplay from "./FileDisplay";
|
||||
import FileDisplay from "@/components/FileDisplay";
|
||||
import * as db from "@/functions/database";
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
|
@ -10,17 +10,19 @@ import { convertToBytes } from "@/functions/util";
|
|||
import { useEffect, useState } from "react";
|
||||
import { getTags } from "@/functions/zipline/tags";
|
||||
import { isLightColor } from "@/functions/color";
|
||||
import { getFolders } from "@/functions/zipline/folders";
|
||||
import { addFileToFolder, getFolders, removeFileFromFolder } from "@/functions/zipline/folders";
|
||||
import axios from "axios";
|
||||
import { editFile } from "@/functions/zipline/files";
|
||||
import { deleteFile, editFile, type EditFileOptions } from "@/functions/zipline/files";
|
||||
import { type ExternalPathString, useRouter } from "expo-router";
|
||||
import * as Clipboard from "expo-clipboard";
|
||||
import * as FileSystem from "expo-file-system"
|
||||
import Popup from "@/components/Popup";
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
file: APIFile;
|
||||
hidden: boolean;
|
||||
onClose: () => void;
|
||||
onClose: (refresh?: boolean) => void | Promise<void>;
|
||||
}
|
||||
|
||||
// WIP
|
||||
|
@ -33,9 +35,24 @@ export default function LargeFileDisplay({ file, hidden, onClose }: Props) {
|
|||
const [folders, setFolders] = useState<APIFoldersNoIncl>([]);
|
||||
|
||||
const [fileContent, setFileContent] = useState<string | null>(null)
|
||||
|
||||
const [filePassword, setFilePassword] = useState<boolean>(file.password)
|
||||
const [fileMaxViews, setFileMaxViews] = useState<number | null>(file.maxViews)
|
||||
const [fileOriginalName, setFileOriginalName] = useState<string | null>(file.originalName)
|
||||
const [fileType, setFileType] = useState<string>(file.type)
|
||||
const [fileFolderId, setFileFolderId] = useState<string | null>(file.folderId)
|
||||
const [fileFavorite, setFileFavorite] = useState<boolean>(file.favorite)
|
||||
|
||||
const [tempHidden, setTempHidden] = useState<boolean>(false)
|
||||
|
||||
const [deleteFilePopup, setDeleteFilePopup] = useState<boolean>(false)
|
||||
const [editFilePopup, setEditFilePopup] = useState<boolean>(false)
|
||||
|
||||
const [editFileMaxViews, setEditFileMaxViews] = useState<number | null>(file.maxViews);
|
||||
const [editFileOriginalName, setEditFileOriginalName] = useState<string | null>(file.originalName);
|
||||
const [editFileType, setEditFileType] = useState<string>(file.type);
|
||||
const [editFilePassword, setEditFilePassword] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const tags = await getTags()
|
||||
|
@ -47,7 +64,7 @@ export default function LargeFileDisplay({ file, hidden, onClose }: Props) {
|
|||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (file.type.startsWith("text/")) {
|
||||
if (fileType.startsWith("text/")) {
|
||||
(async () => {
|
||||
const res = await axios.get(`${dashUrl}/raw/${file.name}`, {
|
||||
responseType: "text"
|
||||
|
@ -56,163 +73,125 @@ export default function LargeFileDisplay({ file, hidden, onClose }: Props) {
|
|||
setFileContent(res.data as string)
|
||||
})()
|
||||
}
|
||||
}, [file, dashUrl])
|
||||
|
||||
setDeleteFilePopup(false)
|
||||
setTempHidden(false)
|
||||
}, [file, dashUrl, fileType])
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={{
|
||||
...styles.popupContainerOverlay,
|
||||
...((hidden || tempHidden || !file) && { display: "none" }),
|
||||
}}
|
||||
onPress={(e) => {
|
||||
if (e.target === e.currentTarget) onClose();
|
||||
}}
|
||||
>
|
||||
<View style={styles.popupContainer}>
|
||||
<Text style={styles.fileHeader}>{file.name}</Text>
|
||||
<>
|
||||
<Popup hidden={!deleteFilePopup} onClose={() => {
|
||||
setDeleteFilePopup(false)
|
||||
setTempHidden(false)
|
||||
}}>
|
||||
<View style={styles.popupContent}>
|
||||
<Text style={styles.mainHeaderText}>Are you sure?</Text>
|
||||
|
||||
<KeyboardAwareScrollView showsVerticalScrollIndicator={false}>
|
||||
{fileContent ? (
|
||||
<TextInput
|
||||
multiline
|
||||
editable={false}
|
||||
style={styles.textDisplay}
|
||||
value={fileContent}
|
||||
/>
|
||||
) : (
|
||||
<FileDisplay
|
||||
passwordProtected={!!file.password}
|
||||
uri={`${dashUrl}/raw/${file.name}`}
|
||||
originalName={file.originalName}
|
||||
mimetype={file.type}
|
||||
name={file.name}
|
||||
maxHeight={500}
|
||||
width={350}
|
||||
file={file}
|
||||
autoHeight
|
||||
/>
|
||||
)}
|
||||
<Text style={styles.serverActionWarningText}>Are you sure you want to delete {file.name}? This action cannot be undone.</Text>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="description" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Type</Text>
|
||||
<Text style={styles.fileInfoText}>{file.type}</Text>
|
||||
</View>
|
||||
<View style={styles.fileDeleteButtonsContainer}>
|
||||
<Pressable style={{
|
||||
...styles.button,
|
||||
...styles.fileDeleteButtonCancel,
|
||||
marginRight: 10
|
||||
}} onPress={() => {
|
||||
setDeleteFilePopup(false)
|
||||
setTempHidden(false)
|
||||
}}>
|
||||
<Text style={styles.buttonText}>Cancel</Text>
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.button,
|
||||
...styles.fileDeleteButtonDanger,
|
||||
marginRight: 10
|
||||
}} onPress={async () => {
|
||||
const fileId = file.id
|
||||
|
||||
const success = await deleteFile(fileId)
|
||||
|
||||
if (typeof success === "string") {
|
||||
ToastAndroid.show(
|
||||
`Error: ${success}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
setDeleteFilePopup(false)
|
||||
setTempHidden(false)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setDeleteFilePopup(false)
|
||||
setTempHidden(false)
|
||||
onClose(true)
|
||||
|
||||
ToastAndroid.show(
|
||||
`Successfully deleted the file ${file.name}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<Text style={styles.buttonText}>Delete {file.name}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="sd-storage" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Size</Text>
|
||||
<Text style={styles.fileInfoText}>{convertToBytes(file.size, {
|
||||
unitSeparator: " "
|
||||
})}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
style={styles.popupSubHeaderText}
|
||||
>
|
||||
Press outside to close this popup
|
||||
</Text>
|
||||
</Popup>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="visibility" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>View</Text>
|
||||
<Text style={styles.fileInfoText}>{file.views}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Popup hidden={!editFilePopup} onClose={() => {
|
||||
setEditFilePopup(false)
|
||||
setTempHidden(false)
|
||||
}}>
|
||||
<View style={styles.popupContent}>
|
||||
<Text style={styles.mainHeaderText}>Editing "{file.name}"</Text>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="file-upload" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Created At</Text>
|
||||
<Text style={styles.fileInfoText}>{new Date(file.createdAt).toLocaleString()}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="autorenew" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Updated At</Text>
|
||||
<Text style={styles.fileInfoText}>{new Date(file.updatedAt).toLocaleString()}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Text style={styles.fileInfoHeader}>Tags</Text>
|
||||
<Select
|
||||
placeholder="Select Tags..."
|
||||
multiple
|
||||
disabled={tags.length <= 0}
|
||||
data={tags.map(tag => ({
|
||||
label: tag.name,
|
||||
value: tag.id,
|
||||
color: tag.color
|
||||
}))}
|
||||
onSelect={console.log}
|
||||
renderItem={(item) => (
|
||||
<View style={styles.selectRenderItemContainer}>
|
||||
<Text style={{
|
||||
...styles.selectRenderItemText,
|
||||
color: isLightColor(item.color as string) ? "black" : "white",
|
||||
backgroundColor: item.color as ColorValue,
|
||||
}}>{item.label}</Text>
|
||||
</View>
|
||||
)}
|
||||
defaultValues={tags.filter(tag => file.tags.find(fileTag => fileTag.id === tag.id)).map(tag => ({
|
||||
label: tag.name,
|
||||
value: tag.id,
|
||||
color: tag.color
|
||||
}))}
|
||||
renderSelectedItem={(item, key) => (
|
||||
<Text key={key} style={{
|
||||
...styles.selectRenderSelectedItemText,
|
||||
color: isLightColor(item.color as string) ? "black" : "white",
|
||||
backgroundColor: item.color as ColorValue,
|
||||
}}>{item.label}</Text>
|
||||
)}
|
||||
maxHeight={500}
|
||||
<Text style={styles.popupHeaderText}>Max Views:</Text>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
onChangeText={(content) => {
|
||||
setEditFileMaxViews(Math.abs(Number.parseInt(content)));
|
||||
}}
|
||||
value={editFileMaxViews ? String(editFileMaxViews) : ""}
|
||||
keyboardType="numeric"
|
||||
placeholder="Unlimited"
|
||||
placeholderTextColor="#222c47"
|
||||
/>
|
||||
|
||||
<Text style={styles.fileInfoHeader}>Folder</Text>
|
||||
{file.folderId ? (
|
||||
<Pressable style={styles.removeFolderButton} onPress={() => console.debug("Remove from folder clicked")}>
|
||||
<Text style={styles.removeFolderButtonText}>Remove from folder "{folders.find(folder => folder.id === file.folderId)?.name}"</Text>
|
||||
</Pressable>
|
||||
) : (
|
||||
<Select
|
||||
placeholder="Add to Folder..."
|
||||
data={folders.map(folder => ({
|
||||
label: folder.name,
|
||||
value: folder.id,
|
||||
}))}
|
||||
defaultValue={file.folderId ? {
|
||||
label: (folders.find(folder => folder.id === file.folderId) as APIFoldersNoIncl[0])?.name,
|
||||
value: file.folderId
|
||||
} : undefined}
|
||||
onSelect={console.log}
|
||||
/>
|
||||
)}
|
||||
<Text style={styles.popupHeaderText}>Original Name:</Text>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
onChangeText={(content) => {
|
||||
setEditFileOriginalName(content);
|
||||
}}
|
||||
value={editFileOriginalName || ""}
|
||||
/>
|
||||
|
||||
<Text style={styles.subHeaderText}>{file.id}</Text>
|
||||
<Text style={{
|
||||
...styles.popupHeaderText,
|
||||
...styles.popupHeaderTextDanger
|
||||
}}>Type:</Text>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
onChangeText={(content) => {
|
||||
setEditFileType(content);
|
||||
}}
|
||||
value={editFileType || ""}
|
||||
/>
|
||||
|
||||
<View style={styles.actionButtonsContainer}>
|
||||
{filePassword ? (
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...styles.actionButtonEdit
|
||||
}}>
|
||||
<MaterialIcons name="edit" size={20} color="white" />
|
||||
</Pressable>
|
||||
...styles.button,
|
||||
...styles.buttonDanger
|
||||
}} onPress={() => {
|
||||
const fileId = file.id
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...styles.actionButtonDelete
|
||||
}}>
|
||||
<MaterialIcons name="delete" size={20} color="white" />
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...(!file.favorite && styles.actionButtonFavorite)
|
||||
}} onPress={async () => {
|
||||
const success = editFile(file.id, {
|
||||
favorite: !file.favorite
|
||||
const success = editFile(fileId, {
|
||||
password: null
|
||||
})
|
||||
|
||||
if (typeof success === "string") return ToastAndroid.show(
|
||||
|
@ -220,96 +199,413 @@ export default function LargeFileDisplay({ file, hidden, onClose }: Props) {
|
|||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
file.favorite = !file.favorite
|
||||
setFilePassword(false)
|
||||
file.password = false
|
||||
|
||||
ToastAndroid.show(
|
||||
`Successfully ${file.favorite ? "added to" : "removed from"} favorites`,
|
||||
"Successfully removed the password",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<MaterialIcons name={file.favorite ? "star-outline" : "star"} size={20} color="white" />
|
||||
<Text style={styles.buttonText}>Remove Password</Text>
|
||||
</Pressable>
|
||||
) : (
|
||||
<>
|
||||
<Text style={styles.popupHeaderText}>Password:</Text>
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
secureTextEntry={true}
|
||||
keyboardType="visible-password"
|
||||
onChangeText={(content) => {
|
||||
setEditFilePassword(content);
|
||||
}}
|
||||
value={editFilePassword || ""}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...styles.actionButtonOpen
|
||||
}} onPress={() => {
|
||||
router.replace(`${dashUrl}${file.url}` as ExternalPathString)
|
||||
}}>
|
||||
<MaterialIcons name="open-in-new" size={20} color="white" />
|
||||
</Pressable>
|
||||
<Pressable
|
||||
style={styles.button}
|
||||
onPress={async () => {
|
||||
const fileId = file.id
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton
|
||||
}} onPress={async () => {
|
||||
const url = `${dashUrl}${file.url}`
|
||||
const editData: EditFileOptions = {}
|
||||
|
||||
const success = await Clipboard.setStringAsync(url)
|
||||
editData.maxViews = editFileMaxViews || null
|
||||
editData.type = editFileType
|
||||
if (editFileOriginalName) editData.originalName = editFileOriginalName
|
||||
if (editFilePassword) editData.password = editFilePassword
|
||||
|
||||
if (!success) return ToastAndroid.show(
|
||||
"Failed to copy the URL",
|
||||
const success = await editFile(fileId, editData)
|
||||
|
||||
if (typeof success === "string") return ToastAndroid.show(
|
||||
`Error: ${success}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
ToastAndroid.show(
|
||||
"Copied URL to clipboard",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<MaterialIcons name="content-copy" size={20} color="white" />
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton
|
||||
}} onPress={async () => {
|
||||
const downloadUrl = `${dashUrl}/raw/${file.name}?download=true`
|
||||
|
||||
let savedFileDownloadUri = db.get("fileDownloadPath")
|
||||
|
||||
if (!savedFileDownloadUri) {
|
||||
const permissions = await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
|
||||
|
||||
if (!permissions.granted) return ToastAndroid.show(
|
||||
"The permission to save the file was not granted",
|
||||
ToastAndroid.SHORT
|
||||
);
|
||||
|
||||
db.set("fileDownloadPath", permissions.directoryUri)
|
||||
savedFileDownloadUri = permissions.directoryUri
|
||||
|
||||
if (editFilePassword) {
|
||||
setFilePassword(true)
|
||||
file.password = true
|
||||
}
|
||||
|
||||
ToastAndroid.show(
|
||||
"Downloading...",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
file.originalName = editFileOriginalName || null
|
||||
setFileOriginalName(editFileOriginalName || null)
|
||||
|
||||
const saveUri = await FileSystem.StorageAccessFramework.createFileAsync(savedFileDownloadUri, file.name, file.type)
|
||||
file.type = editFileType
|
||||
setFileType(editFileType)
|
||||
|
||||
const downloadResult = await FileSystem.downloadAsync(downloadUrl, `${FileSystem.cacheDirectory}/${file.name}`)
|
||||
file.maxViews = editFileMaxViews || null
|
||||
setFileMaxViews(editFileMaxViews)
|
||||
|
||||
if (!downloadResult.uri) return ToastAndroid.show(
|
||||
"Something went wrong while downloading the file",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
const base64File = await FileSystem.readAsStringAsync(downloadResult.uri, {
|
||||
encoding: FileSystem.EncodingType.Base64
|
||||
})
|
||||
|
||||
await FileSystem.writeAsStringAsync(saveUri, base64File, {
|
||||
encoding: FileSystem.EncodingType.Base64
|
||||
})
|
||||
setEditFilePopup(false)
|
||||
setTempHidden(false)
|
||||
|
||||
ToastAndroid.show(
|
||||
"Successfully downloaded the file",
|
||||
`Successfully edited the file ${file.name}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<MaterialIcons name="file-download" size={20} color="white" />
|
||||
</Pressable>
|
||||
</View>
|
||||
</KeyboardAwareScrollView>
|
||||
</View>
|
||||
</Pressable>
|
||||
}}
|
||||
>
|
||||
<Text style={styles.buttonText}>Save Changes</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</Popup>
|
||||
|
||||
<Pressable
|
||||
style={{
|
||||
...styles.popupContainerOverlay,
|
||||
...((hidden || tempHidden || !file) && { display: "none" }),
|
||||
}}
|
||||
onPress={(e) => {
|
||||
if (e.target === e.currentTarget) onClose();
|
||||
}}
|
||||
>
|
||||
<View style={styles.popupContainer}>
|
||||
<Text style={styles.fileHeader}>{file.name}</Text>
|
||||
|
||||
<KeyboardAwareScrollView showsVerticalScrollIndicator={false}>
|
||||
{fileContent ? (
|
||||
<TextInput
|
||||
multiline
|
||||
editable={false}
|
||||
style={styles.textDisplay}
|
||||
value={fileContent}
|
||||
/>
|
||||
) : (
|
||||
<FileDisplay
|
||||
passwordProtected={!!filePassword}
|
||||
uri={`${dashUrl}/raw/${file.name}`}
|
||||
originalName={fileOriginalName}
|
||||
mimetype={fileType}
|
||||
name={file.name}
|
||||
maxHeight={500}
|
||||
width={350}
|
||||
file={file}
|
||||
autoHeight
|
||||
/>
|
||||
)}
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="description" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Type</Text>
|
||||
<Text style={styles.fileInfoText}>{file.type}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="sd-storage" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Size</Text>
|
||||
<Text style={styles.fileInfoText}>{convertToBytes(file.size, {
|
||||
unitSeparator: " "
|
||||
})}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="visibility" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>View</Text>
|
||||
<Text style={styles.fileInfoText}>{file.views}{(fileMaxViews && !Number.isNaN(fileMaxViews)) && `/${fileMaxViews}`}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="file-upload" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Created At</Text>
|
||||
<Text style={styles.fileInfoText}>{new Date(file.createdAt).toLocaleString()}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="autorenew" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Updated At</Text>
|
||||
<Text style={styles.fileInfoText}>{new Date(file.updatedAt).toLocaleString()}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{file.deletesAt && (
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="auto-delete" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Deletes At</Text>
|
||||
<Text style={styles.fileInfoText}>{new Date(file.deletesAt).toLocaleString()}</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{fileOriginalName && (
|
||||
<View style={styles.fileInfoContainer}>
|
||||
<MaterialIcons name="title" size={28} color="white" />
|
||||
<View style={styles.fileInfoTextContainer}>
|
||||
<Text style={styles.fileInfoHeader}>Original Name</Text>
|
||||
<Text style={styles.fileInfoText}>{fileOriginalName}</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Text style={styles.fileInfoHeader}>Tags</Text>
|
||||
<Select
|
||||
placeholder="Select Tags..."
|
||||
multiple
|
||||
disabled={tags.length <= 0}
|
||||
data={tags.map(tag => ({
|
||||
label: tag.name,
|
||||
value: tag.id,
|
||||
color: tag.color
|
||||
}))}
|
||||
onSelect={async (selectedTags) => {
|
||||
const newTags = selectedTags.map(tag => tag.value)
|
||||
|
||||
const success = editFile(file.id, {
|
||||
tags: newTags
|
||||
})
|
||||
|
||||
if (typeof success === "string") return ToastAndroid.show(
|
||||
`Error: ${success}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
file.tags = tags.filter(tag => newTags.includes(tag.id))
|
||||
|
||||
ToastAndroid.show(
|
||||
"Successfully updated the tags",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}
|
||||
renderItem={(item) => (
|
||||
<View style={styles.selectRenderItemContainer}>
|
||||
<Text style={{
|
||||
...styles.selectRenderItemText,
|
||||
color: isLightColor(item.color as string) ? "black" : "white",
|
||||
backgroundColor: item.color as ColorValue,
|
||||
}}>{item.label}</Text>
|
||||
</View>
|
||||
)}
|
||||
defaultValues={tags.filter(tag => file.tags.find(fileTag => fileTag.id === tag.id)).map(tag => ({
|
||||
label: tag.name,
|
||||
value: tag.id,
|
||||
color: tag.color
|
||||
}))}
|
||||
renderSelectedItem={(item, key) => (
|
||||
<Text key={key} style={{
|
||||
...styles.selectRenderSelectedItemText,
|
||||
color: isLightColor(item.color as string) ? "black" : "white",
|
||||
backgroundColor: item.color as ColorValue,
|
||||
}}>{item.label}</Text>
|
||||
)}
|
||||
maxHeight={500}
|
||||
/>
|
||||
|
||||
<Text style={styles.fileInfoHeader}>Folder</Text>
|
||||
{fileFolderId ? (
|
||||
<Pressable style={styles.removeFolderButton} onPress={async () => {
|
||||
if (!fileFolderId) return;
|
||||
|
||||
const folderId = fileFolderId
|
||||
const fileId = file.id
|
||||
|
||||
const success = removeFileFromFolder(folderId, fileId)
|
||||
|
||||
if (typeof success === "string") return ToastAndroid.show(
|
||||
`Error: ${success}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
setFileFolderId(null)
|
||||
file.folderId = null
|
||||
|
||||
ToastAndroid.show(
|
||||
"Successfully removed the file from the folder",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<Text style={styles.removeFolderButtonText}>Remove from folder "{folders.find(folder => folder.id === file.folderId)?.name}"</Text>
|
||||
</Pressable>
|
||||
) : (
|
||||
<Select
|
||||
placeholder="Add to Folder..."
|
||||
data={folders.map(folder => ({
|
||||
label: folder.name,
|
||||
value: folder.id,
|
||||
}))}
|
||||
defaultValue={file.folderId ? {
|
||||
label: (folders.find(folder => folder.id === file.folderId) as APIFoldersNoIncl[0])?.name,
|
||||
value: file.folderId
|
||||
} : undefined}
|
||||
onSelect={async (selectedFolder) => {
|
||||
const folderId = selectedFolder[0].value
|
||||
const fileId = file.id
|
||||
|
||||
const success = await addFileToFolder(folderId, fileId)
|
||||
|
||||
if (typeof success === "string") return ToastAndroid.show(
|
||||
`Error: ${success}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
setFileFolderId(folderId)
|
||||
file.folderId = folderId
|
||||
|
||||
ToastAndroid.show(
|
||||
"Successfully added the file to the folder",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Text style={styles.subHeaderText}>{file.id}</Text>
|
||||
|
||||
<View style={styles.actionButtonsContainer}>
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...styles.actionButtonEdit
|
||||
}} onPress={() => {
|
||||
setEditFilePopup(true)
|
||||
setTempHidden(true)
|
||||
}}>
|
||||
<MaterialIcons name="edit" size={20} color="white" />
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...styles.actionButtonDelete
|
||||
}} onPress={() => {
|
||||
setDeleteFilePopup(true)
|
||||
setTempHidden(true)
|
||||
}}>
|
||||
<MaterialIcons name="delete" size={20} color="white" />
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...(fileFavorite && styles.actionButtonFavorite)
|
||||
}} onPress={async () => {
|
||||
const success = editFile(file.id, {
|
||||
favorite: !file.favorite
|
||||
})
|
||||
|
||||
if (typeof success === "string") return ToastAndroid.show(
|
||||
`Error: ${success}`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
file.favorite = !fileFavorite
|
||||
setFileFavorite((prev) => !prev)
|
||||
|
||||
ToastAndroid.show(
|
||||
`Successfully ${fileFavorite ? "removed from" : "added to"} favorites`,
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<MaterialIcons name={fileFavorite ? "star" : "star-outline"} size={20} color="white" />
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton,
|
||||
...styles.actionButtonOpen
|
||||
}} onPress={() => {
|
||||
router.replace(`${dashUrl}${file.url}` as ExternalPathString)
|
||||
}}>
|
||||
<MaterialIcons name="open-in-new" size={20} color="white" />
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton
|
||||
}} onPress={async () => {
|
||||
const url = `${dashUrl}${file.url}`
|
||||
|
||||
const success = await Clipboard.setStringAsync(url)
|
||||
|
||||
if (!success) return ToastAndroid.show(
|
||||
"Failed to copy the URL",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
ToastAndroid.show(
|
||||
"Copied URL to clipboard",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<MaterialIcons name="content-copy" size={20} color="white" />
|
||||
</Pressable>
|
||||
|
||||
<Pressable style={{
|
||||
...styles.actionButton
|
||||
}} onPress={async () => {
|
||||
const downloadUrl = `${dashUrl}/raw/${file.name}?download=true`
|
||||
|
||||
let savedFileDownloadUri = db.get("fileDownloadPath")
|
||||
|
||||
if (!savedFileDownloadUri) {
|
||||
const permissions = await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
|
||||
|
||||
if (!permissions.granted) return ToastAndroid.show(
|
||||
"The permission to save the file was not granted",
|
||||
ToastAndroid.SHORT
|
||||
);
|
||||
|
||||
db.set("fileDownloadPath", permissions.directoryUri)
|
||||
savedFileDownloadUri = permissions.directoryUri
|
||||
}
|
||||
|
||||
ToastAndroid.show(
|
||||
"Downloading...",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
const saveUri = await FileSystem.StorageAccessFramework.createFileAsync(savedFileDownloadUri, file.name, file.type)
|
||||
|
||||
const downloadResult = await FileSystem.downloadAsync(downloadUrl, `${FileSystem.cacheDirectory}/${file.name}`)
|
||||
|
||||
if (!downloadResult.uri) return ToastAndroid.show(
|
||||
"Something went wrong while downloading the file",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
|
||||
const base64File = await FileSystem.readAsStringAsync(downloadResult.uri, {
|
||||
encoding: FileSystem.EncodingType.Base64
|
||||
})
|
||||
|
||||
await FileSystem.writeAsStringAsync(saveUri, base64File, {
|
||||
encoding: FileSystem.EncodingType.Base64
|
||||
})
|
||||
|
||||
ToastAndroid.show(
|
||||
"Successfully downloaded the file",
|
||||
ToastAndroid.SHORT
|
||||
)
|
||||
}}>
|
||||
<MaterialIcons name="file-download" size={20} color="white" />
|
||||
</Pressable>
|
||||
</View>
|
||||
</KeyboardAwareScrollView>
|
||||
</View>
|
||||
</Pressable>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue