Skip to content

Commit

Permalink
Merge pull request #99 from mykhailodanilenko/feature/recent-backends
Browse files Browse the repository at this point in the history
Add recent instances view
  • Loading branch information
mykhailodanilenko authored Jul 11, 2024
2 parents 82b9eb5 + abff2f3 commit 6bf014a
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 11 deletions.
16 changes: 13 additions & 3 deletions OwnTube.tv/app/(home)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import { HomeScreen } from "../../screens";
import { Feather } from "@expo/vector-icons";
import { useTheme } from "@react-navigation/native";
import { StyleSheet, View } from "react-native";
import { useRecentInstances } from "../../hooks";
import { RootStackParams } from "../_layout";

export default function index() {
const router = useRouter();
const navigation = useNavigation();
const theme = useTheme();
const { backend } = useLocalSearchParams();
const { backend } = useLocalSearchParams<RootStackParams[ROUTES.INDEX]>();
const [isGettingStoredBackend, setIsGettingStoredBackend] = useState(true);
const { recentInstances, addRecentInstance } = useRecentInstances();

const getSourceAndRedirect = async () => {
if (backend) {
Expand All @@ -39,17 +42,24 @@ export default function index() {
title: `OwnTube.tv@${backend}`,
headerRight: () => (
<View style={styles.headerControls}>
<Link style={styles.headerButton} href={{ pathname: `/${ROUTES.SETTINGS}`, params: { backend } }}>
<Link
style={styles.headerButton}
href={{ pathname: `/${ROUTES.SETTINGS}`, params: { backend, tab: "history" } }}
>
<Feather name="settings" size={24} color={theme.colors.primary} />
</Link>
<DeviceCapabilitiesModal />
</View>
),
});

if (!(recentInstances?.[0] === backend)) {
addRecentInstance(backend);
}
}

getSourceAndRedirect();
}, [backend]),
}, [backend, recentInstances]),
);

