feat(plugins/settings): add sheet for additional plugin options
This commit is contained in:
parent
2bc2a9fd08
commit
de063fe260
8 changed files with 495 additions and 191 deletions
|
|
@ -1,27 +1,15 @@
|
|||
import { useNavigation } from '@react-navigation/native'
|
||||
import { getAssetIdByName } from '@revenge-mod/assets'
|
||||
import { styles } from '@revenge-mod/components/_'
|
||||
import FormSwitch from '@revenge-mod/components/FormSwitch'
|
||||
import { AlertActionCreators } from '@revenge-mod/discord/actions'
|
||||
import { Tokens } from '@revenge-mod/discord/common'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
import {
|
||||
enablePlugin,
|
||||
getPluginDependencies,
|
||||
isPluginEnabled,
|
||||
isPluginEssential,
|
||||
runPluginLate,
|
||||
} from '@revenge-mod/plugins/_'
|
||||
import { isPluginEssential } from '@revenge-mod/plugins/_'
|
||||
import { memo } from 'react'
|
||||
import {
|
||||
Image,
|
||||
PixelRatio,
|
||||
Pressable,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import { PluginFlashList } from './PluginList'
|
||||
import PluginStatesProvider, { usePluginEnabled } from './PluginStateProvider'
|
||||
import { Image, Pressable } from 'react-native'
|
||||
import { handleDisablePlugin, handleEnablePlugin } from '../utils/actions'
|
||||
import { openPluginSettings } from '../utils/alerts'
|
||||
import { showPluginOptionsActionSheet } from '../utils/sheets'
|
||||
import { usePluginEnabled } from './PluginStateProvider'
|
||||
import {
|
||||
enableTooltipTarget,
|
||||
essentialTooltipTarget,
|
||||
|
|
@ -35,6 +23,7 @@ const { Card, Text, Stack, IconButton, createStyles } = Design
|
|||
|
||||
const PuzzlePieceIcon = getAssetIdByName('PuzzlePieceIcon', 'png')!
|
||||
const SettingsIcon = getAssetIdByName('SettingsIcon', 'png')!
|
||||
const MoreVerticalIcon = getAssetIdByName('MoreVerticalIcon', 'png')!
|
||||
|
||||
export const PLUGIN_CARD_ESTIMATED_SIZE = 116
|
||||
|
||||
|
|
@ -66,6 +55,37 @@ export const PluginCard = memo(function PluginCard({
|
|||
rowEnd && styles_.rowEnd,
|
||||
]}
|
||||
>
|
||||
<PluginInfo
|
||||
name={name}
|
||||
description={description}
|
||||
author={author}
|
||||
icon={icon}
|
||||
actions={actions}
|
||||
aligned
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
})
|
||||
|
||||
export const PluginInfo = memo(function PluginInfo({
|
||||
name,
|
||||
description,
|
||||
author,
|
||||
icon,
|
||||
actions,
|
||||
aligned,
|
||||
}: {
|
||||
name: string
|
||||
description: string
|
||||
author: string
|
||||
icon?: string
|
||||
actions?: React.ReactNode
|
||||
aligned?: boolean
|
||||
}) {
|
||||
const styles_ = usePluginCardStyles()
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Stack
|
||||
direction="horizontal"
|
||||
style={[styles.grow, styles_.topContainer]}
|
||||
|
|
@ -89,7 +109,10 @@ export const PluginCard = memo(function PluginCard({
|
|||
</Stack>
|
||||
{actions}
|
||||
</Stack>
|
||||
<Stack spacing={4} style={[styles_.alignedContainer, styles.grow]}>
|
||||
<Stack
|
||||
spacing={4}
|
||||
style={[aligned && styles_.alignedContainer, styles.grow]}
|
||||
>
|
||||
<Text
|
||||
color="text-muted"
|
||||
style={styles.grow}
|
||||
|
|
@ -101,7 +124,7 @@ export const PluginCard = memo(function PluginCard({
|
|||
{description}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stack>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -116,16 +139,13 @@ export const InstalledPluginCard = memo(function InstalledPluginCard({
|
|||
rowEnd?: boolean
|
||||
columnEnd?: boolean
|
||||
}) {
|
||||
const navigation = useNavigation()
|
||||
const enabled = usePluginEnabled(plugin)
|
||||
|
||||
const {
|
||||
manifest: { name, description, author, icon },
|
||||
} = plugin
|
||||
|
||||
const dependencies = getPluginDependencies(plugin)
|
||||
const essential = isPluginEssential(meta)
|
||||
const { dependents } = meta
|
||||
|
||||
const settingsRef = useClickOutsideTooltip(enableTooltipTarget, () => {
|
||||
setEnablePluginTooltipVisible?.(false)
|
||||
|
|
@ -145,6 +165,14 @@ export const InstalledPluginCard = memo(function InstalledPluginCard({
|
|||
columnEnd={columnEnd}
|
||||
actions={
|
||||
<>
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
icon={MoreVerticalIcon}
|
||||
onPress={() => {
|
||||
showPluginOptionsActionSheet(plugin)
|
||||
}}
|
||||
/>
|
||||
{plugin.SettingsComponent && (
|
||||
<Pressable
|
||||
onPress={e => {
|
||||
|
|
@ -165,7 +193,7 @@ export const InstalledPluginCard = memo(function InstalledPluginCard({
|
|||
variant="secondary"
|
||||
icon={SettingsIcon}
|
||||
onPress={() => {
|
||||
navigation.navigate(plugin.manifest.id)
|
||||
openPluginSettings(plugin)
|
||||
}}
|
||||
disabled={!enabled}
|
||||
/>
|
||||
|
|
@ -186,20 +214,12 @@ export const InstalledPluginCard = memo(function InstalledPluginCard({
|
|||
ref={switchRef}
|
||||
>
|
||||
<FormSwitch
|
||||
// Only FormSwitch listens for props changes, so we use a unique key just for this one.
|
||||
// Only FormSwitch listens for props changes, so we use a unique key just for this one (FlashList optimization).
|
||||
key={plugin.manifest.id}
|
||||
disabled={essential}
|
||||
onValueChange={enabled => {
|
||||
if (enabled)
|
||||
return handleEnablePlugin(
|
||||
plugin,
|
||||
dependencies,
|
||||
)
|
||||
else
|
||||
return handleDisablePlugin(
|
||||
plugin,
|
||||
dependents,
|
||||
)
|
||||
if (enabled) handleEnablePlugin(plugin)
|
||||
else handleDisablePlugin(plugin)
|
||||
}}
|
||||
value={enabled}
|
||||
/>
|
||||
|
|
@ -210,162 +230,6 @@ export const InstalledPluginCard = memo(function InstalledPluginCard({
|
|||
)
|
||||
})
|
||||
|
||||
async function handleEnablePlugin(
|
||||
plugin: AnyPlugin,
|
||||
dependencies: AnyPlugin[],
|
||||
) {
|
||||
const disabledDeps = dependencies.filter(dep => !isPluginEnabled(dep))
|
||||
|
||||
async function action() {
|
||||
await enablePlugin(plugin)
|
||||
await runPluginLate(plugin)
|
||||
}
|
||||
|
||||
if (disabledDeps.length)
|
||||
AlertActionCreators.openAlert(
|
||||
'plugin-has-dependencies',
|
||||
<PluginStatesProvider>
|
||||
<PluginHasDependenciesAlert
|
||||
plugin={plugin}
|
||||
dependencies={disabledDeps}
|
||||
action={action}
|
||||
/>
|
||||
</PluginStatesProvider>,
|
||||
)
|
||||
else await action()
|
||||
}
|
||||
|
||||
async function handleDisablePlugin(plugin: AnyPlugin, dependents: AnyPlugin[]) {
|
||||
const action = plugin.disable
|
||||
|
||||
const enabledDeps = dependents.filter(isPluginEnabled)
|
||||
|
||||
if (enabledDeps.length)
|
||||
AlertActionCreators.openAlert(
|
||||
'plugin-has-dependents',
|
||||
<PluginStatesProvider>
|
||||
<PluginHasDependentsAlert
|
||||
plugin={plugin}
|
||||
dependents={enabledDeps}
|
||||
action={action}
|
||||
/>
|
||||
</PluginStatesProvider>,
|
||||
)
|
||||
else await action()
|
||||
}
|
||||
|
||||
function PluginHasDependenciesAlert({
|
||||
plugin,
|
||||
dependencies,
|
||||
action,
|
||||
}: {
|
||||
plugin: AnyPlugin
|
||||
dependencies: AnyPlugin[]
|
||||
action: () => Promise<void>
|
||||
}) {
|
||||
const { height: windowHeight } = useWindowDimensions()
|
||||
const maxHeight = PixelRatio.get() * windowHeight * 0.35 - 64
|
||||
|
||||
return (
|
||||
<Design.AlertModal
|
||||
title="Plugin has dependencies"
|
||||
content={
|
||||
<Text color="header-secondary">
|
||||
Plugin{' '}
|
||||
<Text variant="text-md/semibold" color="text-normal">
|
||||
{plugin.manifest.name}
|
||||
</Text>{' '}
|
||||
depends on other plugins to function.
|
||||
{'\n'}
|
||||
Enabling it will also enable the following plugins:
|
||||
</Text>
|
||||
}
|
||||
extraContent={
|
||||
<View
|
||||
style={{
|
||||
height: Math.min(
|
||||
PLUGIN_CARD_ESTIMATED_SIZE * dependencies.length,
|
||||
maxHeight,
|
||||
),
|
||||
maxHeight,
|
||||
}}
|
||||
>
|
||||
<PluginFlashList plugins={dependencies} />
|
||||
</View>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<Design.AlertActionButton
|
||||
onPress={action}
|
||||
text="Enable all"
|
||||
variant="primary"
|
||||
/>
|
||||
<Design.AlertActionButton
|
||||
text="Cancel"
|
||||
variant="secondary"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function PluginHasDependentsAlert({
|
||||
plugin,
|
||||
dependents,
|
||||
action,
|
||||
}: {
|
||||
plugin: AnyPlugin
|
||||
dependents: AnyPlugin[]
|
||||
action: () => Promise<void>
|
||||
}) {
|
||||
const { height: windowHeight } = useWindowDimensions()
|
||||
const maxHeight = PixelRatio.get() * windowHeight * 0.35 - 64
|
||||
|
||||
return (
|
||||
<Design.AlertModal
|
||||
title="Plugin has dependents"
|
||||
content={
|
||||
<Text color="header-secondary">
|
||||
Other plugins depend on{' '}
|
||||
<Text variant="text-md/semibold" color="text-normal">
|
||||
{plugin.manifest.name}
|
||||
</Text>{' '}
|
||||
to function.
|
||||
{'\n'}
|
||||
Disabling it will also disable the following plugins:
|
||||
</Text>
|
||||
}
|
||||
extraContent={
|
||||
<View
|
||||
style={{
|
||||
height: Math.min(
|
||||
PLUGIN_CARD_ESTIMATED_SIZE * dependents.length,
|
||||
maxHeight,
|
||||
),
|
||||
maxHeight,
|
||||
}}
|
||||
>
|
||||
<PluginFlashList plugins={dependents} />
|
||||
</View>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<Design.AlertActionButton
|
||||
text="Disable all"
|
||||
variant="destructive"
|
||||
onPress={action}
|
||||
/>
|
||||
<Design.AlertActionButton
|
||||
text="Cancel"
|
||||
variant="secondary"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const usePluginCardStyles = createStyles({
|
||||
icon: {
|
||||
width: 20,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import { Design } from '@revenge-mod/discord/design'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
|
||||
const { AlertModal, AlertActionButton, Text } = Design
|
||||
|
||||
export default function PluginClearDataConfirmationAlert({
|
||||
plugin,
|
||||
action,
|
||||
}: {
|
||||
plugin: AnyPlugin
|
||||
action: () => Promise<void>
|
||||
}) {
|
||||
return (
|
||||
<AlertModal
|
||||
title="Clear plugin data?"
|
||||
content={
|
||||
<Text color="header-secondary">
|
||||
All data, including files and settings for{' '}
|
||||
<Text variant="text-md/semibold" color="text-normal">
|
||||
{plugin.manifest.name}
|
||||
</Text>{' '}
|
||||
will be deleted
|
||||
</Text>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<AlertActionButton
|
||||
onPress={action}
|
||||
text="Clear"
|
||||
variant="destructive"
|
||||
/>
|
||||
<AlertActionButton text="Cancel" variant="secondary" />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { PixelRatio, useWindowDimensions, View } from 'react-native'
|
||||
import { PLUGIN_CARD_ESTIMATED_SIZE } from './PluginCard'
|
||||
import { PluginFlashList } from './PluginList'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
|
||||
const { AlertModal, AlertActionButton, Text } = Design
|
||||
|
||||
export default function PluginHasDependenciesAlert({
|
||||
plugin,
|
||||
dependencies,
|
||||
action,
|
||||
}: {
|
||||
plugin: AnyPlugin
|
||||
dependencies: AnyPlugin[]
|
||||
action: () => Promise<void>
|
||||
}) {
|
||||
const { height: windowHeight } = useWindowDimensions()
|
||||
const maxHeight = PixelRatio.get() * windowHeight * 0.35 - 64
|
||||
|
||||
return (
|
||||
<AlertModal
|
||||
title="Plugin has dependencies"
|
||||
content={
|
||||
<Text color="header-secondary">
|
||||
Plugin{' '}
|
||||
<Text variant="text-md/semibold" color="text-normal">
|
||||
{plugin.manifest.name}
|
||||
</Text>{' '}
|
||||
depends on other plugins to function.
|
||||
{'\n'}
|
||||
Enabling it will also enable the following plugins:
|
||||
</Text>
|
||||
}
|
||||
extraContent={
|
||||
<View
|
||||
style={{
|
||||
height: Math.min(
|
||||
PLUGIN_CARD_ESTIMATED_SIZE * dependencies.length,
|
||||
maxHeight,
|
||||
),
|
||||
maxHeight,
|
||||
}}
|
||||
>
|
||||
<PluginFlashList plugins={dependencies} />
|
||||
</View>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<AlertActionButton
|
||||
onPress={action}
|
||||
text="Enable all"
|
||||
variant="primary"
|
||||
/>
|
||||
<AlertActionButton text="Cancel" variant="secondary" />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { PixelRatio, useWindowDimensions, View } from 'react-native'
|
||||
import { PLUGIN_CARD_ESTIMATED_SIZE } from './PluginCard'
|
||||
import { PluginFlashList } from './PluginList'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
|
||||
const { AlertModal, AlertActionButton, Text } = Design
|
||||
|
||||
export default function PluginHasDependentsAlert({
|
||||
plugin,
|
||||
dependents,
|
||||
action,
|
||||
}: {
|
||||
plugin: AnyPlugin
|
||||
dependents: AnyPlugin[]
|
||||
action: () => Promise<void>
|
||||
}) {
|
||||
const { height: windowHeight } = useWindowDimensions()
|
||||
const maxHeight = PixelRatio.get() * windowHeight * 0.35 - 64
|
||||
|
||||
return (
|
||||
<AlertModal
|
||||
title="Plugin has dependents"
|
||||
content={
|
||||
<Text color="header-secondary">
|
||||
Other plugins depend on{' '}
|
||||
<Text variant="text-md/semibold" color="text-normal">
|
||||
{plugin.manifest.name}
|
||||
</Text>{' '}
|
||||
to function.
|
||||
{'\n'}
|
||||
Disabling it will also disable the following plugins:
|
||||
</Text>
|
||||
}
|
||||
extraContent={
|
||||
<View
|
||||
style={{
|
||||
height: Math.min(
|
||||
PLUGIN_CARD_ESTIMATED_SIZE * dependents.length,
|
||||
maxHeight,
|
||||
),
|
||||
maxHeight,
|
||||
}}
|
||||
>
|
||||
<PluginFlashList plugins={dependents} />
|
||||
</View>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
<AlertActionButton
|
||||
text="Disable all"
|
||||
variant="destructive"
|
||||
onPress={action}
|
||||
/>
|
||||
<AlertActionButton text="Cancel" variant="secondary" />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import { getAssetIdByName } from '@revenge-mod/assets'
|
||||
import { FormSwitch } from '@revenge-mod/components'
|
||||
import {
|
||||
ActionSheetActionCreators,
|
||||
ToastActionCreators,
|
||||
} from '@revenge-mod/discord/actions'
|
||||
import { Design } from '@revenge-mod/discord/design'
|
||||
import { Clipboard } from '@revenge-mod/externals/react-native-clipboard'
|
||||
import {
|
||||
getInternalPluginMeta,
|
||||
isPluginEssential,
|
||||
} from '@revenge-mod/plugins/_'
|
||||
import { lookupGeneratedIconComponent } from '@revenge-mod/utils/discord'
|
||||
import { getErrorStack } from '@revenge-mod/utils/error'
|
||||
import { handleDisablePlugin, handleEnablePlugin } from '../utils/actions'
|
||||
import {
|
||||
openPluginSettings,
|
||||
showPluginClearDataConfirmation,
|
||||
} from '../utils/alerts'
|
||||
import { PluginInfo } from './PluginCard'
|
||||
import { usePluginEnabled } from './PluginStateProvider'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
|
||||
export interface PluginOptionsActionSheetProps {
|
||||
plugin: AnyPlugin
|
||||
sheetKey: string
|
||||
}
|
||||
|
||||
const { ActionSheet, IconButton, TableRowGroup, TableRow, Stack } = Design
|
||||
|
||||
const FileWarningIcon = getAssetIdByName('FileWarningIcon', 'png')!
|
||||
const SettingsIcon = getAssetIdByName('SettingsIcon', 'png')!
|
||||
|
||||
export default function PluginOptionsActionSheet({
|
||||
plugin,
|
||||
sheetKey,
|
||||
}: PluginOptionsActionSheetProps) {
|
||||
const enabled = usePluginEnabled(plugin)
|
||||
const meta = getInternalPluginMeta(plugin)
|
||||
const { dependencies, dependents } = meta!
|
||||
const essential = isPluginEssential(meta)
|
||||
const {
|
||||
manifest: { id, name, author, description, icon },
|
||||
errors,
|
||||
flags,
|
||||
} = plugin
|
||||
|
||||
const closeActionSheet = () => {
|
||||
ActionSheetActionCreators.hideActionSheet(sheetKey)
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionSheet>
|
||||
<Stack spacing={16}>
|
||||
<PluginInfo
|
||||
name={name}
|
||||
author={author}
|
||||
description={description}
|
||||
icon={icon}
|
||||
actions={
|
||||
!essential && (
|
||||
<FormSwitch
|
||||
value={enabled}
|
||||
onValueChange={v => {
|
||||
if (v) handleEnablePlugin(plugin)
|
||||
else handleDisablePlugin(plugin)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Stack
|
||||
direction="horizontal"
|
||||
justify="space-around"
|
||||
style={{ paddingHorizontal: 8, paddingVertical: 16 }}
|
||||
>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
icon={FileWarningIcon}
|
||||
label="Clear Data"
|
||||
onPress={() => {
|
||||
showPluginClearDataConfirmation(
|
||||
plugin,
|
||||
closeActionSheet,
|
||||
)
|
||||
}}
|
||||
/>
|
||||
{plugin.SettingsComponent && (
|
||||
<Design.IconButton
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
icon={SettingsIcon}
|
||||
label="Settings"
|
||||
onPress={() => {
|
||||
openPluginSettings(plugin)
|
||||
closeActionSheet()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
<TableRowGroup title="Status">
|
||||
<TableRow label="Flags (TODO)" subLabel={`${flags}`} />
|
||||
{errors.length && (
|
||||
<TableRow
|
||||
label="Errors"
|
||||
subLabel={`${errors.length} errors. Tap to copy.`}
|
||||
onPress={() => {
|
||||
Clipboard.setString(
|
||||
errors.map(getErrorStack).join('\n'),
|
||||
)
|
||||
showCopiedToClipboardToast()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</TableRowGroup>
|
||||
<TableRowGroup title="Advanced">
|
||||
<TableRow
|
||||
label="ID"
|
||||
subLabel={id}
|
||||
onPress={() => {
|
||||
Clipboard.setString(id)
|
||||
showCopiedToClipboardToast()
|
||||
}}
|
||||
/>
|
||||
{dependencies?.length && (
|
||||
<TableRow
|
||||
label="Dependencies (TODO)"
|
||||
subLabel={`${name} depends on ${dependencies.length} other plugins`}
|
||||
/>
|
||||
)}
|
||||
{dependents?.length && (
|
||||
<TableRow
|
||||
label="Dependents (TODO)"
|
||||
subLabel={`${dependents.length} other plugins depend on ${name}`}
|
||||
/>
|
||||
)}
|
||||
</TableRowGroup>
|
||||
</Stack>
|
||||
</ActionSheet>
|
||||
)
|
||||
}
|
||||
|
||||
const CopyIcon = lookupGeneratedIconComponent('CopyIcon')!
|
||||
|
||||
function showCopiedToClipboardToast() {
|
||||
ToastActionCreators.open({
|
||||
key: 'REVENGE_PLUGIN_SETTINGS_COPIED',
|
||||
content: 'Copied to clipboard',
|
||||
IconComponent: CopyIcon,
|
||||
})
|
||||
}
|
||||
38
src/plugins/start/settings/utils/actions.tsx
Normal file
38
src/plugins/start/settings/utils/actions.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import {
|
||||
disablePlugin,
|
||||
enablePlugin,
|
||||
getInternalPluginMeta,
|
||||
getPluginDependencies,
|
||||
isPluginEnabled,
|
||||
runPluginLate,
|
||||
} from '@revenge-mod/plugins/_'
|
||||
import {
|
||||
showPluginHasDependenciesAlert,
|
||||
showPluginHasDependentsAlert,
|
||||
} from './alerts'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
|
||||
export async function handleEnablePlugin(plugin: AnyPlugin) {
|
||||
const dependencies = getPluginDependencies(plugin)!
|
||||
const disabledDeps = dependencies?.filter(dep => !isPluginEnabled(dep))
|
||||
|
||||
async function action() {
|
||||
await enablePlugin(plugin)
|
||||
await runPluginLate(plugin)
|
||||
}
|
||||
|
||||
if (disabledDeps?.length)
|
||||
showPluginHasDependenciesAlert(plugin, disabledDeps, action)
|
||||
else await action()
|
||||
}
|
||||
|
||||
export async function handleDisablePlugin(plugin: AnyPlugin) {
|
||||
const { dependents } = getInternalPluginMeta(plugin)!
|
||||
const action = () => disablePlugin(plugin)
|
||||
|
||||
const enabledDeps = dependents.filter(isPluginEnabled)
|
||||
|
||||
if (enabledDeps.length)
|
||||
showPluginHasDependentsAlert(plugin, enabledDeps, action)
|
||||
else await action()
|
||||
}
|
||||
73
src/plugins/start/settings/utils/alerts.tsx
Normal file
73
src/plugins/start/settings/utils/alerts.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { AlertActionCreators } from '@revenge-mod/discord/actions'
|
||||
import { RootNavigationRef } from '@revenge-mod/discord/modules/main_tabs_v2'
|
||||
import PluginClearDataConfirmationAlert from '../components/PluginClearDataConfirmationAlert'
|
||||
import PluginHasDependenciesAlert from '../components/PluginHasDependenciesAlert'
|
||||
import PluginHasDependentsAlert from '../components/PluginHasDependentsAlert'
|
||||
import PluginStatesProvider from '../components/PluginStateProvider'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
import type { InitPluginApi } from '@revenge-mod/plugins/types'
|
||||
import type { AnyObject } from '@revenge-mod/utils/types'
|
||||
|
||||
export function showPluginClearDataConfirmation(
|
||||
plugin: AnyPlugin,
|
||||
callback: () => void,
|
||||
) {
|
||||
const KEY = 'plugin-clear-data-confirmation'
|
||||
|
||||
async function action() {
|
||||
const { storage } = plugin.api as InitPluginApi<{ storage: AnyObject }>
|
||||
if (await storage.exists()) await storage.delete()
|
||||
callback()
|
||||
}
|
||||
|
||||
AlertActionCreators.openAlert(
|
||||
KEY,
|
||||
<PluginClearDataConfirmationAlert plugin={plugin} action={action} />,
|
||||
)
|
||||
}
|
||||
|
||||
export function openPluginSettings(plugin: AnyPlugin) {
|
||||
if (!plugin.SettingsComponent) return
|
||||
|
||||
const navigation = RootNavigationRef.getRootNavigationRef()
|
||||
if (navigation.isReady()) navigation.navigate(plugin.manifest.id)
|
||||
else
|
||||
navigation.addListener('ready', function self() {
|
||||
navigation.navigate(plugin.manifest.id)
|
||||
navigation.removeListener('ready', self)
|
||||
})
|
||||
}
|
||||
|
||||
export function showPluginHasDependenciesAlert(
|
||||
plugin: AnyPlugin,
|
||||
dependencies: AnyPlugin[],
|
||||
action: () => Promise<void>,
|
||||
) {
|
||||
AlertActionCreators.openAlert(
|
||||
'plugin-has-dependencies',
|
||||
<PluginStatesProvider>
|
||||
<PluginHasDependenciesAlert
|
||||
plugin={plugin}
|
||||
dependencies={dependencies}
|
||||
action={action}
|
||||
/>
|
||||
</PluginStatesProvider>,
|
||||
)
|
||||
}
|
||||
|
||||
export function showPluginHasDependentsAlert(
|
||||
plugin: AnyPlugin,
|
||||
dependents: AnyPlugin[],
|
||||
action: () => Promise<void>,
|
||||
) {
|
||||
AlertActionCreators.openAlert(
|
||||
'plugin-has-dependents',
|
||||
<PluginStatesProvider>
|
||||
<PluginHasDependentsAlert
|
||||
plugin={plugin}
|
||||
dependents={dependents}
|
||||
action={action}
|
||||
/>
|
||||
</PluginStatesProvider>,
|
||||
)
|
||||
}
|
||||
20
src/plugins/start/settings/utils/sheets.tsx
Normal file
20
src/plugins/start/settings/utils/sheets.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { ActionSheetActionCreators } from '@revenge-mod/discord/actions'
|
||||
import PluginStatesProvider from '../components/PluginStateProvider'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
import type { PluginOptionsActionSheetProps } from '../components/PluginOptionsActionSheet'
|
||||
|
||||
export function showPluginOptionsActionSheet(plugin: AnyPlugin) {
|
||||
const KEY = 'plugin-options-action-sheet'
|
||||
|
||||
ActionSheetActionCreators.openLazy(
|
||||
import('../components/PluginOptionsActionSheet').then(m => ({
|
||||
default: (props: PluginOptionsActionSheetProps) => (
|
||||
<PluginStatesProvider>
|
||||
<m.default {...props} plugin={plugin} />
|
||||
</PluginStatesProvider>
|
||||
),
|
||||
})),
|
||||
KEY,
|
||||
{ plugin, sheetKey: KEY },
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue