Skip to content

Commit

Permalink
settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Slouchwind committed Jun 27, 2023
1 parent 3fc356c commit e0c99fc
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 124 deletions.
4 changes: 2 additions & 2 deletions components/extraReact.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export type setStateFun<S> = (newState: Partial<S>) => void
export type SetStateFun<S> = (newState: Partial<S>) => void

export function getClassState<S>(
useState: [S, React.Dispatch<React.SetStateAction<S>>]
): [S, setStateFun<S>] {
): [S, SetStateFun<S>] {
const setState = (newState: Partial<S>) => useState[1]((preState) => ({ ...preState, ...newState }));
return [useState[0], setState];
}
2 changes: 1 addition & 1 deletion components/i18n/config/chat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const chat = {
const chat = {
title: {
'zh-CN': '聊天编辑',
'zh-TW': '聊天編輯',
Expand Down
4 changes: 3 additions & 1 deletion components/i18n/config/indexConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const index = {
import { i18nContents } from "..";

const index: i18nContents = {
selectingText: {
'zh-CN': '请选择栏目。',
'zh-TW': '請選擇欄目。'
Expand Down
4 changes: 3 additions & 1 deletion components/i18n/config/info.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const info = {
import { i18nContents } from "..";

const info: i18nContents = {
title: {
'zh-CN': '学生信息',
'zh-TW': '學生信息',
Expand Down
37 changes: 37 additions & 0 deletions components/i18n/config/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { i18nContents } from "..";

const main: i18nContents = {
setting: {
'zh-CN': '设置',
'zh-TW': '設定',
},
setLocale: {
'zh-CN': '选择语言',
'zh-TW': '選取語言',
},
locales: {
'zh-CN': '中文(简体)',
'zh-TW': '中文(繁體)',
},
setAnimation: {
'zh-CN': '设置开屏动画模式',
'zh-TW': '設定開屏動畫模式',
},
animationTnone: {
'zh-CN': '从不',
'zh-TW': '從不',
},
animationTfirst: {
'zh-CN': '首次',
'zh-TW': '首次',
},
animationTevery: {
'zh-CN': '每次',
'zh-TW': '每次',
},
done: {
'zh-CN': '确定',
'zh-TW': '確定',
},
}
export default main;
20 changes: 13 additions & 7 deletions components/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useRouter } from "next/router";
import { useSetting } from "../setting";

export type i18nContents = { [x: string]: { [x: string]: string } };
export type Locales = 'zh-CN' | 'zh-TW';

export type i18nContents = Record<string, Record<Locales, string>>;

export function fillBlank(i18n: string, ...fills: (string | undefined)[]): string {
let text = i18n;
Expand All @@ -9,10 +11,14 @@ export function fillBlank(i18n: string, ...fills: (string | undefined)[]): strin
}

export function useLocale<T extends i18nContents, K extends keyof T>(i18n: T) {
const { locale: localeText, defaultLocale = 'zh-CN', ...other } = useRouter();
const lo = localeText || defaultLocale;
function locale(key: K) {
return i18n[key][lo];
const { setting } = useSetting();
//const { locale: localeText, defaultLocale = 'zh-CN', ...other } = useRouter();
const lo = setting.locale || 'zh-CN';
function locale(key: K, loc?: string) {
return (i18n as Record<K, Record<string, string>>)[key][loc || lo];
}
function localeType<Type extends string>(key: Type, loc?: string) {
return (i18n as Record<Type, Record<string, string>>)[key][loc || lo];
}
return { lo, locale, ...other };
return { lo, locale, localeType };
}
87 changes: 80 additions & 7 deletions components/main.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
//Components
import Link from 'next/link';
import { SettingForm, useSetting } from '@/components/setting';

//Styles
import styles from '@/styles/MainNode.module.scss';

//Methods
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import Window, { AllWindow, AllWindows, getWindowFun } from './window';
import { Locales, useLocale } from './i18n';
import main from './i18n/config/main';
import { SettingState, SettingArg } from '@/components/setting';

function MomoTalkIcon() {
return (
Expand All @@ -32,17 +37,23 @@ function MTBarLink({ type }: { type: string }) {
);
}

function MTStart() {
const [start, setStart] = useState<'true' | 'false'>('false');
function MTStart({ animation }: { animation: SettingState['animation'] }) {
const [ani, setAni] = useState<'true' | 'false'>('false');
useEffect(() => {
const { animation } = window.sessionStorage;
if (animation !== undefined) setStart(animation);
if (animation !== undefined) setAni(animation);
else {
window.sessionStorage.animation = 'false';
setStart('true');
setAni('true');
}
}, [])
return <>{start === 'true' && (
}, []);

const userAni = (() => {
if (animation === 'none' || animation === undefined) return 'false';
else if (animation === 'first') return ani;
else if (animation === 'every') return 'true';
})();
return <>{userAni === 'true' && (
<div id={styles.MTStart}>
<div>
<MomoTalkIcon />
Expand All @@ -56,15 +67,77 @@ export default function MainNode({ children, onBodyClick }: {
children: React.ReactNode;
onBodyClick?: React.MouseEventHandler;
}) {
const { lo, locale, localeType } = useLocale(main);

const { setting, setSetting } = useSetting({
locale: lo,
animation: 'first',
});

const {
allWindow,
addNewWindow,
openWindow,
closeWindow,
} = getWindowFun(useState<AllWindow>({ all: [], component: {} }));

const Setting = new Window<SettingArg>('Setting');

useEffect(() => {
const animations = ['none', 'first', 'every'];
addNewWindow(Setting, (zIndex, id, display, { setting, setSetting }, all) => (
<Setting.Component
title={locale('setting')}
closeWindow={() => closeWindow(all, id)}
element={close => (
<SettingForm
option={{
locale: {
type: 'option',
label: locale('setLocale'),
values: ['zh-CN', 'zh-TW'] as Locales[],
defaultValue: setting.locale,
getValue: v => (<option value={v}>{locale('locales', v)}</option>),
},
animation: {
type: 'option',
label: locale('setAnimation'),
values: animations,
defaultValue: setting.animation,
getValue: v => (<option value={v}>{localeType('animationT' + v)}</option>),
},
}}
done={locale('done')}
onSubmit={data => {
const dataAsSet = data as SettingState;
window.localStorage.set = JSON.stringify(dataAsSet);
setSetting(dataAsSet);
close();
}}
/>
)}
zIndex={zIndex}
display={display}
/>
));
}, []);

return (
<div id={styles.main} onClick={onBodyClick}>
<MTStart />
<AllWindows zIndex={1099} allWindow={allWindow} />
<MTStart animation={setting.animation} />
<div id={styles.MTBackground}>
<div id={styles.MTBar}>
<div id={styles.left}>
<MomoTalkIcon />
<p className={styles.MTText}>MomoTalk</p>
</div>
<div id={styles.right}>
<img
src='/api/icon/setting'
onClick={() => openWindow(allWindow.all, Setting, { setting, setSetting })}
/>
</div>
</div>
<div id={styles.MTContents}>
<div id={styles.MTLeftBar}>
Expand Down
8 changes: 7 additions & 1 deletion components/repeat.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from 'react';

interface RepeatProps<T> {
/**自变量初始值 */
variable: T,
Expand All @@ -12,7 +14,11 @@ interface RepeatProps<T> {
export default function Repeat<T>({ variable, repeat, func, components }: RepeatProps<T>) {
const array = new Array(repeat).fill(null);
array.forEach((_, i) => {
array[i] = components(variable);
array[i] = (
<React.Fragment key={i}>
{components(variable)}
</React.Fragment>
);
variable = func(variable);
});
return <>{array}</>;
Expand Down
87 changes: 87 additions & 0 deletions components/setting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import styles from '@/styles/MainNode.module.scss';

import React, { useEffect, useState } from 'react';
import Repeat from './repeat';
import { useForm } from 'react-hook-form';
import { SetStateFun } from './extraReact';
import { getClassState } from './extraReact';

interface SettingOption {
type: string;
label: string;
}

interface OptionSO<T extends string = string> extends SettingOption {
type: 'option';
values?: T[];
defaultValue?: T;
getValue: (value: T, index: number, array: T[]) => React.ReactNode;
}

type OptionTypes = OptionSO;

interface SettingFormProps<N extends string> {
option: Record<N, OptionTypes>;
done: string;
onSubmit: (data: Record<N, string>) => any;
}

export function SettingForm<N extends string>({ option, done, onSubmit }: SettingFormProps<N>) {
const { register, handleSubmit } = useForm();
let optionArray: (OptionTypes & { name: N })[] = [];
for (let o in option) {
let opt = option[o];
optionArray.push(Object.assign(opt, { name: o }));
}
return (
<form onSubmit={handleSubmit(data => {
const dataKeyAsN = data as Record<N, string>;
onSubmit(dataKeyAsN);
})} id={styles.settings}>
<Repeat
variable={0}
repeat={optionArray.length}
func={i => i + 1}
components={i => {
const o = optionArray[i];
return (
<label>
{o.label}
{o.type === 'option' && (
<select {...register(o.name)} defaultValue={o.defaultValue}>
{o.values?.map((v, i, a) => (
<React.Fragment key={i}>
{o.getValue(v, i, a)}
</React.Fragment>
))}
</select>
)}
</label>
);
}}
/>
<button type='submit'>{done}</button>
</form>
)
}

export interface SettingState {
locale?: string;
animation?: 'none' | 'first' | 'every';
}

export interface SettingArg {
setting: SettingState;
setSetting: SetStateFun<SettingState>;
}

export function useSetting(state?: SettingState) {
const [setting, setSetting] = getClassState(useState<SettingState>(state || {}));
useEffect(() => {
let setting: string = window.localStorage.set;
if (setting !== undefined) {
setSetting(JSON.parse(setting));
}
}, []);
return { setting, setSetting };
}
4 changes: 2 additions & 2 deletions components/window.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function AllWindows({ zIndex, allWindow }: {
components={v => {
if (allWindow.component && allWindow.all)
return (
<React.Fragment key={allWindow.all[v].id}>
<div className='window'>
<div className='back' style={{ zIndex: zIndex + (2 * v) }} />
{allWindow.component[allWindow.all[v].name](
zIndex + 1 + (2 * v),
Expand All @@ -82,7 +82,7 @@ export function AllWindows({ zIndex, allWindow }: {
allWindow.all[v].arg,
allWindow.all
)}
</React.Fragment>
</div>
);
}}
/>
Expand Down
4 changes: 0 additions & 4 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ const nextConfig = {
sassOptions: {
includePaths: [path.join(__dirname, 'styles')],
},
i18n: {
locales: ['zh-CN', 'zh-TW'],
defaultLocale: 'zh-CN'
}
}

module.exports = nextConfig;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"next-seo": "^5.15.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.45.0",
"sass": "^1.58.3",
"typescript": "4.9.5"
}
Expand Down
Loading

1 comment on commit e0c99fc

@vercel
Copy link

@vercel vercel bot commented on e0c99fc Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.