if (isGettingStoredBackend) {
Expand Down
2 changes: 1 addition & 1 deletion OwnTube.tv/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const unstable_settings = {

export type RootStackParams = {
[ROUTES.INDEX]: { backend: string };
[ROUTES.SETTINGS]: { backend: string };
[ROUTES.SETTINGS]: { backend: string; tab: "history" | "instance" | "config" };
[ROUTES.VIDEO]: { backend: string; id: string; timestamp?: string };
};

Expand Down
16 changes: 13 additions & 3 deletions OwnTube.tv/components/ComboBoxInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const ComboBoxInput = ({ value = "", onChange, data = [], testID }: Combo
);

return (
<View testID={testID} accessible={false}>
<View testID={testID} accessible={false} style={styles.container}>
<TextInput
placeholder="Search instances..."
placeholderTextColor={colors.text}
Expand All @@ -95,7 +95,15 @@ export const ComboBoxInput = ({ value = "", onChange, data = [], testID }: Combo
onChangeText={setInputValue}
/>
{isDropDownVisible && (
<View style={[{ borderColor: colors.border }, styles.optionsContainer]}>
<View
style={[
{
borderColor: colors.border,
backgroundColor: colors.background,
},
styles.optionsContainer,
]}
>
<FlatList
data={filteredList}
renderItem={renderItem}
Expand All @@ -116,6 +124,7 @@ export const ComboBoxInput = ({ value = "", onChange, data = [], testID }: Combo
};

const styles = StyleSheet.create({
container: { position: "relative", zIndex: 1 },
input: {
borderRadius: 8,
borderWidth: 1,
Expand All @@ -127,10 +136,11 @@ const styles = StyleSheet.create({
borderWidth: 1,
flex: 1,
height: 400,
left: 0,
padding: 8,
position: "absolute",
top: 30,
width: 500,
zIndex: 1,
zIndex: 99,
},
});
8 changes: 8 additions & 0 deletions OwnTube.tv/components/SourceSelect.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ jest.mock("../api", () => ({
],
}),
}));
jest.mock("../hooks", () => ({
...jest.requireActual("../hooks"),
useRecentInstances: jest.fn(() => ({
recentInstances: [],
addRecentInstance: jest.fn(),
clearRecentInstances: jest.fn(),
})),
}));

jest.mock("./ComboBoxInput", () => ({
ComboBoxInput: "ComboBoxInput",
Expand Down
23 changes: 23 additions & 0 deletions OwnTube.tv/components/SourceSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ import { useGetInstancesQuery } from "../api";
import { ComboBoxInput } from "./ComboBoxInput";
import { Spacer } from "./shared/Spacer";
import { colors } from "../colors";
import { useRecentInstances } from "../hooks";
import { Ionicons } from "@expo/vector-icons";

export const SourceSelect = () => {
const { backend } = useLocalSearchParams<RootStackParams["settings"]>();
const router = useRouter();
const theme = useTheme();
const { recentInstances, addRecentInstance, clearRecentInstances } = useRecentInstances();

const handleSelectSource = (backend: string) => {
router.setParams({ backend });
writeToAsyncStorage(STORAGE.DATASOURCE, backend);
addRecentInstance(backend);
};

const renderItem = useCallback(
Expand Down Expand Up @@ -57,6 +61,23 @@ export const SourceSelect = () => {
</View>
)}
<Spacer height={16} />
{!!recentInstances?.length && (
<>
<View style={styles.recentsHeader}>
<Typography>Recent instances:</Typography>
<Ionicons.Button
name="trash"
backgroundColor={theme.colors.background}
style={{ ...styles.iconButton, borderColor: theme.colors.border }}
onPress={clearRecentInstances}
>
<Typography>Clear</Typography>
</Ionicons.Button>
</View>
{recentInstances?.map(renderItem)}
</>
)}
<Spacer height={16} />
<Typography>Predefined instances:</Typography>
{Object.values(SOURCES).map(renderItem)}
{backend && backend in SOURCES && renderItem(backend)}
Expand All @@ -82,6 +103,8 @@ const styles = StyleSheet.create({
container: {
marginTop: 8,
},
iconButton: { borderWidth: 1 },
recentsHeader: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" },
source: {
opacity: 0.5,
padding: 5,
Expand Down
1 change: 1 addition & 0 deletions OwnTube.tv/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./useCategoryScroll";
export * from "./useViewHistory";
export * from "./useDeviceCapabilities";
export * from "./useRecentInstances";
41 changes: 41 additions & 0 deletions OwnTube.tv/hooks/useRecentInstances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { readFromAsyncStorage, writeToAsyncStorage } from "../utils";
import { STORAGE } from "../types";

export const useRecentInstances = () => {
const queryClient = useQueryClient();
const { data: recentInstances } = useQuery({
queryKey: ["recentInstances"],
queryFn: async () => {
const instances: string[] | undefined = await readFromAsyncStorage(STORAGE.RECENT_INSTANCES);

return instances || [];
},
select: (data) => {
return data.slice(0, 50);
},
});

const { mutateAsync: updateRecentInstances } = useMutation({
mutationFn: async (data: string[]) => {
await writeToAsyncStorage(STORAGE.RECENT_INSTANCES, data);
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["recentInstances"],
});
},
});

const addRecentInstance = async (instance: string) => {
const currentInstances = queryClient.getQueryData<string[]>(["recentInstances"]);

await updateRecentInstances(Array.from(new Set([instance, ...(currentInstances || [])])));
};

const clearRecentInstances = async () => {
await updateRecentInstances([]);
};

return { recentInstances, addRecentInstance, clearRecentInstances };
};
16 changes: 12 additions & 4 deletions OwnTube.tv/screens/SettingsScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { SourceSelect, Typography, ViewHistory, AppConfig } from "../../componen
import { Screen } from "../../layouts";
import { styles } from "./styles";
import { useTheme } from "@react-navigation/native";
import React, { useState } from "react";
import React from "react";
import { useLocalSearchParams, useRouter } from "expo-router";
import { RootStackParams } from "../../app/_layout";
import { ROUTES } from "../../types";

type SettingsTab = "history" | "instance" | "config";

Expand All @@ -15,14 +18,19 @@ const tabsWithNames: Record<SettingsTab, string> = {

export const SettingsScreen = () => {
const { colors } = useTheme();
const [tab, setTab] = useState<SettingsTab>("history");
const { tab } = useLocalSearchParams<RootStackParams[ROUTES.SETTINGS]>();
const router = useRouter();

const tabContent: Record<SettingsTab, React.JSX.Element> = {
history: <ViewHistory />,
instance: <SourceSelect />,
config: <AppConfig />,
};

const setTab = (newTab: SettingsTab) => () => {
router.setParams({ tab: newTab });
};

return (
<Screen style={{ ...styles.container, backgroundColor: colors.background }}>
<View
Expand All @@ -34,12 +42,12 @@ export const SettingsScreen = () => {
]}
>
{Object.entries(tabsWithNames).map(([key, label]) => (
<Pressable key={key} onPress={() => setTab(key as SettingsTab)}>
<Pressable key={key} onPress={setTab(key as SettingsTab)}>
<Typography color={key === tab ? colors.primary : undefined}>{label}</Typography>
</Pressable>
))}
</View>
{tabContent[tab]}
{!!tab && tabContent[tab]}
</Screen>
);
};
1 change: 1 addition & 0 deletions OwnTube.tv/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum SOURCES {
export enum STORAGE {
DATASOURCE = "data_source",
VIEW_HISTORY = "view_history",
RECENT_INSTANCES = "recent_instances",
}

export enum ROUTES {
Expand Down

0 comments on commit 6bf014a

Please sign in to comment.