Compare commits

...
Sign in to create a new pull request.

1 commit
main ... pr/ui

Author SHA1 Message Date
Rosie Val
4e444cf55d Address review comments on channel switching 2026-08-16 12:45:24 -04:00
3 changed files with 117 additions and 118 deletions

View file

@ -499,6 +499,7 @@ export namespace DiscordModules {
extends TableRowGroupProps { extends TableRowGroupProps {
children: ReactNode children: ReactNode
onChange: (value: T) => void onChange: (value: T) => void
value?: T
defaultValue?: T defaultValue?: T
} }

View file

@ -25,7 +25,6 @@ import {
} from '@revenge-mod/plugins/_' } from '@revenge-mod/plugins/_'
import { import {
listRepoPlugins, listRepoPlugins,
listRepos,
refreshRepo, refreshRepo,
} from '@revenge-mod/plugins/_/repositories' } from '@revenge-mod/plugins/_/repositories'
import { PluginStatus } from '@revenge-mod/plugins/constants' import { PluginStatus } from '@revenge-mod/plugins/constants'
@ -39,7 +38,11 @@ import {
showPluginClearDataConfirmation, showPluginClearDataConfirmation,
showPluginUninstallConfirmation, showPluginUninstallConfirmation,
} from '../utils/alerts' } from '../utils/alerts'
import { messageOf, runInstallFlow, showErrorToast } from '../utils/repos' import {
messageOf,
runInstallFlowWithInternalRepos,
showErrorToast,
} from '../utils/repos'
import { InstalledPluginSwitch, PluginInfo } from './PluginCard' import { InstalledPluginSwitch, PluginInfo } from './PluginCard'
import { usePluginEnabled, usePluginStatus } from './PluginStateProvider' import { usePluginEnabled, usePluginStatus } from './PluginStateProvider'
import PluginTooltipsProvider, { import PluginTooltipsProvider, {
@ -47,7 +50,6 @@ import PluginTooltipsProvider, {
usePluginTooltip, usePluginTooltip,
} from './TooltipProvider' } from './TooltipProvider'
import type { AnyPlugin } from '@revenge-mod/plugins/_' import type { AnyPlugin } from '@revenge-mod/plugins/_'
import type { RepoPluginListing } from '@revenge-mod/plugins/_/repositories'
export interface PluginOptionsActionSheetProps { export interface PluginOptionsActionSheetProps {
plugin: AnyPlugin plugin: AnyPlugin
@ -131,7 +133,8 @@ function PluginOptions({ plugin, sheetKey }: PluginOptionsActionSheetProps) {
}} }}
/> />
<StatusSection plugin={plugin} /> <StatusSection plugin={plugin} />
<AdvancedSection plugin={plugin} sheetKey={sheetKey} /> <ChannelSection plugin={plugin} />
<AdvancedSection plugin={plugin} />
</Stack> </Stack>
) )
} }
@ -173,92 +176,72 @@ function StatusSection({ plugin }: { plugin: AnyPlugin }) {
function ChannelSection({ function ChannelSection({
plugin, plugin,
sheetKey,
}: { }: {
plugin: AnyPlugin plugin: AnyPlugin
sheetKey: string
}) { }) {
const meta = getInternalPluginMeta(plugin) const meta = getInternalPluginMeta(plugin)
const source = meta.source const source = meta.source
if (isPluginInternal(meta) || !source?.repo) return null if (isPluginInternal(meta) || !source?.repo) return null
const [listing, setListing] = useState<RepoPluginListing | null>(null) const [channels, setChannels] = useState<Record<string, string>>({})
const [resetKey, setResetKey] = useState(0) const [selected, setSelected] = useState(source.channel)
useEffect(() => { useEffect(() => {
let cancelled = false listRepoPlugins(source.repo)
;(async () => { .then(listings => {
try { const listing = listings.find(l => l.id === plugin.manifest.id)
const listings = await listRepoPlugins(source.repo) if (listing) setChannels(listing.channels)
const found = listings.find(l => l.id === plugin.manifest.id) })
if (found && !cancelled) setListing(found) .catch(() =>
} catch { refreshRepo(source.repo)
try { .then(() => listRepoPlugins(source.repo))
await refreshRepo(source.repo) .then(listings => {
const listings = await listRepoPlugins(source.repo) const listing = listings.find(
const found = listings.find(l => l.id === plugin.manifest.id) l => l.id === plugin.manifest.id,
if (found && !cancelled) setListing(found) )
} catch {} if (listing) setChannels(listing.channels)
} })
})() .catch(() => {}),
return () => { )
cancelled = true
}
}, [source.repo, plugin.manifest.id]) }, [source.repo, plugin.manifest.id])
if (!listing || Object.keys(listing.channels).length === 0) return null if (Object.keys(channels).length === 0) return null
const handleChange = async (selected: string) => { const handleChange = (value: string) => {
if (selected === source.channel) return setSelected(value)
const targetVersion = listing.channels[selected]
if (!targetVersion) return
const targetVersion = channels[value]!
if (targetVersion === plugin.manifest.version) { if (targetVersion === plugin.manifest.version) {
setResetKey(k => k + 1)
ToastActionCreators.open({ ToastActionCreators.open({
key: 'REVENGE_PLUGIN_CHANNEL_UP_TO_DATE', key: 'REVENGE_PLUGIN_VERSION_ALREADY_INSTALLED',
content: 'This channel is already installed', content: 'This version is already installed',
IconComponent: () => <TableRowAssetIcon name="CircleCheckIcon" />, IconComponent: () => <TableRowAssetIcon name="CircleCheckIcon" />,
}) })
return return
} }
const repos = await listRepos() runInstallFlowWithInternalRepos(
const internalUrls = repos
.filter(r => r.internal)
.map(r => r.url)
ActionSheetActionCreators.hideActionSheet(sheetKey)
runInstallFlow(
plugin.manifest.id, plugin.manifest.id,
undefined, undefined,
selected, value,
[...internalUrls, source.repo], source.repo,
) )
} }
return ( return (
<TableRadioGroup <TableRadioGroup
key={resetKey}
title="Channel" title="Channel"
defaultValue={source.channel} value={selected}
onChange={v => handleChange(v as string)} onChange={v => handleChange(v as string)}
> >
{Object.keys(listing.channels).map(c => ( {Object.keys(channels).map(c => (
<TableRadioRow key={c} label={c} value={c} /> <TableRadioRow key={c} label={c} value={c} />
))} ))}
</TableRadioGroup> </TableRadioGroup>
) )
} }
function AdvancedSection({ function AdvancedSection({ plugin }: { plugin: AnyPlugin }) {
plugin,
sheetKey,
}: {
plugin: AnyPlugin
sheetKey: string
}) {
const meta = getInternalPluginMeta(plugin) const meta = getInternalPluginMeta(plugin)
const dependents = getPluginDependents(plugin, true) const dependents = getPluginDependents(plugin, true)
const dependencies = getPluginDependencies(plugin, false) const dependencies = getPluginDependencies(plugin, false)
@ -266,8 +249,6 @@ function AdvancedSection({
const { id, name } = plugin.manifest const { id, name } = plugin.manifest
return ( return (
<>
<ChannelSection plugin={plugin} sheetKey={sheetKey} />
<TableRowGroup title="Advanced"> <TableRowGroup title="Advanced">
<IdRow id={id} /> <IdRow id={id} />
<RepositoryRow <RepositoryRow
@ -332,7 +313,6 @@ function AdvancedSection({
/> />
)} )}
</TableRowGroup> </TableRowGroup>
</>
) )
} }

View file

@ -170,3 +170,21 @@ export async function runInstallFlow(
return false return false
} }
} }
/**
* Same as {@link runInstallFlow}, but automatically includes every internal repository plus
* the target repository in `filteredRepos`, so repo-installed plugins can be updated in place.
*/
export async function runInstallFlowWithInternalRepos(
id: string,
version?: string,
channel?: string,
repo?: string,
): Promise<boolean> {
const repos = await listRepos()
const filteredRepos = repos
.filter(r => r.internal)
.map(r => r.url)
if (repo) filteredRepos.push(repo)
return runInstallFlow(id, version, channel, filteredRepos)
}