[nobuild] continue building skeleton UI - unfinished

This commit is contained in:
Stef-00012 2025-05-04 21:26:41 +02:00
parent 7e7b99754a
commit 7e23483dfb
No known key found for this signature in database
GPG key ID: 28BE9A9E4EF0E6BF
8 changed files with 461 additions and 137 deletions

View file

@ -46,6 +46,9 @@ import {
View, View,
} from "react-native"; } from "react-native";
import ColorPicker from "@/components/ColorPicker"; import ColorPicker from "@/components/ColorPicker";
import SkeletonTable from "@/components/skeleton/Table";
import { Skeleton } from "moti/skeleton";
import { colors } from "@/constants/skeleton";
export default function Files() { export default function Files() {
const router = useRouter(); const router = useRouter();
@ -1177,30 +1180,58 @@ export default function Files() {
})} })}
/> />
) : ( ) : (
<> <ScrollView showsVerticalScrollIndicator={false}>
<ScrollView showsVerticalScrollIndicator={false}> {files.page.map((file) => (
{files.page.map((file) => ( <View key={file.id} style={styles.imageContainer}>
<View key={file.id} style={styles.imageContainer}> <FileDisplay
<FileDisplay uri={`${url}/raw/${file.name}`}
uri={`${url}/raw/${file.name}`} width={filesWidth - 50}
width={filesWidth - 50} originalName={file.originalName}
originalName={file.originalName} name={file.name}
name={file.name} autoHeight
autoHeight passwordProtected={file.password}
passwordProtected={file.password} file={file}
file={file} onPress={() => setFocusedFile(file)}
onPress={() => setFocusedFile(file)} />
/> </View>
</View> ))}
))} </ScrollView>
</ScrollView>
</>
)} )}
</> </>
) : ( ) : (
<View style={styles.loadingContainer}> <>
<Text style={styles.loadingText}>Loading...</Text> {compactModeEnabled ? (
</View> <SkeletonTable
headerRow={[
"Name",
"Tags",
"Type",
"Size",
"Created At",
"Favorite",
"ID",
"Actions",
]}
rowWidth={[150, 150, 150, 80, 130, 100, 220, 170]}
rows={[...Array(10).keys()].map(() => {
return [80, 40, 60, 60, 90, 20, 120, 150];
})}
rowHeight={55}
disableAnimations
/>
) : (
<ScrollView showsVerticalScrollIndicator={false}>
{[...Array(4).keys()].map(index => (
<View key={index} style={{
marginVertical: 5,
marginHorizontal: 5
}}>
<Skeleton colors={colors} width="100%" height={250} />
</View>
))}
</ScrollView>
)}
</>
)} )}
</ScrollView> </ScrollView>

View file

@ -21,6 +21,9 @@ import {
editFolder, editFolder,
getFolders, getFolders,
} from "@/functions/zipline/folders"; } from "@/functions/zipline/folders";
import SkeletonTable from "@/components/skeleton/Table";
import { Skeleton } from "moti/skeleton";
import { colors } from "@/constants/skeleton";
export type FolderActions = export type FolderActions =
| "viewFiles" | "viewFiles"
@ -675,9 +678,39 @@ export default function Folders() {
)} )}
</> </>
) : ( ) : (
<View style={styles.loadingContainer}> <>
<Text style={styles.loadingText}>Loading...</Text> {compactModeEnabled ? (
</View> <SkeletonTable
headerRow={[
"Name",
"Public",
"Uploads?",
"Created",
"Last Updated At",
"Files",
"ID",
"Actions",
]}
rowWidth={[140, 90, 110, 140, 150, 100, 220, 252]}
rows={[...Array(12).keys()].map(() => {
return [80, 30, 30, 90, 90, 40, 120, 200];
})}
rowHeight={55}
disableAnimations
/>
) : (
<ScrollView showsVerticalScrollIndicator={false}>
{[...Array(4).keys()].map(index => (
<View key={index} style={{
marginVertical: 5,
marginHorizontal: 5
}}>
<Skeleton colors={colors} width="100%" height={200} />
</View>
))}
</ScrollView>
)}
</>
)} )}
</View> </View>
</View> </View>

View file

