feat: 增加系统设置页面的自定义设置项注册(for plugin)

pull/64/head
moonrailgun 3 years ago
parent badfaa07d3
commit e9b96a15a2

@ -11,10 +11,19 @@ import {
fetchServiceRegistryPlugins, fetchServiceRegistryPlugins,
PluginManifest, PluginManifest,
} from '../model/plugin'; } from '../model/plugin';
import { fetchUserInfo, UserBaseInfo } from '../model/user'; import { fetchUserInfo, getUserSettings, UserBaseInfo } from '../model/user';
import { parseUrlStr } from '../utils/url-helper'; import { parseUrlStr } from '../utils/url-helper';
import { queryClient } from './index'; import { queryClient } from './index';
export enum CacheKey {
user = 'user',
converse = 'converse',
baseGroupInfo = 'baseGroupInfo',
groupInvite = 'groupInvite',
pluginRegistry = 'pluginRegistry',
userSettings = 'userSettings',
}
/** /**
* *
*/ */
@ -23,7 +32,7 @@ export async function getCachedUserInfo(
refetch = false refetch = false
): Promise<UserBaseInfo> { ): Promise<UserBaseInfo> {
const data = await queryClient.fetchQuery( const data = await queryClient.fetchQuery(
['user', userId], [CacheKey.user, userId],
() => fetchUserInfo(userId), () => fetchUserInfo(userId),
{ {
staleTime: refetch ? 0 : 2 * 60 * 60 * 1000, // 缓存2小时 staleTime: refetch ? 0 : 2 * 60 * 60 * 1000, // 缓存2小时
@ -39,8 +48,9 @@ export async function getCachedUserInfo(
export async function getCachedConverseInfo( export async function getCachedConverseInfo(
converseId: string converseId: string
): Promise<ChatConverseInfo> { ): Promise<ChatConverseInfo> {
const data = await queryClient.fetchQuery(['converse', converseId], () => const data = await queryClient.fetchQuery(
fetchConverseInfo(converseId) [CacheKey.converse, converseId],
() => fetchConverseInfo(converseId)
); );
return data; return data;
@ -52,8 +62,9 @@ export async function getCachedConverseInfo(
export async function getCachedBaseGroupInfo( export async function getCachedBaseGroupInfo(
groupId: string groupId: string
): Promise<GroupBasicInfo | null> { ): Promise<GroupBasicInfo | null> {
const data = await queryClient.fetchQuery(['baseGroupInfo', groupId], () => const data = await queryClient.fetchQuery(
getGroupBasicInfo(groupId) [CacheKey.baseGroupInfo, groupId],
() => getGroupBasicInfo(groupId)
); );
return data; return data;
@ -65,8 +76,9 @@ export async function getCachedBaseGroupInfo(
export async function getCachedGroupInviteInfo( export async function getCachedGroupInviteInfo(
inviteCode: string inviteCode: string
): Promise<GroupInvite | null> { ): Promise<GroupInvite | null> {
const data = await queryClient.fetchQuery(['groupInvite', inviteCode], () => const data = await queryClient.fetchQuery(
findGroupInviteByCode(inviteCode) [CacheKey.groupInvite, inviteCode],
() => findGroupInviteByCode(inviteCode)
); );
return data; return data;
@ -77,7 +89,7 @@ export async function getCachedGroupInviteInfo(
*/ */
export async function getCachedRegistryPlugins(): Promise<PluginManifest[]> { export async function getCachedRegistryPlugins(): Promise<PluginManifest[]> {
const data = await queryClient.fetchQuery( const data = await queryClient.fetchQuery(
['pluginRegistry'], [CacheKey.pluginRegistry],
() => () =>
Promise.all([ Promise.all([
fetchRegistryPlugins().catch(() => []), fetchRegistryPlugins().catch(() => []),
@ -111,3 +123,18 @@ export async function getCachedRegistryPlugins(): Promise<PluginManifest[]> {
return data; return data;
} }
/**
*
*/
export async function getCachedUserSettings() {
const data = await queryClient.fetchQuery(
[CacheKey.userSettings],
() => getUserSettings,
{
staleTime: 1 * 60 * 1000, // 缓存1分钟
}
);
return data;
}

@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useUpdateRef } from '../hooks/useUpdateRef'; import { useUpdateRef } from '../hooks/useUpdateRef';
import type { ChatMessage, SendMessagePayload } from '../model/message'; import type { ChatMessage, SendMessagePayload } from '../model/message';
import { EventEmitter } from 'eventemitter-strict'; import { EventEmitter } from 'eventemitter-strict';
import type { UserBaseInfo } from '../model/user'; import type { UserBaseInfo, UserSettings } from '../model/user';
/** /**
* *
@ -51,6 +51,11 @@ export interface SharedEventMap {
* *
*/ */
groupPanelBadgeUpdate: () => void; groupPanelBadgeUpdate: () => void;
/**
*
*/
userSettingsUpdate: (userSettings: UserSettings) => void;
} }
export type SharedEventType = keyof SharedEventMap; export type SharedEventType = keyof SharedEventMap;

@ -1,4 +1,6 @@
import { CacheKey } from '../../cache/cache';
import { useQuery, useQueryClient } from '../../cache/useCache'; import { useQuery, useQueryClient } from '../../cache/useCache';
import { sharedEvent } from '../../event';
import { import {
getUserSettings, getUserSettings,
setUserSettings, setUserSettings,
@ -12,7 +14,7 @@ import { useAsyncRequest } from '../useAsyncRequest';
export function useUserSettings() { export function useUserSettings() {
const client = useQueryClient(); const client = useQueryClient();
const { data: settings, isLoading } = useQuery( const { data: settings, isLoading } = useQuery(
['useUserSettings'], [CacheKey],
() => getUserSettings(), () => getUserSettings(),
{ {
staleTime: 1 * 60 * 1000, // 缓存1分钟 staleTime: 1 * 60 * 1000, // 缓存1分钟
@ -23,7 +25,8 @@ export function useUserSettings() {
async (settings: UserSettings) => { async (settings: UserSettings) => {
const newSettings = await setUserSettings(settings); const newSettings = await setUserSettings(settings);
client.setQueryData(['useUserSettings'], () => newSettings); client.setQueryData([CacheKey], () => newSettings);
sharedEvent.emit('userSettingsUpdate', newSettings);
}, },
[client] [client]
); );

@ -12,6 +12,7 @@ export {
getCachedBaseGroupInfo, getCachedBaseGroupInfo,
getCachedGroupInviteInfo, getCachedGroupInviteInfo,
getCachedRegistryPlugins, getCachedRegistryPlugins,
getCachedUserSettings,
} from './cache/cache'; } from './cache/cache';
export { useCachedUserInfo, useCachedOnlineStatus } from './cache/useCache'; export { useCachedUserInfo, useCachedOnlineStatus } from './cache/useCache';

@ -27,6 +27,11 @@ export interface UserSettings {
* *
*/ */
messageListVirtualization?: boolean; messageListVirtualization?: boolean;
/**
*
*/
[key: string]: any;
} }
export function pickUserBaseInfo(userInfo: UserLoginInfo) { export function pickUserBaseInfo(userInfo: UserLoginInfo) {

@ -0,0 +1,105 @@
import { Select, Switch } from 'antd';
import React from 'react';
import {
DefaultFullModalInputEditorRender,
DefaultFullModalTextAreaEditorRender,
FullModalField,
} from './Field';
export type FullModalFactoryConfig = {
name: string;
label: string;
desc?: string;
} & (
| {
type: 'text';
}
| {
type: 'textarea';
}
| {
type: 'boolean';
}
| {
type: 'select';
options: { label: string; value: string }[];
}
);
interface FullModalFactoryProps<T = any> {
value: T;
onChange: (val: T) => void;
config: FullModalFactoryConfig;
}
/**
*
*/
export const FullModalFactory: React.FC<FullModalFactoryProps> = React.memo(
(props) => {
const { value, onChange, config } = props;
if (config.type === 'text') {
return (
<FullModalField
title={config.label}
value={value}
editable={true}
renderEditor={DefaultFullModalInputEditorRender}
onSave={(val) => onChange(val)}
/>
);
}
if (config.type === 'textarea') {
return (
<FullModalField
title={config.label}
value={value}
editable={true}
renderEditor={DefaultFullModalTextAreaEditorRender}
onSave={(val) => onChange(val)}
/>
);
}
if (config.type === 'boolean') {
return (
<FullModalField
title={config.label}
tip={config.desc}
content={
<Switch
checked={value ?? false}
onChange={(checked) => onChange(checked)}
/>
}
/>
);
}
if (config.type === 'select') {
return (
<FullModalField
title={config.label}
tip={config.desc}
content={
<Select
style={{ width: 280 }}
size="large"
value={value}
onChange={(val) => onChange(val)}
>
{config.options.map((opt) => (
<Select.Option key={opt.value} value={opt.value}>
{opt.label}
</Select.Option>
))}
</Select>
}
/>
);
}
return null;
}
);
FullModalFactory.displayName = 'FullModalFactory';

@ -1,23 +1,20 @@
import { FullModalFactory } from '@/components/FullModal/Factory';
import { FullModalField } from '@/components/FullModal/Field'; import { FullModalField } from '@/components/FullModal/Field';
import { LanguageSelect } from '@/components/LanguageSelect'; import { LanguageSelect } from '@/components/LanguageSelect';
import { pluginColorScheme } from '@/plugin/common'; import { pluginColorScheme, pluginSettings } from '@/plugin/common';
import { Select, Switch } from 'antd'; import { Select, Switch } from 'antd';
import React from 'react'; import React from 'react';
import { import {
AlphaContainer,
t, t,
useAlphaMode, useAlphaMode,
useColorScheme, useColorScheme,
useSingleUserSetting, useUserSettings,
} from 'tailchat-shared'; } from 'tailchat-shared';
import _get from 'lodash/get';
export const SettingsSystem: React.FC = React.memo(() => { export const SettingsSystem: React.FC = React.memo(() => {
const { colorScheme, setColorScheme } = useColorScheme(); const { colorScheme, setColorScheme } = useColorScheme();
const { const { settings, setSettings, loading } = useUserSettings();
value: messageListVirtualization,
setValue: setMessageListVirtualization,
loading,
} = useSingleUserSetting('messageListVirtualization', false);
const { isAlphaMode, setAlphaMode } = useAlphaMode(); const { isAlphaMode, setAlphaMode } = useAlphaMode();
return ( return (
@ -45,6 +42,23 @@ export const SettingsSystem: React.FC = React.memo(() => {
} }
/> />
{pluginSettings
.filter((item) => item.position === 'system')
.map((item) => {
return (
<FullModalFactory
key={item.name}
value={_get(settings, item.name, false)}
onChange={(val) => {
setSettings({
[item.name]: val,
});
}}
config={item}
/>
);
})}
<FullModalField <FullModalField
title={t('Alpha测试开关')} title={t('Alpha测试开关')}
tip={t( tip={t(
@ -52,7 +66,6 @@ export const SettingsSystem: React.FC = React.memo(() => {
)} )}
content={ content={
<Switch <Switch
disabled={loading}
checked={isAlphaMode} checked={isAlphaMode}
onChange={(checked) => setAlphaMode(checked)} onChange={(checked) => setAlphaMode(checked)}
/> />
@ -65,8 +78,13 @@ export const SettingsSystem: React.FC = React.memo(() => {
content={ content={
<Switch <Switch
disabled={loading} disabled={loading}
checked={messageListVirtualization} loading={loading}
onChange={(checked) => setMessageListVirtualization(checked)} checked={settings.messageListVirtualization ?? false}
onChange={(checked) =>
setSettings({
messageListVirtualization: checked,
})
}
/> />
} }
/> />

@ -38,6 +38,7 @@ export {
getCachedUserInfo, getCachedUserInfo,
getCachedConverseInfo, getCachedConverseInfo,
getCachedBaseGroupInfo, getCachedBaseGroupInfo,
getCachedUserSettings,
localTrans, localTrans,
getLanguage, getLanguage,
sharedEvent, sharedEvent,

@ -8,6 +8,7 @@ import {
PermissionItemType, PermissionItemType,
} from 'tailchat-shared'; } from 'tailchat-shared';
import type { MetaFormFieldMeta } from 'tailchat-design'; import type { MetaFormFieldMeta } from 'tailchat-design';
import type { FullModalFactoryConfig } from '@/components/FullModal/Factory';
/** /**
* *
@ -254,3 +255,13 @@ interface PluginUserExtraInfo {
*/ */
export const [pluginUserExtraInfo, regUserExtraInfo] = export const [pluginUserExtraInfo, regUserExtraInfo] =
buildRegList<PluginUserExtraInfo>(); buildRegList<PluginUserExtraInfo>();
type PluginSettings = FullModalFactoryConfig & {
position: 'system'; // 后面可能还会有个人设置/群组设置
};
/**
*
*/
export const [pluginSettings, regPluginSettings] =
buildRegList<PluginSettings>();

Loading…
Cancel
Save