From f7d769ef1874cfa2278966cf487db48f11e46f52 Mon Sep 17 00:00:00 2001 From: Rosie Val Date: Sun, 9 Aug 2026 03:29:45 -0400 Subject: [PATCH] UI/UX improvements --- .../BrowseFilterAndSortActionSheet.tsx | 21 +++++++++++ .../components/PluginListActionSheet.tsx | 33 +++++++++++++++++ .../components/PluginOptionsActionSheet.tsx | 28 ++++++++++++-- .../RepoRemoveConfirmationAlert.tsx | 37 +++++++++++++++++++ src/plugins/start/settings.plugins/index.ts | 1 - .../RevengePluginsAdvancedSettingScreen.tsx | 16 ++++++-- .../RevengePluginsBrowseSettingScreen.tsx | 24 ++++++++---- .../start/settings.plugins/utils/alerts.tsx | 20 +++++++++- 8 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 src/plugins/start/settings.plugins/components/PluginListActionSheet.tsx create mode 100644 src/plugins/start/settings.plugins/components/RepoRemoveConfirmationAlert.tsx diff --git a/src/plugins/start/settings.plugins/components/BrowseFilterAndSortActionSheet.tsx b/src/plugins/start/settings.plugins/components/BrowseFilterAndSortActionSheet.tsx index 7ceed9f..208f9c6 100644 --- a/src/plugins/start/settings.plugins/components/BrowseFilterAndSortActionSheet.tsx +++ b/src/plugins/start/settings.plugins/components/BrowseFilterAndSortActionSheet.tsx @@ -32,6 +32,11 @@ export interface BrowseFilterAndSortActionSheetProps { /** Checked repository URLs. */ checked: string[] setChecked: (urls: string[]) => void + /** Available release channels across all listings. */ + channels: string[] + /** Selected release channel. Empty string means default. */ + channel: string + setChannel: (channel: string) => void sort: BrowseSortKey setSort: (sort: BrowseSortKey) => void reverse: boolean @@ -42,6 +47,9 @@ export default function BrowseFilterAndSortActionSheet({ repos, checked, setChecked, + channels, + channel, + setChannel, sort, setSort, reverse, @@ -76,6 +84,19 @@ export default function BrowseFilterAndSortActionSheet({ ) })} + setChannel(v as string)} + > + + {channels.map(c => ( + + ))} + + + + {plugins.map(plugin => ( + } + label={plugin.manifest.name} + subLabel={plugin.manifest.id} + /> + ))} + + + + ) +} diff --git a/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx b/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx index 87998c1..0459dcb 100644 --- a/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx +++ b/src/plugins/start/settings.plugins/components/PluginOptionsActionSheet.tsx @@ -117,7 +117,7 @@ function StatusSection({ plugin }: { plugin: AnyPlugin }) { } - label="Status (TODO)" + label="Status" subLabel={bitFieldToString(PluginStatus, status)} /> {errors.length > 0 && ( @@ -175,15 +175,37 @@ function AdvancedSection({ plugin }: { plugin: AnyPlugin }) { {dependencies.length > 0 && ( } - label="Dependencies (TODO)" + label="Dependencies" subLabel={`${name} depends on ${dependencies.length} other plugins`} + onPress={() => { + ActionSheetActionCreators.openLazy( + import('./PluginListActionSheet'), + `plugin-deps-${id}`, + { + title: `Dependencies of ${name}`, + plugins: dependencies, + sheetKey: `plugin-deps-${id}`, + }, + ) + }} /> )} {dependents.length > 0 && ( } - label="Dependents (TODO)" + label="Dependents" subLabel={`${dependents.length} other plugins depend on ${name}`} + onPress={() => { + ActionSheetActionCreators.openLazy( + import('./PluginListActionSheet'), + `plugin-dependents-${id}`, + { + title: `Dependents of ${name}`, + plugins: dependents, + sheetKey: `plugin-dependents-${id}`, + }, + ) + }} /> )} diff --git a/src/plugins/start/settings.plugins/components/RepoRemoveConfirmationAlert.tsx b/src/plugins/start/settings.plugins/components/RepoRemoveConfirmationAlert.tsx new file mode 100644 index 0000000..d9c8aa0 --- /dev/null +++ b/src/plugins/start/settings.plugins/components/RepoRemoveConfirmationAlert.tsx @@ -0,0 +1,37 @@ +import { Design } from '@revenge-mod/discord/design' +import type { Repo } from '@revenge-mod/plugins/_/repositories' + +const { AlertModal, AlertActionButton, Text } = Design + +export default function RepoRemoveConfirmationAlert({ + repo, + action, +}: { + repo: Repo + action: () => Promise | void +}) { + return ( + + + {repo.name ?? repo.url} + {' '} + will be removed. You can restore the default repositories by + clearing Plugin Settings data. + + } + actions={ + <> + + + + } + /> + ) +} diff --git a/src/plugins/start/settings.plugins/index.ts b/src/plugins/start/settings.plugins/index.ts index c69d730..bf33bfc 100644 --- a/src/plugins/start/settings.plugins/index.ts +++ b/src/plugins/start/settings.plugins/index.ts @@ -96,7 +96,6 @@ function autoUpdateService(settings: Storage) { }, AUTO_UPDATE_CHECK_DELAY) } -// TODO: Add UI for this? function defaultRepoRestoreService( settings: Storage, storage: JsonStorage, diff --git a/src/plugins/start/settings.plugins/screens/RevengePluginsAdvancedSettingScreen.tsx b/src/plugins/start/settings.plugins/screens/RevengePluginsAdvancedSettingScreen.tsx index b585676..e8fe8f7 100644 --- a/src/plugins/start/settings.plugins/screens/RevengePluginsAdvancedSettingScreen.tsx +++ b/src/plugins/start/settings.plugins/screens/RevengePluginsAdvancedSettingScreen.tsx @@ -20,8 +20,9 @@ import { lookupGeneratedIconComponent } from '@revenge-mod/utils/discord' import { useCallback, useEffect, useState } from 'react' import { ScrollView, View } from 'react-native' import { api } from '..' -import { toConfig } from '../repos' +import { addDefaultRepoIfNeeded, toConfig } from '../repos' import { formatBytes, messageOf, showErrorToast } from '../utils/repos' +import { showRemoveRepoConfirmation } from '../utils/alerts' import type { DownloadProgressEvent, Repo, @@ -101,7 +102,7 @@ function UserRepoRow({ label: 'Delete', IconComponent: TrashIconComponent, variant: 'destructive' as const, - action: () => onRemove(repo), + action: () => showRemoveRepoConfirmation(repo, () => onRemove(repo)), }, ], ] @@ -212,7 +213,6 @@ export default function RevengePluginsAdvancedSettingScreen() { const removeRepo = useCallback( async (repo: Repo) => { - // TODO: Add warning for default repo removal, user can clear "Plugin Settings" data to restore the default repo await commit(toConfig(userRepos.filter(r => r.url !== repo.url))) @@ -370,6 +370,16 @@ export default function RevengePluginsAdvancedSettingScreen() { ) } /> + } + label="Restore default repositories" + subLabel="Add the bundled default repository if it's missing" + onPress={() => { + addDefaultRepoIfNeeded().catch(e => + showErrorToast(messageOf(e)), + ) + }} + /> diff --git a/src/plugins/start/settings.plugins/screens/RevengePluginsBrowseSettingScreen.tsx b/src/plugins/start/settings.plugins/screens/RevengePluginsBrowseSettingScreen.tsx index 6018f38..c6096a0 100644 --- a/src/plugins/start/settings.plugins/screens/RevengePluginsBrowseSettingScreen.tsx +++ b/src/plugins/start/settings.plugins/screens/RevengePluginsBrowseSettingScreen.tsx @@ -49,9 +49,9 @@ export default function RevengePluginsBrowseSettingScreen() { ) } -// TODO: Let the user pick a release channel -/** The channel a listing displays: `latest`, else its first channel. */ -function displayChannelOf(listing: RepoPluginListing): string | undefined { +/** The channel a listing displays: preferred, `latest`, else its first channel. */ +function displayChannelOf(listing: RepoPluginListing, preferredChannel: string): string | undefined { + if (preferredChannel && listing.channels[preferredChannel]) return preferredChannel if (listing.channels.latest) return 'latest' return Object.keys(listing.channels)[0] } @@ -82,15 +82,18 @@ function Screen() { const [excluded, setExcluded] = useState([]) const [sort, setSort] = useState('name') const [reverse, setReverse] = useState(false) + const [channel, setChannel] = useState('') + const [channels, setChannels] = useState([]) const hasFilter = useMemo( - () => excluded.length > 0 || sort !== 'name' || reverse, - [excluded, sort, reverse], + () => excluded.length > 0 || sort !== 'name' || reverse || channel !== '', + [excluded, sort, reverse, channel], ) const load = useCallback(async () => { const repos = await listRepos() const all: BrowseEntry[] = [] + const allChannels = new Set() setInternalRepos( repos.filter(repo => repo.internal).map(repo => repo.url), @@ -108,7 +111,9 @@ function Screen() { for (const listing of listings) { const plugin = pList.get(listing.id) - const displayChannel = displayChannelOf(listing) + for (const c of Object.keys(listing.channels)) + allChannels.add(c) + const displayChannel = displayChannelOf(listing, channel) const displayVersion = displayChannel ? (listing.channels[displayChannel] ?? '') : '' @@ -119,7 +124,6 @@ function Screen() { repoName: repo.name ?? null, repositoryText, version: displayVersion, - // TODO: Let the user pick a release channel channel: displayChannel, size: listing.versions[displayVersion] @@ -139,8 +143,9 @@ function Screen() { ), ) + setChannels([...allChannels].sort()) setEntries(all) - }, []) + }, [channel, setChannels]) useEffect(() => { // Show cached indexes right away, then refresh everything @@ -234,6 +239,9 @@ function Screen() { .map(repo => repo.url) .filter(url => !urls.includes(url)), ), + channels, + channel, + setChannel, sort, setSort, reverse, diff --git a/src/plugins/start/settings.plugins/utils/alerts.tsx b/src/plugins/start/settings.plugins/utils/alerts.tsx index 75e05eb..7e07c66 100644 --- a/src/plugins/start/settings.plugins/utils/alerts.tsx +++ b/src/plugins/start/settings.plugins/utils/alerts.tsx @@ -11,8 +11,10 @@ import PluginClearDataConfirmationAlert from '../components/PluginClearDataConfi import PluginHasDependenciesAlert from '../components/PluginHasDependenciesAlert' import PluginHasDependentsAlert from '../components/PluginHasDependentsAlert' import PluginMissingDependenciesAlert from '../components/PluginMissingDependenciesAlert' -import PluginStatesProvider from '../components/PluginStateProvider' +import PluginStatesProvider from '../components/PluginStatesProvider' import PluginUninstallConfirmationAlert from '../components/PluginUninstallConfirmationAlert' +import RepoRemoveConfirmationAlert from '../components/RepoRemoveConfirmationAlert' +import type { Repo } from '@revenge-mod/plugins/_/repositories' import type { AnyPlugin } from '@revenge-mod/plugins/_' export function showPluginClearDataConfirmation( @@ -100,6 +102,22 @@ export function showPluginMissingDependenciesAlert( ) } +export function showRemoveRepoConfirmation( + repo: Repo, + callback: () => Promise | void, +) { + const KEY = 'repo-remove-confirmation' + + async function action() { + await callback() + } + + AlertActionCreators.openAlert( + KEY, + , + ) +} + export function showPluginHasDependentsAlert( plugin: AnyPlugin, dependents: AnyPlugin[],