@ -24,6 +24,9 @@ import {
type StatsProps, type StatsProps,
} from "@/functions/zipline/stats"; } from "@/functions/zipline/stats";
import { MaterialIcons } from "@expo/vector-icons"; import { MaterialIcons } from "@expo/vector-icons";
import { Skeleton } from "moti/skeleton";
import { colors } from "@/constants/skeleton";
import SkeletonTable from "@/components/skeleton/Table";
export default function Metrics() { export default function Metrics() {
useAuth(); useAuth();
@ -222,7 +225,7 @@ export default function Metrics() {
difference: statsDifferences.files, difference: statsDifferences.files,
}, },
{ {
title: "URLs", title: "URLs:",
amount: mainStat.data.urls, amount: mainStat.data.urls,
difference: statsDifferences.urls, difference: statsDifferences.urls,
}, },
@ -656,8 +659,166 @@ export default function Metrics() {
</ScrollView> </ScrollView>
</View> </View>
) : ( ) : (
<View style={styles.loadingContainer}> <View style={styles.mainContainer}>
<Text style={styles.loadingText}>Loading...</Text> <Skeleton.Group show={!filteredStats || !mainStat}>
<ScrollView style={{ height: "93%" }}>
<ScrollView horizontal style={styles.scrollView}>
{[
"Files:",
"URLs:",
"Storage Used:",
"Users:",
"File Views:",
"URL Views:",
].map((stat) => (
<View key={stat} style={styles.statContainer}>
<Text style={styles.subHeaderText}>{stat}</Text>
<View style={styles.statContainerData}>
<Skeleton colors={colors} height={36} width={60} />
<View style={{
width: 5
}} />
<View style={{
marginTop: 9
}}>
<Skeleton colors={colors} height={27} width={40} />
</View>
</View>
</View>
))}
</ScrollView>
{userSpecificMetrics && (
<>
<View
style={{
...styles.chartContainer,
padding: 0,
}}
>
<SkeletonTable
headerRow={[
"User",
"URLs",
"Views",
]}
rowWidth={[190, 100, 100]}
rows={[[80, 50]]}
/>
</View>
<View
style={{
...styles.chartContainer,
padding: 0,
}}
>
<SkeletonTable
headerRow={[
"User",
"Files",
"Storage Used",
"Views",
]}
rowWidth={[150, 60, 130, 50]}
rows={[[70, 50, 100, 50]]}
/>
</View>
<View
style={{
...styles.chartContainer,
padding: 0,
}}
>
<SkeletonTable
headerRow={[
"Type",
"Files",
]}
rowWidth={[tableTypeWidth, tableFilesWidth]}
rows={[...Array(4).keys()].map(() => {
return ["55%", 30];
})}
/>
</View>
</>
)}
<View style={styles.chartContainer}>
<View style={styles.pieChartContainer}>
<Skeleton colors={colors} radius="round" width={250} height={250} />
</View>
<View style={{
flexDirection: "row",
marginTop: 10
}}>
{[...Array(5).keys()].map((index) => (
<View key={index} style={{
marginHorizontal: 2.5
}}>
<Skeleton colors={colors} width={60} height={16} />
</View>
))}
</View>
</View>
<View style={styles.chartContainer}>
<Text style={styles.chartTitle}>Count</Text>
<Skeleton colors={colors} width="100%" height={220} />
<ChartLegend
data={[
{
label: "Files",
color: "#323ea8",
},
{
label: "URLs",
color: "#2f9e44",
},
]}
/>
</View>
<View style={styles.chartContainer}>
<Text style={styles.chartTitle}>Views</Text>
<Skeleton colors={colors} width="100%" height={220} />
<ChartLegend
data={[
{
label: "File Views",
color: "#323ea8",
},
{
label: "URL Views",
color: "#2f9e44",
},
]}
/>
</View>
<View style={styles.chartContainer}>
<Text style={styles.chartTitle}>Storage Used</Text>
<Skeleton colors={colors} width="100%" height={220} />
<ChartLegend
data={[
{
label: "Storage Used",
color: "#323ea8",
},
]}
/>
</View>
</ScrollView>
</Skeleton.Group>
</View> </View>
)} )}
</View> </View>

View file

