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[],