Address PR review comments

This commit is contained in:
Rosie Val 2026-08-09 14:52:34 -04:00
parent f7d769ef18
commit b6304635e7
11 changed files with 105 additions and 64 deletions

View file

@ -32,11 +32,6 @@ 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
@ -47,9 +42,6 @@ export default function BrowseFilterAndSortActionSheet({
repos,
checked,
setChecked,
channels,
channel,
setChannel,
sort,
setSort,
reverse,
@ -84,19 +76,6 @@ export default function BrowseFilterAndSortActionSheet({
)
})}
</TableRowGroup>
<TableRadioGroup
title="Release channel"
defaultValue={channel}
onChange={v => setChannel(v as string)}
>
<TableRadioRow
label="Default (latest / first available)"
value=""
/>
{channels.map(c => (
<TableRadioRow key={c} label={c} value={c} />
))}
</TableRadioGroup>
<TableRadioGroup
title="Sort by"
defaultValue={sort}

View file

@ -1,10 +1,19 @@
import { getAssetIdByName } from '@revenge-mod/assets'
import { ActionSheetActionCreators } from '@revenge-mod/discord/actions'
import { Design } from '@revenge-mod/discord/design'
import { useState } from 'react'
import { PluginInfo } from './PluginCard'
import { IdRow, RepositoryRow } from './PluginOptionsActionSheet'
import type { RepoPluginListing } from '@revenge-mod/plugins/_/repositories'
const { ActionSheet, Button, Stack, TableRowGroup } = Design
const {
ActionSheet,
Button,
Stack,
TableRowGroup,
TableRadioGroup,
TableRadioRow,
} = Design
const DownloadIcon = getAssetIdByName('DownloadIcon', 'png')!
@ -15,9 +24,11 @@ export interface BrowsePluginActionSheetProps {
version: string
icon?: string
id: string
listing: RepoPluginListing
channel: string
/** Display text for the Repository row, eg. `Name (url)`. */
repositoryText: string
onInstall: () => void
onInstall: (channel?: string, version?: string) => void
sheetKey: string
}
@ -31,17 +42,26 @@ export default function BrowsePluginActionSheet({
version,
icon,
id,
listing,
channel,
repositoryText,
onInstall,
sheetKey,
}: BrowsePluginActionSheetProps) {
const [selectedChannel, setSelectedChannel] = useState(channel)
const selectedVersion = selectedChannel
? (listing.channels[selectedChannel] ?? '')
: version
const channelNames = Object.keys(listing.channels)
return (
<ActionSheet>
<Stack spacing={24} style={{ paddingTop: 8 }}>
<PluginInfo
name={name}
author={author}
version={version}
version={selectedVersion}
description={description}
icon={icon}
actions={
@ -53,11 +73,26 @@ export default function BrowsePluginActionSheet({
ActionSheetActionCreators.hideActionSheet(
sheetKey,
)
onInstall()
onInstall(selectedChannel, selectedVersion)
}}
/>
}
/>
{channelNames.length > 0 && (
<TableRadioGroup
title="Channel"
defaultValue={selectedChannel}
onChange={v => setSelectedChannel(v as string)}
>
{channelNames.map(c => (
<TableRadioRow
key={c}
label={c}
value={c}
/>
))}
</TableRadioGroup>
)}
<TableRowGroup title="Advanced">
<IdRow id={id} />
<RepositoryRow text={repositoryText} copyable />

View file

@ -27,6 +27,7 @@ import {
useEssentialPluginTooltip,
} from './TooltipProvider'
import type { AnyPlugin, InternalPluginMeta } from '@revenge-mod/plugins/_'
import type { RepoPluginListing } from '@revenge-mod/plugins/_/repositories'
const { Card, Text, Stack, IconButton, Button, createStyles } = Design
@ -262,6 +263,8 @@ export const BrowsePluginCard = memo(function BrowsePluginCard({
author,
icon,
id,
listing,
channel,
repositoryText,
onInstall,
}: {
@ -271,8 +274,10 @@ export const BrowsePluginCard = memo(function BrowsePluginCard({
author: string
icon?: string
id: string
listing: RepoPluginListing
channel: string
repositoryText: string
onInstall: () => void
onInstall: (channel?: string, version?: string) => void
}) {
return (
<PluginCard
@ -295,6 +300,8 @@ export const BrowsePluginCard = memo(function BrowsePluginCard({
version,
icon,
id,
listing,
channel,
repositoryText,
onInstall,
})

View file

@ -121,7 +121,11 @@ export function BrowsePluginMasonryFlashList({
onInstall,
}: {
entries: BrowseEntry[]
onInstall: (entry: BrowseEntry) => void
onInstall: (
entry: BrowseEntry,
channel?: string,
version?: string,
) => void
}) {
const numColumns = useNumColumns()
const resetTooltips = useResetTooltips()
@ -152,8 +156,12 @@ export function BrowsePluginMasonryFlashList({
author={listing.author}
icon={listing.icon ?? undefined}
id={listing.id}
listing={listing}
channel={entry.channel ?? ''}
repositoryText={entry.repositoryText}
onInstall={() => onInstall(entry)}
onInstall={(channel, version) =>
onInstall(entry, channel, version)
}
/>
)
}}

View file

@ -179,12 +179,11 @@ function AdvancedSection({ plugin }: { plugin: AnyPlugin }) {
subLabel={`${name} depends on ${dependencies.length} other plugins`}
onPress={() => {
ActionSheetActionCreators.openLazy(
import('./PluginListActionSheet'),
import('./PluginRelationsListActionSheet'),
`plugin-deps-${id}`,
{
title: `Dependencies of ${name}`,
plugins: dependencies,
sheetKey: `plugin-deps-${id}`,
},
)
}}
@ -197,12 +196,11 @@ function AdvancedSection({ plugin }: { plugin: AnyPlugin }) {
subLabel={`${dependents.length} other plugins depend on ${name}`}
onPress={() => {
ActionSheetActionCreators.openLazy(
import('./PluginListActionSheet'),
import('./PluginRelationsListActionSheet'),
`plugin-dependents-${id}`,
{
title: `Dependents of ${name}`,
plugins: dependents,
sheetKey: `plugin-dependents-${id}`,
},
)
}}

View file

@ -4,16 +4,15 @@ import type { AnyPlugin } from '@revenge-mod/plugins/_'
const { ActionSheet, Stack, TableRow, TableRowGroup } = Design
export interface PluginListActionSheetProps {
export interface PluginRelationsListActionSheetProps {
title: string
plugins: AnyPlugin[]
sheetKey: string
}
export default function PluginListActionSheet({
export default function PluginRelationsListActionSheet({
title,
plugins,
}: PluginListActionSheetProps) {
}: PluginRelationsListActionSheetProps) {
return (
<ActionSheet>
<Stack spacing={24} style={{ paddingTop: 8 }}>

View file

@ -18,8 +18,7 @@ export default function RepoRemoveConfirmationAlert({
<Text variant="text-md/semibold" color="text-default">
{repo.name ?? repo.url}
</Text>{' '}
will be removed. You can restore the default repositories by
clearing Plugin Settings data.
will be removed.
</Text>
}
actions={

View file

@ -7,14 +7,16 @@ import { noop } from '@revenge-mod/utils/callback'
import { api } from '.'
import type { Repo, RepoConfigEntry } from '@revenge-mod/plugins/_/repositories'
export async function addDefaultRepoIfNeeded() {
if (!__BUILD_DEFAULT_PLUGIN_REPOSITORY_URL__) return
export async function addDefaultRepoIfNeeded(
refresh = false,
): Promise<boolean> {
if (!__BUILD_DEFAULT_PLUGIN_REPOSITORY_URL__) return false
const repos = await listRepos()
if (
repos.some(repo => repo.url === __BUILD_DEFAULT_PLUGIN_REPOSITORY_URL__)
)
return
return false
const userRepos = toConfig(repos.filter(repo => !repo.internal))
await setRepos([
@ -24,7 +26,10 @@ export async function addDefaultRepoIfNeeded() {
await api.jsonStorage.set({ defaultRepoRestored: true }).catch(noop)
await refreshRepo(__BUILD_DEFAULT_PLUGIN_REPOSITORY_URL__).catch(noop)
if (refresh)
await refreshRepo(__BUILD_DEFAULT_PLUGIN_REPOSITORY_URL__).catch(noop)
return true
}
export function toConfig(list: Repo[]): RepoConfigEntry[] {

View file

@ -3,6 +3,7 @@ import { FormSwitch } from '@revenge-mod/components'
import { styles } from '@revenge-mod/components/_'
import Page from '@revenge-mod/components/Page'
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
import { ToastActionCreators } from '@revenge-mod/discord/actions'
import { Design } from '@revenge-mod/discord/design'
import { Clipboard } from '@revenge-mod/externals/react-native-clipboard'
import { callNativeMethod } from '@revenge-mod/modules/native'
@ -373,11 +374,18 @@ export default function RevengePluginsAdvancedSettingScreen() {
<TableRow
icon={<TableRowAssetIcon name="GlobeEarthIcon" />}
label="Restore default repositories"
subLabel="Add the bundled default repository if it's missing"
onPress={() => {
addDefaultRepoIfNeeded().catch(e =>
showErrorToast(messageOf(e)),
)
onPress={async () => {
try {
const restored =
await addDefaultRepoIfNeeded(true)
if (!restored)
ToastActionCreators.open({
key: 'revenge-default-repo-nothing',
content: 'No default repository to restore',
})
} catch (e) {
showErrorToast(messageOf(e))
}
}}
/>
</TableRowGroup>

View file

@ -82,18 +82,15 @@ function Screen() {
const [excluded, setExcluded] = useState<string[]>([])
const [sort, setSort] = useState<BrowseSortKey>('name')
const [reverse, setReverse] = useState(false)
const [channel, setChannel] = useState('')
const [channels, setChannels] = useState<string[]>([])
const hasFilter = useMemo(
() => excluded.length > 0 || sort !== 'name' || reverse || channel !== '',
[excluded, sort, reverse, channel],
() => excluded.length > 0 || sort !== 'name' || reverse,
[excluded, sort, reverse],
)
const load = useCallback(async () => {
const repos = await listRepos()
const all: BrowseEntry[] = []
const allChannels = new Set<string>()
setInternalRepos(
repos.filter(repo => repo.internal).map(repo => repo.url),
@ -111,9 +108,7 @@ function Screen() {
for (const listing of listings) {
const plugin = pList.get(listing.id)
for (const c of Object.keys(listing.channels))
allChannels.add(c)
const displayChannel = displayChannelOf(listing, channel)
const displayChannel = displayChannelOf(listing, '')
const displayVersion = displayChannel
? (listing.channels[displayChannel] ?? '')
: ''
@ -143,9 +138,8 @@ function Screen() {
),
)
setChannels([...allChannels].sort())
setEntries(all)
}, [channel, setChannels])
}, [])
useEffect(() => {
// Show cached indexes right away, then refresh everything
@ -167,12 +161,24 @@ function Screen() {
}, [load])
const install = useCallback(
async (entry: BrowseEntry) => {
async (
entry: BrowseEntry,
channel?: string,
version?: string,
) => {
const targetChannel = channel || entry.channel
const targetVersion =
version ||
(targetChannel
? (entry.listing.channels[targetChannel] ?? '')
: '') ||
entry.version
// Pin the displayed version, channel, and repository, so the plan matches the card
await runInstallFlow(
entry.listing.id,
entry.version || undefined,
entry.channel,
targetVersion || undefined,
targetChannel,
// Internal repos so external plugins can link against internal plugins as well
[...internalRepos, entry.repoUrl],
)
@ -239,9 +245,6 @@ function Screen() {
.map(repo => repo.url)
.filter(url => !urls.includes(url)),
),
channels,
channel,
setChannel,
sort,
setSort,
reverse,

View file

@ -11,7 +11,7 @@ import PluginClearDataConfirmationAlert from '../components/PluginClearDataConfi
import PluginHasDependenciesAlert from '../components/PluginHasDependenciesAlert'
import PluginHasDependentsAlert from '../components/PluginHasDependentsAlert'
import PluginMissingDependenciesAlert from '../components/PluginMissingDependenciesAlert'
import PluginStatesProvider from '../components/PluginStatesProvider'
import PluginStatesProvider from '../components/PluginStateProvider'
import PluginUninstallConfirmationAlert from '../components/PluginUninstallConfirmationAlert'
import RepoRemoveConfirmationAlert from '../components/RepoRemoveConfirmationAlert'
import type { Repo } from '@revenge-mod/plugins/_/repositories'