@ -24,6 +24,9 @@ import {
editURL, editURL,
getURLs, getURLs,
} from "@/functions/zipline/urls"; } from "@/functions/zipline/urls";
import SkeletonTable from "@/components/skeleton/Table";
import { Skeleton } from "moti/skeleton";
import { colors } from "@/constants/skeleton";
export type URLActions = export type URLActions =
| "copyShortLink" | "copyShortLink"
@ -140,8 +143,8 @@ export default function Urls() {
switch (type) { switch (type) {
case "copyShortLink": { case "copyShortLink": {
const urlDest = url.vanity const urlDest = url.vanity
? `${dashUrl}${settings?.urlsRoute === "/" ? "" : settings?.urlsRoute}/${url.vanity}` ? `${dashUrl}${settings?.urlsRoute === "/" ? "" : settings?.urlsRoute || "/go"}/${url.vanity}`
: `${dashUrl}${settings?.urlsRoute === "/" ? "" : settings?.urlsRoute}/${url.code}`; : `${dashUrl}${settings?.urlsRoute === "/" ? "" : settings?.urlsRoute || "/go"}/${url.code}`;
const saved = await Clipboard.setStringAsync(urlDest); const saved = await Clipboard.setStringAsync(urlDest);
@ -455,13 +458,13 @@ export default function Urls() {
}} }}
icon="add-link" icon="add-link"
color="transparent" color="transparent"
iconColor={urls && settings && dashUrl ? "#2d3f70" : "#2d3f7055"} iconColor={urls && dashUrl ? "#2d3f70" : "#2d3f7055"}
borderColor="#222c47" borderColor="#222c47"
borderWidth={2} borderWidth={2}
iconSize={30} iconSize={30}
padding={4} padding={4}
rippleColor="#283557" rippleColor="#283557"
disabled={!urls || !dashUrl || !settings} disabled={!urls || !dashUrl}
margin={{ margin={{
left: 2, left: 2,
right: 2, right: 2,
@ -479,13 +482,13 @@ export default function Urls() {
}} }}
icon={compactModeEnabled ? "view-module" : "view-agenda"} icon={compactModeEnabled ? "view-module" : "view-agenda"}
color="transparent" color="transparent"
iconColor={urls && settings && dashUrl ? "#2d3f70" : "#2d3f7055"} iconColor={urls && dashUrl ? "#2d3f70" : "#2d3f7055"}
borderColor="#222c47" borderColor="#222c47"
borderWidth={2} borderWidth={2}
iconSize={30} iconSize={30}
padding={4} padding={4}
rippleColor="#283557" rippleColor="#283557"
disabled={!urls || !dashUrl || !settings} disabled={!urls || !dashUrl}
margin={{ margin={{
left: 2, left: 2,
right: 2, right: 2,
@ -532,7 +535,7 @@ export default function Urls() {
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<View style={styles.urlsContainer}> <View style={styles.urlsContainer}>
{urls && settings && dashUrl ? ( {urls && dashUrl ? (
<> <>
{compactModeEnabled ? ( {compactModeEnabled ? (
<Table <Table
@ -644,7 +647,7 @@ export default function Urls() {
<Link <Link
key={url.id} key={url.id}
href={ href={
`${dashUrl}${settings.urlsRoute === "/" ? "" : settings.urlsRoute}/${url.code}` as ExternalPathString `${dashUrl}${settings?.urlsRoute === "/" ? "" : settings?.urlsRoute || "/go"}/${url.code}` as ExternalPathString
} }
style={{ style={{
...styles.rowText, ...styles.rowText,
@ -661,7 +664,7 @@ export default function Urls() {
<Link <Link
key={url.id} key={url.id}
href={ href={
`${dashUrl}${settings.urlsRoute === "/" ? "" : settings.urlsRoute}/${url.vanity}` as ExternalPathString `${dashUrl}${settings?.urlsRoute === "/" ? "" : settings?.urlsRoute || "/go"}/${url.vanity}` as ExternalPathString
} }
style={{ style={{
...styles.rowText, ...styles.rowText,
@ -783,7 +786,7 @@ export default function Urls() {
<LargeURLView <LargeURLView
key={url.id} key={url.id}
url={url} url={url}
urlsRoute={settings.urlsRoute} urlsRoute={settings?.urlsRoute || "/go"}
dashUrl={dashUrl} dashUrl={dashUrl}
onAction={onAction} onAction={onAction}
/> />
@ -792,9 +795,40 @@ export default function Urls() {
)} )}
</> </>
) : ( ) : (
<View style={styles.loadingContainer}> <>
<Text style={styles.loadingText}>Loading...</Text> {compactModeEnabled ? (
</View> <SkeletonTable
headerRow={[
"Code",
"Vanity",
"Destination",
"Views",
"Max Views",
"Created",
"Enabled",
"ID",
"Actions",
]}
rowWidth={[100, 120, 300, 110, 140, 130, 100, 220, 130]}
rows={[...Array(12).keys()].map(() => {
return [60, 50, 200, 30, 30, 70, 40, 180, 90];
})}
rowHeight={55}
disableAnimations
/>
) : (
<ScrollView showsVerticalScrollIndicator={false}>
{[...Array(4).keys()].map(index => (
<View key={index} style={{
marginVertical: 5,
marginHorizontal: 5
}}>
<Skeleton colors={colors} width="100%" height={200} />
</View>
))}
</ScrollView>
)}
</>
)} )}
</View> </View>
</View> </View>

View file

@ -53,11 +53,13 @@ export default function Layout() {
</Host> </Host>
</Header> </Header>
) : ( ) : (
<View style={styles.noInternetContainer}> <Header>
<Text style={styles.noInternetText}> <View style={styles.noInternetContainer}>
No internet connection. <Text style={styles.noInternetText}>
</Text> No internet connection.
</View> </Text>
</View>
</Header>
)} )}
</GestureHandlerRootView> </GestureHandlerRootView>
</KeyboardProvider> </KeyboardProvider>

View file

@ -257,24 +257,31 @@ export default function Home() {
) : ( ) : (
<View style={styles.mainContainer}> <View style={styles.mainContainer}>
<Skeleton.Group show={!user}> <Skeleton.Group show={!user}>
<ScrollView style={{ <ScrollView
marginTop: 5 style={{
}}> marginTop: 5,
<View> }}
<Skeleton colors={colors} width="70%" height={28} /> >
<View
style={{
flexDirection: "row",
}}
>
<Text style={styles.mainHeader}>Welcome back, </Text>
<Skeleton colors={colors} width="60%" height={36} />
</View> </View>
<View style={{ <View
marginTop: 5 style={{
}}> marginTop: 5,
<Skeleton colors={colors} width="50%" height={16} /> }}
>
<Text style={styles.subHeader}>
You have ## files uploaded.
</Text>
</View> </View>
<View style={{ <Text style={styles.headerText}>Recent Files</Text>
marginTop: 7
}}>
<Skeleton colors={colors} width="30%" height={28} />
</View>
<ScrollView horizontal style={styles.scrollView}> <ScrollView horizontal style={styles.scrollView}>
{[1, 2, 3].map((file) => ( {[1, 2, 3].map((file) => (
@ -289,11 +296,7 @@ export default function Home() {
))} ))}
</ScrollView> </ScrollView>
<View style={{ <Text style={styles.headerText}>Stats</Text>
marginTop: 7
}}>
<Skeleton colors={colors} width="20%" height={28} />
</View>
<ScrollView <ScrollView
horizontal horizontal
@ -301,24 +304,35 @@ export default function Home() {
...styles.scrollView, ...styles.scrollView,
}} }}
> >
{ {[
[1, 2, 3, 4, 5, 6, 7, 8].map(stat => ( ["Files Uploaded:", 60],
<View key={stat} style={{ ["Favorite Files:", 50],
marginHorizontal: 4, ["Storage Used:", 90],
marginVertical: 7.5 ["Average Storage Used:", 70],
}}> ["File Views:", 50],
<Skeleton colors={colors} width={170} height={100} /> ["File Average Views:", 50],
["Links Created:", 60],
["Total Link View:", 50],
].map((stat) => (
<View key={stat[0]} style={styles.statContainer}>
<Text style={styles.subHeaderText}>{stat[0]}</Text>
<View
style={{
marginTop: 5,
}}
>
<Skeleton
colors={colors}
width={stat[1] as number}
height={36}
/>
</View> </View>
)) </View>
} ))}
</ScrollView> </ScrollView>
<View style={{ <Text style={styles.headerText}>File Types</Text>
marginTop: 7
}}>
<Skeleton colors={colors} width="20%" height={28} />
</View>
<View <View
style={{ style={{
...styles.scrollView, ...styles.scrollView,
@ -326,17 +340,10 @@ export default function Home() {
}} }}
> >
<SkeletonTable <SkeletonTable
headerRow={[ headerRow={["File Type", "Count"]}
{
row: "File Type",
},
{
row: "Count",
},
]}
rowWidth={[250, 150]} rowWidth={[250, 150]}
rows={[...Array(10).keys()].map((index) => { rows={[...Array(4).keys()].map(() => {
return ["55%", 30] return ["55%", 30];
})} })}
/> />
</View> </View>

View file

@ -11,6 +11,8 @@ import Sidebar from "@/components/Sidebar";
import type React from "react"; import type React from "react";
import { useShareIntent } from "@/hooks/useShareIntent"; import { useShareIntent } from "@/hooks/useShareIntent";
import { getRippleColor } from "@/functions/util"; import { getRippleColor } from "@/functions/util";
import { Skeleton } from "moti/skeleton";
import { colors } from "@/constants/skeleton";
export default function Header({ children }: PropsWithChildren) { export default function Header({ children }: PropsWithChildren) {
const router = useRouter(); const router = useRouter();
@ -25,13 +27,7 @@ export default function Header({ children }: PropsWithChildren) {
useEffect(() => { useEffect(() => {
if (!avatar || !user) { if (!avatar || !user) {
(async () => { fetchUser()
const avatar = await getCurrentUserAvatar();
const user = await getCurrentUser();
setAvatar(avatar);
setUser(typeof user === "string" ? null : user);
})();
} }
}, [avatar, user]); }, [avatar, user]);
@ -103,7 +99,43 @@ export default function Header({ children }: PropsWithChildren) {
</View> </View>
</View> </View>
) : ( ) : (
<View /> <View
style={{
marginBottom: 70,
}}
>
<Skeleton.Group show={!user}>
<View style={styles.header}>
<View style={styles.headerLeft}>
<IconButton
disabled
icon={() => (
<MaterialIcons name="menu" color={"#ffffff77"} size={40} />
)}
android_ripple={{
color: getRippleColor("#0c101c")
}}
onPress={() => {
setSidebarOpen((prev) => !prev);
}}
/>
</View>
<Stack>
<View>
<View
style={{
padding: 10,
borderRadius: 10
}}
>
<Skeleton colors={colors} height={36} width={80} />
</View>
</View>
</Stack>
</View>
</Skeleton.Group>
</View>
)} )}
<Sidebar <Sidebar
open={sidebarOpen} open={sidebarOpen}

View file

@ -2,42 +2,39 @@ import { Table as NativeTable, Row } from "react-native-reanimated-table";
import { styles } from "@/styles/components/table"; import { styles } from "@/styles/components/table";
import { Skeleton } from "moti/skeleton"; import { Skeleton } from "moti/skeleton";
import { colors } from "@/constants/skeleton"; import { colors } from "@/constants/skeleton";
import { import { ScrollView, Text, View, type DimensionValue } from "react-native";
Pressable, import type { ReactNode } from "react";
ScrollView,
Text,
View,
type DimensionValue,
} from "react-native";
interface Props { interface Props {
headerRow: Array<string>; headerRow: Array<ReactNode>;
rows: Array<Array<DimensionValue>>; rows: Array<Array<DimensionValue>>;
rowWidth: Array<number>; rowWidth: Array<number>;
rowHeight?: DimensionValue;
disableAnimations?: boolean;
} }
export default function SkeletonTable({ export default function SkeletonTable({
headerRow, headerRow,
rows, rows,
rowWidth rowWidth,
rowHeight,
disableAnimations,
}: Props) { }: Props) {
const updatedHeaderRow = headerRow.map((row) => { const updatedHeaderRow = headerRow.map((row, index) => (
return ["string", "number", "boolean"].includes(typeof row.row) ? ( <Text
<Text // biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
style={{ key={index}
...styles.rowText, style={{
...styles.headerRow, ...styles.rowText,
}} ...styles.headerRow,
> }}
{row.row} >
</Text> {row}
) : ( </Text>
row.row ));
)
}) return (
<ScrollView showsHorizontalScrollIndicator={false} horizontal>
return (
<ScrollView showsHorizontalScrollIndicator={false} horizontal>
<View> <View>
<NativeTable> <NativeTable>
<Row <Row
@ -57,25 +54,52 @@ export default function SkeletonTable({
> >
<NativeTable> <NativeTable>
{rows.map((row, index) => { {rows.map((row, index) => {
let rowStyle = styles.row; let rowStyle = {
...styles.row,
...(rowHeight && { height: rowHeight }),
};
if (index === 0) if (index === 0)
rowStyle = { rowStyle = {
...styles.row, ...styles.row,
...styles.firstRow, ...styles.firstRow,
...(rowHeight && { height: rowHeight }),
}; };
if (index === rows.length - 1) if (index === rows.length - 1)
rowStyle = { rowStyle = {
...styles.row, ...styles.row,
...styles.lastRow, ...styles.lastRow,
...(rowHeight && { height: rowHeight }),
}; };
const rowData = row.map(data => { const rowData = row.map((data, index) => {
return ( return (
<Skeleton colors={colors} height={14} width={data} /> <>
) {disableAnimations ? (
}) <View
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
key={index}
style={{
backgroundColor: colors[1],
height: 14,
width: data,
borderRadius: 8
}}
/>
) : (
<Skeleton
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
key={index}
colors={colors}
height={14}
width={data}
/>
)}
</>
);
});
return ( return (
<Row <Row
@ -92,5 +116,5 @@ export default function SkeletonTable({
</ScrollView> </ScrollView>
</View> </View>
</ScrollView> </ScrollView>
) );
} }