mobile: bug fix and DRYer code

This commit is contained in:
daniel31x13
2025-11-16 15:49:12 -05:00
parent ece88eed5c
commit d7b6ce04e8
5 changed files with 99 additions and 297 deletions

View File

@@ -1,31 +1,12 @@
import { useLinks } from "@linkwarden/router/links";
import {
View,
StyleSheet,
FlatList,
Platform,
Text,
ActivityIndicator,
ViewToken,
} from "react-native";
import { View, StyleSheet, Platform } from "react-native";
import useAuthStore from "@/store/auth";
import LinkListing from "@/components/LinkListing";
import { useLocalSearchParams, useNavigation } from "expo-router";
import React, { useEffect } from "react";
import { LinkIncludingShortenedCollectionAndTags } from "@linkwarden/types";
import Spinner from "@/components/ui/Spinner";
import { rawTheme, ThemeName } from "@/lib/colors";
import { useColorScheme } from "nativewind";
import { useCollections } from "@linkwarden/router/collections";
const RenderItem = React.memo(
({ item }: { item: LinkIncludingShortenedCollectionAndTags }) => {
return <LinkListing link={item} />;
}
);
import Links from "@/components/Links";
export default function LinksScreen() {
const { colorScheme } = useColorScheme();
const { auth } = useAuthStore();
const { search, id } = useLocalSearchParams<{
search?: string;
@@ -66,60 +47,7 @@ export default function LinksScreen() {
collapsable={false}
collapsableChildren={false}
>
{data.isLoading ? (
<View className="flex justify-center h-full items-center">
<ActivityIndicator size="large" />
<Text className="text-base mt-2.5 text-neutral">Loading...</Text>
</View>
) : (
<FlatList
contentInsetAdjustmentBehavior="automatic"
ListHeaderComponent={() => <></>}
data={links || []}
refreshControl={
<Spinner
refreshing={data.isRefetching}
onRefresh={() => data.refetch()}
progressBackgroundColor={
rawTheme[colorScheme as ThemeName]["base-200"]
}
colors={[rawTheme[colorScheme as ThemeName]["base-content"]]}
/>
}
refreshing={data.isRefetching}
initialNumToRender={4}
keyExtractor={(item) => item.id?.toString() || ""}
renderItem={({ item }) => (
<RenderItem item={item} key={item.id?.toString()} />
)}
onEndReached={() => data.fetchNextPage()}
onEndReachedThreshold={0.5}
ItemSeparatorComponent={() => (
<View className="bg-neutral-content h-px" />
)}
ListEmptyComponent={
<View className="flex justify-center py-10 items-center">
<Text className="text-center text-xl text-neutral">
Nothing found...
</Text>
</View>
}
onViewableItemsChanged={({
viewableItems,
}: {
viewableItems: ViewToken[];
}) => {
const links = viewableItems.map(
(e) => e.item
) as LinkIncludingShortenedCollectionAndTags[];
if (links.some((e) => e.id && !e.preview)) {
data.refetch();
}
}}
viewabilityConfig={{ itemVisiblePercentThreshold: 50 }}
/>
)}
<Links links={links} data={data} />
</View>
);
}

View File

@@ -1,28 +1,10 @@
import { useLinks } from "@linkwarden/router/links";
import {
View,
StyleSheet,
FlatList,
Platform,
ActivityIndicator,
Text,
ViewToken,
} from "react-native";
import { View, StyleSheet, Platform } from "react-native";
import useAuthStore from "@/store/auth";
import LinkListing from "@/components/LinkListing";
import { useLocalSearchParams, useNavigation } from "expo-router";
import React, { useEffect, useMemo } from "react";
import { LinkIncludingShortenedCollectionAndTags } from "@linkwarden/types";
import { useCollections } from "@linkwarden/router/collections";
import Spinner from "@/components/ui/Spinner";
import { rawTheme, ThemeName } from "@/lib/colors";
import { useColorScheme } from "nativewind";
const RenderItem = React.memo(
({ item }: { item: LinkIncludingShortenedCollectionAndTags }) => {
return <LinkListing link={item} />;
}
);
import Links from "@/components/Links";
export default function LinksScreen() {
const { auth } = useAuthStore();
@@ -31,7 +13,6 @@ export default function LinksScreen() {
section?: "pinned-links" | "recent-links" | "collection";
collectionId?: string;
}>();
const { colorScheme } = useColorScheme();
const navigation = useNavigation();
const collections = useCollections(auth);
@@ -76,57 +57,7 @@ export default function LinksScreen() {
collapsable={false}
collapsableChildren={false}
>
{data.isLoading ? (
<View className="flex justify-center h-full items-center">
<ActivityIndicator size="large" />
<Text className="text-base mt-2.5 text-neutral">Loading...</Text>
</View>
) : (
<FlatList
contentInsetAdjustmentBehavior="automatic"
ListHeaderComponent={() => <></>}
data={links || []}
refreshControl={
<Spinner
refreshing={data.isRefetching}
onRefresh={() => data.refetch()}
progressBackgroundColor={
rawTheme[colorScheme as ThemeName]["base-200"]
}
colors={[rawTheme[colorScheme as ThemeName]["base-content"]]}
/>
}
initialNumToRender={4}
keyExtractor={(item) => item.id?.toString() || ""}
renderItem={({ item }) => (
<RenderItem item={item} key={item.id?.toString()} />
)}
onEndReached={() => data.fetchNextPage()}
onEndReachedThreshold={0.5}
ItemSeparatorComponent={() => <View className="bg-base-200 h-px" />}
ListEmptyComponent={
<View className="flex justify-center py-10 items-center">
<Text className="text-center text-xl text-neutral">
Nothing found...
</Text>
</View>
}
onViewableItemsChanged={({
viewableItems,
}: {
viewableItems: ViewToken[];
}) => {
const links = viewableItems.map(
(e) => e.item
) as LinkIncludingShortenedCollectionAndTags[];
if (links.some((e) => e.id && !e.preview)) {
data.refetch();
}
}}
viewabilityConfig={{ itemVisiblePercentThreshold: 50 }}
/>
)}
<Links links={links} data={data} />
</View>
);
}

View File

@@ -1,30 +1,11 @@
import { useLinks } from "@linkwarden/router/links";
import {
View,
StyleSheet,
FlatList,
Platform,
Text,
ActivityIndicator,
ViewToken,
} from "react-native";
import { View, StyleSheet, Platform } from "react-native";
import useAuthStore from "@/store/auth";
import LinkListing from "@/components/LinkListing";
import { useLocalSearchParams } from "expo-router";
import React from "react";
import { LinkIncludingShortenedCollectionAndTags } from "@linkwarden/types";
import Spinner from "@/components/ui/Spinner";
import { rawTheme, ThemeName } from "@/lib/colors";
import { useColorScheme } from "nativewind";
const RenderItem = React.memo(
({ item }: { item: LinkIncludingShortenedCollectionAndTags }) => {
return <LinkListing link={item} />;
}
);
import Links from "@/components/Links";
export default function LinksScreen() {
const { colorScheme } = useColorScheme();
const { auth } = useAuthStore();
const { search } = useLocalSearchParams<{ search?: string }>();
@@ -43,60 +24,7 @@ export default function LinksScreen() {
collapsable={false}
collapsableChildren={false}
>
{data.isLoading ? (
<View className="flex justify-center h-full items-center">
<ActivityIndicator size="large" />
<Text className="text-base mt-2.5 text-neutral">Loading...</Text>
</View>
) : (
<FlatList
contentInsetAdjustmentBehavior="automatic"
ListHeaderComponent={() => <></>}
data={links || []}
refreshControl={
<Spinner
refreshing={data.isRefetching}
onRefresh={() => data.refetch()}
progressBackgroundColor={
rawTheme[colorScheme as ThemeName]["base-200"]
}
colors={[rawTheme[colorScheme as ThemeName]["base-content"]]}
/>
}
refreshing={data.isRefetching}
initialNumToRender={4}
keyExtractor={(item) => item.id?.toString() || ""}
renderItem={({ item }) => (
<RenderItem item={item} key={item.id?.toString()} />
)}
onEndReached={() => data.fetchNextPage()}
onEndReachedThreshold={0.5}
ItemSeparatorComponent={() => (
<View className="bg-neutral-content h-px" />
)}
ListEmptyComponent={
<View className="flex justify-center py-10 items-center">
<Text className="text-center text-xl text-neutral">
Nothing found...
</Text>
</View>
}
onViewableItemsChanged={({
viewableItems,
}: {
viewableItems: ViewToken[];
}) => {
const links = viewableItems.map(
(e) => e.item
) as LinkIncludingShortenedCollectionAndTags[];
if (links.some((e) => e.id && !e.preview)) {
data.refetch();
}
}}
viewabilityConfig={{ itemVisiblePercentThreshold: 50 }}
/>
)}
<Links links={links} data={data} />
</View>
);
}

View File

@@ -1,31 +1,12 @@
import { useLinks } from "@linkwarden/router/links";
import {
View,
StyleSheet,
FlatList,
Platform,
Text,
ActivityIndicator,
ViewToken,
} from "react-native";
import { View, StyleSheet, Platform } from "react-native";
import useAuthStore from "@/store/auth";
import LinkListing from "@/components/LinkListing";
import { useLocalSearchParams, useNavigation } from "expo-router";
import React, { useEffect } from "react";
import { LinkIncludingShortenedCollectionAndTags } from "@linkwarden/types";
import Spinner from "@/components/ui/Spinner";
import { rawTheme, ThemeName } from "@/lib/colors";
import { useColorScheme } from "nativewind";
import { useTags } from "@linkwarden/router/tags";
const RenderItem = React.memo(
({ item }: { item: LinkIncludingShortenedCollectionAndTags }) => {
return <LinkListing link={item} />;
}
);
import Links from "@/components/Links";
export default function LinksScreen() {
const { colorScheme } = useColorScheme();
const { auth } = useAuthStore();
const { search, id } = useLocalSearchParams<{
search?: string;
@@ -64,60 +45,7 @@ export default function LinksScreen() {
collapsable={false}
collapsableChildren={false}
>
{data.isLoading ? (
<View className="flex justify-center h-full items-center">
<ActivityIndicator size="large" />
<Text className="text-base mt-2.5 text-neutral">Loading...</Text>
</View>
) : (
<FlatList
contentInsetAdjustmentBehavior="automatic"
ListHeaderComponent={() => <></>}
data={links || []}
refreshControl={
<Spinner
refreshing={data.isRefetching}
onRefresh={() => data.refetch()}
progressBackgroundColor={
rawTheme[colorScheme as ThemeName]["base-200"]
}
colors={[rawTheme[colorScheme as ThemeName]["base-content"]]}
/>
}
refreshing={data.isRefetching}
initialNumToRender={4}
keyExtractor={(item) => item.id?.toString() || ""}
renderItem={({ item }) => (
<RenderItem item={item} key={item.id?.toString()} />
)}
onEndReached={() => data.fetchNextPage()}
onEndReachedThreshold={0.5}
ItemSeparatorComponent={() => (
<View className="bg-neutral-content h-px" />
)}
ListEmptyComponent={
<View className="flex justify-center py-10 items-center">
<Text className="text-center text-xl text-neutral">
Nothing found...
</Text>
</View>
}
onViewableItemsChanged={({
viewableItems,
}: {
viewableItems: ViewToken[];
}) => {
const links = viewableItems.map(
(e) => e.item
) as LinkIncludingShortenedCollectionAndTags[];
if (links.some((e) => e.id && !e.preview)) {
data.refetch();
}
}}
viewabilityConfig={{ itemVisiblePercentThreshold: 50 }}
/>
)}
<Links links={links} data={data} />
</View>
);
}

View File

@@ -0,0 +1,87 @@
import {
View,
FlatList,
Text,
ActivityIndicator,
ViewToken,
} from "react-native";
import LinkListing from "@/components/LinkListing";
import React, { useState } from "react";
import { LinkIncludingShortenedCollectionAndTags } from "@linkwarden/types";
import Spinner from "@/components/ui/Spinner";
import { rawTheme, ThemeName } from "@/lib/colors";
import { useColorScheme } from "nativewind";
const RenderItem = React.memo(
({ item }: { item: LinkIncludingShortenedCollectionAndTags }) => {
return <LinkListing link={item} />;
}
);
type Props = {
links: LinkIncludingShortenedCollectionAndTags[];
data: any;
};
export default function Links({ links, data }: Props) {
const { colorScheme } = useColorScheme();
const [promptedRefetch, setPromptedRefetch] = useState(false);
return data.isLoading ? (
<View className="flex justify-center h-full items-center">
<ActivityIndicator size="large" />
<Text className="text-base mt-2.5 text-neutral">Loading...</Text>
</View>
) : (
<FlatList
contentInsetAdjustmentBehavior="automatic"
ListHeaderComponent={() => <></>}
data={links || []}
refreshControl={
<Spinner
refreshing={data.isRefetching && promptedRefetch}
onRefresh={async () => {
setPromptedRefetch(true);
await data.refetch();
setPromptedRefetch(false);
}}
progressBackgroundColor={
rawTheme[colorScheme as ThemeName]["base-200"]
}
colors={[rawTheme[colorScheme as ThemeName]["base-content"]]}
/>
}
refreshing={data.isRefetching && promptedRefetch}
initialNumToRender={4}
keyExtractor={(item) => item.id?.toString() || ""}
renderItem={({ item }) => (
<RenderItem item={item} key={item.id?.toString()} />
)}
onEndReached={() => data.fetchNextPage()}
onEndReachedThreshold={0.5}
ItemSeparatorComponent={() => (
<View className="bg-neutral-content h-px" />
)}
ListEmptyComponent={
<View className="flex justify-center py-10 items-center">
<Text className="text-center text-xl text-neutral">
Nothing found...
</Text>
</View>
}
onViewableItemsChanged={({
viewableItems,
}: {
viewableItems: ViewToken[];
}) => {
const links = viewableItems.map(
(e) => e.item
) as LinkIncludingShortenedCollectionAndTags[];
if (!data.isRefetching && links.some((e) => e.id && !e.preview))
data.refetch();
}}
viewabilityConfig={{ itemVisiblePercentThreshold: 50 }}
/>
);
}