-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #96 from mykhailodanilenko/feature/sepia-instances…
…-view Select custom instances from Sepia
- Loading branch information
Showing
16 changed files
with
376 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import axios, { AxiosInstance } from "axios"; | ||
import { PeertubeInstance } from "./models"; | ||
|
||
export class InstanceSearchApi { | ||
private instance!: AxiosInstance; | ||
constructor() { | ||
this.createAxiosInstance(); | ||
} | ||
|
||
/** | ||
* Create the Axios instance with request/response interceptors | ||
*/ | ||
private createAxiosInstance(): void { | ||
this.instance = axios.create({ | ||
headers: { | ||
"Access-Control-Allow-Origin": "*", | ||
"Content-Type": "application/json", | ||
}, | ||
}); | ||
} | ||
|
||
// Common query parameters for fetching videos that are classified as "local", "non-live", and "Safe-For-Work" | ||
private readonly commonQueryParams = { | ||
start: 0, | ||
count: 1000, | ||
sort: "createdAt", | ||
}; | ||
|
||
/** | ||
* Get up to 1000 instances | ||
*/ | ||
async searchInstances(): Promise<{ data: Array<PeertubeInstance> }> { | ||
try { | ||
const response = await this.instance.get("instances", { | ||
params: this.commonQueryParams, | ||
baseURL: "https://instances.joinpeertube.org/api/v1", | ||
}); | ||
return response.data; | ||
} catch (error: unknown) { | ||
throw new Error(`Failed to fetch instances: ${(error as Error).message}`); | ||
} | ||
} | ||
} | ||
|
||
export const InstanceSearchServiceImpl = new InstanceSearchApi(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Subset of a video object from the PeerTube backend API, https://github.com/Chocobozzz/PeerTube/blob/develop/server/core/models/video/video.ts#L460 | ||
import { VideoModel } from "@peertube/peertube-types/server/core/models/video/video"; | ||
|
||
export type GetVideosVideo = Pick<VideoModel, "uuid" | "name" | "description" | "duration"> & { | ||
thumbnailPath: string; | ||
category: { id: number | null; label: string }; | ||
}; | ||
|
||
export type PeertubeInstance = { | ||
id: number; | ||
host: string; | ||
name: string; | ||
shortDescription: string; | ||
version: string; | ||
signupAllowed: boolean; | ||
signupRequiresApproval: boolean; | ||
userVideoQuota: number; | ||
liveEnabled: boolean; | ||
categories: number[]; | ||
languages: string[]; | ||
autoBlacklistUserVideosEnabled: boolean; | ||
defaultNSFWPolicy: "do_not_list" | "display" | "blur"; | ||
isNSFW: boolean; | ||
avatars: Array<{ width: number; url: string }>; | ||
banners: Array<{ width: number; url: string }>; | ||
totalUsers: number; | ||
totalVideos: number; | ||
totalLocalVideos: number; | ||
totalInstanceFollowers: number; | ||
totalInstanceFollowing: number; | ||
supportsIPv6: boolean; | ||
country: string; | ||
health: number; | ||
createdAt: number; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { DeviceCapabilities } from "./DeviceCapabilities"; | ||
import { StyleSheet, Switch, View } from "react-native"; | ||
import { Typography } from "./Typography"; | ||
import { useAppConfigContext, useColorSchemeContext } from "../contexts"; | ||
|
||
export const AppConfig = () => { | ||
const { isDebugMode, setIsDebugMode } = useAppConfigContext(); | ||
const { scheme, toggleScheme } = useColorSchemeContext(); | ||
|
||
return ( | ||
<View style={styles.deviceInfoAndToggles}> | ||
<DeviceCapabilities /> | ||
<View style={styles.togglesContainer}> | ||
<View style={styles.option}> | ||
<Typography>Debug logging</Typography> | ||
<Switch value={isDebugMode} onValueChange={setIsDebugMode} /> | ||
</View> | ||
<View style={styles.option}> | ||
<Typography>Toggle Theme</Typography> | ||
<Switch value={scheme === "light"} onValueChange={toggleScheme} /> | ||
</View> | ||
</View> | ||
</View> | ||
); | ||
}; | ||
|
||
const styles = StyleSheet.create({ | ||
deviceInfoAndToggles: { flexDirection: "row", flexWrap: "wrap", gap: 16, width: "100%" }, | ||
option: { | ||
alignItems: "center", | ||
flexDirection: "row", | ||
gap: 5, | ||
}, | ||
togglesContainer: { flex: 1, minWidth: 200 }, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { FlatList, Pressable, StyleSheet, TextInput, View } from "react-native"; | ||
import { Typography } from "./Typography"; | ||
import { useCallback, useMemo, useState } from "react"; | ||
import { useTheme } from "@react-navigation/native"; | ||
|
||
interface DropDownItem { | ||
label: string; | ||
value: string; | ||
} | ||
|
||
interface ComboBoxInputProps { | ||
value?: string; | ||
onChange: (value: string) => void; | ||
data?: Array<DropDownItem>; | ||
testID: string; | ||
} | ||
|
||
const LIST_ITEM_HEIGHT = 50; | ||
|
||
const DropdownItem = ({ | ||
onSelect, | ||
item, | ||
value, | ||
}: { | ||
onSelect: (item: DropDownItem) => () => void; | ||
item: DropDownItem; | ||
value: string; | ||
}) => { | ||
const [isHovered, setIsHovered] = useState(false); | ||
const { colors } = useTheme(); | ||
|
||
return ( | ||
<Pressable | ||
onHoverIn={() => setIsHovered(true)} | ||
onHoverOut={() => setIsHovered(false)} | ||
style={{ | ||
justifyContent: "center", | ||
height: LIST_ITEM_HEIGHT, | ||
backgroundColor: colors[isHovered ? "card" : "background"], | ||
}} | ||
onPress={onSelect(item)} | ||
> | ||
<Typography color={item.value === value ? colors.primary : undefined}>{item.label}</Typography> | ||
</Pressable> | ||
); | ||
}; | ||
|
||
export const ComboBoxInput = ({ value = "", onChange, data = [], testID }: ComboBoxInputProps) => { | ||
const { colors } = useTheme(); | ||
const [inputValue, setInputValue] = useState(""); | ||
const [isDropDownVisible, setIsDropDownVisible] = useState(false); | ||
|
||
const onSelect = (item: { label: string; value: string }) => () => { | ||
onChange(item.value); | ||
setInputValue(""); | ||
setIsDropDownVisible(false); | ||
}; | ||
|
||
const filteredList = useMemo(() => { | ||
if (!inputValue) { | ||
return data; | ||
} | ||
|
||
return data.filter(({ label }) => label.toLowerCase().includes(inputValue.toLowerCase())); | ||
}, [data, inputValue]); | ||
|
||
const initialScrollIndex = useMemo(() => { | ||
if (value) { | ||
const scrollTo = filteredList?.findIndex(({ value: itemValue }) => itemValue === value) || 0; | ||
|
||
return scrollTo > 0 ? scrollTo : 0; | ||
} | ||
|
||
return 0; | ||
}, [filteredList, value]); | ||
|
||
const renderItem = useCallback( | ||
({ item }: { item: DropDownItem }) => <DropdownItem item={item} onSelect={onSelect} value={value} />, | ||
[value, onSelect], | ||
); | ||
|
||
return ( | ||
<View testID={testID} accessible={false}> | ||
<TextInput | ||
placeholder="Search instances..." | ||
placeholderTextColor={colors.text} | ||
style={[{ color: colors.primary, backgroundColor: colors.card, borderColor: colors.primary }, styles.input]} | ||
onFocus={() => setIsDropDownVisible(true)} | ||
onBlur={() => { | ||
setTimeout(() => { | ||
setIsDropDownVisible(false); | ||
}, 300); | ||
}} | ||
value={inputValue} | ||
onChangeText={setInputValue} | ||
/> | ||
{isDropDownVisible && ( | ||
<View style={[{ borderColor: colors.border }, styles.optionsContainer]}> | ||
<FlatList | ||
data={filteredList} | ||
renderItem={renderItem} | ||
extraData={filteredList?.length} | ||
initialScrollIndex={initialScrollIndex} | ||
keyExtractor={({ value }) => value} | ||
getItemLayout={(_, index) => ({ | ||
length: LIST_ITEM_HEIGHT, | ||
offset: LIST_ITEM_HEIGHT * index, | ||
index, | ||
})} | ||
maxToRenderPerBatch={50} | ||
/> | ||
</View> | ||
)} | ||
</View> | ||
); | ||
}; | ||
|
||
const styles = StyleSheet.create({ | ||
input: { | ||
borderRadius: 8, | ||
borderWidth: 1, | ||
height: 30, | ||
width: 300, | ||
}, | ||
optionsContainer: { | ||
borderRadius: 8, | ||
borderWidth: 1, | ||
flex: 1, | ||
height: 400, | ||
padding: 8, | ||
position: "absolute", | ||
top: 30, | ||
width: 500, | ||
zIndex: 1, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { render, screen } from "@testing-library/react-native"; | ||
import { SourceSelect } from "./SourceSelect"; | ||
|
||
jest.mock("../api", () => ({ | ||
...jest.requireActual("../api"), | ||
useGetInstancesQuery: () => ({ | ||
data: [ | ||
{ id: "withLocalVids", totalLocalVideos: 100 }, | ||
{ id: "withoutLocalVids", totalLocalVideos: 0 }, | ||
], | ||
}), | ||
})); | ||
|
||
jest.mock("./ComboBoxInput", () => ({ | ||
ComboBoxInput: "ComboBoxInput", | ||
})); | ||
|
||
describe("SourceSelect", () => { | ||
it("should filter out 0-video instances", () => { | ||
render(<SourceSelect />); | ||
expect(screen.getByTestId("custom-instance-select").props.data.length).toBe(1); | ||
}); | ||
}); |
Oops, something went wrong.