feat(pane_admin): enable some site settings in fe

This commit is contained in:
hexxa 2022-04-09 17:41:47 +08:00 committed by Hexxa
parent 1694e9026b
commit f55581efb8
11 changed files with 191 additions and 85 deletions

View file

@ -93,10 +93,16 @@ export interface GetSharingDirResp {
sharingDir: string; sharingDir: string;
} }
export interface ClientConfigMsg { export interface ClientConfig {
siteName: string; siteName: string;
siteDesc: string; siteDesc: string;
bg: BgConfig; bg: BgConfig;
allowSetBg: boolean;
autoTheme: boolean;
}
export interface ClientConfigMsg {
clientCfg: ClientConfig;
captchaEnabled?: boolean; captchaEnabled?: boolean;
} }

View file

@ -15,16 +15,20 @@ export const resps = {
status: 200, status: 200,
statusText: "", statusText: "",
data: { data: {
siteName: "", clientCfg: {
siteDesc: "", siteName: "",
captchaEnabled: true, siteDesc: "",
bg: { bg: {
url: "clientCfg_bg_url", url: "clientCfg_bg_url",
repeat: "clientCfg_bg_repeat", repeat: "clientCfg_bg_repeat",
position: "clientCfg_bg_position", position: "clientCfg_bg_position",
align: "clientCfg_bg_align", align: "clientCfg_bg_align",
bgColor: "clientCfg_bg_bg_Color" bgColor: "clientCfg_bg_bg_Color"
},
allowSetBg: true,
autoTheme: true,
}, },
captchaEnabled: true,
}, },
}, },
reportErrorsMockResp: { reportErrorsMockResp: {

View file

@ -123,16 +123,20 @@ describe("Login", () => {
// ui // ui
expect(coreState.ui).toEqual({ expect(coreState.ui).toEqual({
isVertical: false, isVertical: false,
siteName: "", clientCfg: {
siteDesc: "", siteName: "",
captchaEnabled: true, siteDesc: "",
bg: { bg: {
url: "clientCfg_bg_url", url: "clientCfg_bg_url",
repeat: "clientCfg_bg_repeat", repeat: "clientCfg_bg_repeat",
position: "clientCfg_bg_position", position: "clientCfg_bg_position",
align: "clientCfg_bg_align", align: "clientCfg_bg_align",
bgColor: "clientCfg_bg_bg_Color", bgColor: "clientCfg_bg_bg_Color",
},
allowSetBg: true,
autoTheme: true,
}, },
captchaEnabled: true,
control: { control: {
controls: Map<string, string>({ controls: Map<string, string>({
[panelTabs]: "filesPanel", [panelTabs]: "filesPanel",

View file

@ -142,7 +142,7 @@ describe("State Manager", () => {
expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US")); expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US"));
// ui // ui
expect(coreState.ui.bg).toEqual(settingsResps.getClientCfgMockResp.data.bg); expect(coreState.ui.clientCfg.bg).toEqual(settingsResps.getClientCfgMockResp.data.clientCfg.bg);
}); });
test("initUpdater for visitor in sharing mode", async () => { test("initUpdater for visitor in sharing mode", async () => {
@ -257,7 +257,7 @@ describe("State Manager", () => {
expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US")); expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US"));
// ui // ui
expect(coreState.ui.bg).toEqual(settingsResps.getClientCfgMockResp.data.bg); expect(coreState.ui.clientCfg.bg).toEqual(settingsResps.getClientCfgMockResp.data.clientCfg.bg);
}); });
test("initUpdater for visitor", async () => { test("initUpdater for visitor", async () => {
@ -352,7 +352,7 @@ describe("State Manager", () => {
expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US")); expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US"));
// ui // ui
expect(coreState.ui.bg).toEqual({ expect(coreState.ui.clientCfg.bg).toEqual({
align: "clientCfg_bg_align", align: "clientCfg_bg_align",
position: "clientCfg_bg_position", position: "clientCfg_bg_position",
repeat: "clientCfg_bg_repeat", repeat: "clientCfg_bg_repeat",

View file

@ -2,7 +2,7 @@ import { List, Set, Map } from "immutable";
import { UploadEntry } from "../worker/interface"; import { UploadEntry } from "../worker/interface";
import { MsgPackage } from "../i18n/msger"; import { MsgPackage } from "../i18n/msger";
import { User, MetadataResp } from "../client"; import { User, MetadataResp, ClientConfig } from "../client";
import { FilesProps } from "./panel_files"; import { FilesProps } from "./panel_files";
import { UploadingsProps } from "./panel_uploadings"; import { UploadingsProps } from "./panel_uploadings";
import { SharingsProps } from "./panel_sharings"; import { SharingsProps } from "./panel_sharings";
@ -25,17 +25,9 @@ export interface MsgProps {
} }
export interface UIProps { export interface UIProps {
isVertical: boolean; clientCfg: ClientConfig;
siteName: string;
siteDesc: string;
captchaEnabled: boolean; captchaEnabled: boolean;
bg: { isVertical: boolean;
url: string;
repeat: string;
position: string;
align: string;
bgColor: string;
};
control: { control: {
controls: Map<string, string>; controls: Map<string, string>;
options: Map<string, Set<string>>; options: Map<string, Set<string>>;
@ -120,14 +112,18 @@ export function initState(): ICoreState {
}, },
ui: { ui: {
isVertical: isVertical(), isVertical: isVertical(),
siteName: "", clientCfg: {
siteDesc: "", siteName: "",
bg: { siteDesc: "",
url: "", bg: {
repeat: "", url: "",
position: "", repeat: "",
align: "", position: "",
bgColor: "", align: "",
bgColor: "",
},
allowSetBg: false,
autoTheme: true,
}, },
captchaEnabled: true, captchaEnabled: true,
control: { control: {

View file

@ -575,6 +575,19 @@ export class AdminPane extends React.Component<Props, State, {}> {
return ( return (
<div className="font-m"> <div className="font-m">
<Container>
<Flexbox
children={List([
<h5 className="title-m">{this.props.msg.pkg.get("siteSettings")}</h5>,
<button onClick={this.addUser} className="button-default">
{this.props.msg.pkg.get("update")}
</button>,
])}
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/>
</Container>
<Container> <Container>
<BgCfg <BgCfg
ui={this.props.ui} ui={this.props.ui}
@ -711,53 +724,60 @@ interface BgProps {
update?: (updater: (prevState: ICoreState) => ICoreState) => void; update?: (updater: (prevState: ICoreState) => ICoreState) => void;
} }
interface BgState {} interface BgState { }
export class BgCfg extends React.Component<BgProps, BgState, {}> { export class BgCfg extends React.Component<BgProps, BgState, {}> {
changeSiteName = (ev: React.ChangeEvent<HTMLInputElement>) => { onChangeSiteName = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ ...this.props.ui, siteName: ev.target.value }); updater().setClientCfg({ ...this.props.ui.clientCfg, siteName: ev.target.value });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
onChangeSiteDesc = (ev: React.ChangeEvent<HTMLInputElement>) => {
changeSiteDesc = (ev: React.ChangeEvent<HTMLInputElement>) => { updater().setClientCfg({ ...this.props.ui.clientCfg, siteDesc: ev.target.value });
updater().setClientCfg({ ...this.props.ui, siteDesc: ev.target.value }); this.props.update(updater().updateUI);
};
onChangeAllowSetBg = (enabled: boolean) => {
updater().setClientCfg({ ...this.props.ui.clientCfg, allowSetBg: enabled });
this.props.update(updater().updateUI);
};
onChangeAutoTheme = (enabled: boolean) => {
updater().setClientCfg({ ...this.props.ui.clientCfg, autoTheme: enabled });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
changeBgUrl = (ev: React.ChangeEvent<HTMLInputElement>) => { changeBgUrl = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ updater().setClientCfg({
...this.props.ui, ...this.props.ui.clientCfg,
bg: { ...this.props.ui.bg, url: ev.target.value }, bg: { ...this.props.ui.clientCfg.bg, url: ev.target.value },
}); });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
changeBgRepeat = (ev: React.ChangeEvent<HTMLInputElement>) => { changeBgRepeat = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ updater().setClientCfg({
...this.props.ui, ...this.props.ui.clientCfg,
bg: { ...this.props.ui.bg, repeat: ev.target.value }, bg: { ...this.props.ui.clientCfg.bg, repeat: ev.target.value },
}); });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
changeBgPos = (ev: React.ChangeEvent<HTMLInputElement>) => { changeBgPos = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ updater().setClientCfg({
...this.props.ui, ...this.props.ui.clientCfg,
bg: { ...this.props.ui.bg, position: ev.target.value }, bg: { ...this.props.ui.clientCfg.bg, position: ev.target.value },
}); });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
changeBgAlign = (ev: React.ChangeEvent<HTMLInputElement>) => { changeBgAlign = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ updater().setClientCfg({
...this.props.ui, ...this.props.ui.clientCfg,
bg: { ...this.props.ui.bg, align: ev.target.value }, bg: { ...this.props.ui.clientCfg.bg, align: ev.target.value },
}); });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
changeBgBgColor = (ev: React.ChangeEvent<HTMLInputElement>) => { changeBgBgColor = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ updater().setClientCfg({
...this.props.ui, ...this.props.ui.clientCfg,
bg: { ...this.props.ui.bg, bgColor: ev.target.value }, bg: { ...this.props.ui.clientCfg.bg, bgColor: ev.target.value },
}); });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
@ -772,13 +792,13 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
}; };
setClientCfg = async () => { setClientCfg = async () => {
const bgURL = this.props.ui.bg.url; const bgURL = this.props.ui.clientCfg.bg.url;
if (bgURL.length >= 4096) { if (bgURL.length >= 4096) {
Env().alertMsg(this.props.msg.pkg.get("bg.url.alert")); Env().alertMsg(this.props.msg.pkg.get("bg.url.alert"));
return; return;
} }
const bgRepeat = this.props.ui.bg.repeat; const bgRepeat = this.props.ui.clientCfg.bg.repeat;
if ( if (
bgRepeat !== "repeat-x" && bgRepeat !== "repeat-x" &&
bgRepeat !== "repeat-y" && bgRepeat !== "repeat-y" &&
@ -791,7 +811,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
return; return;
} }
const bgPos = this.props.ui.bg.position; const bgPos = this.props.ui.clientCfg.bg.position;
if ( if (
bgPos !== "top" && bgPos !== "top" &&
bgPos !== "bottom" && bgPos !== "bottom" &&
@ -803,7 +823,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
return; return;
} }
const bgAlign = this.props.ui.bg.align; const bgAlign = this.props.ui.clientCfg.bg.align;
if (bgAlign !== "scroll" && bgAlign !== "fixed" && bgAlign !== "local") { if (bgAlign !== "scroll" && bgAlign !== "fixed" && bgAlign !== "local") {
Env().alertMsg(this.props.msg.pkg.get("bg.align.alert")); Env().alertMsg(this.props.msg.pkg.get("bg.align.alert"));
return; return;
@ -813,9 +833,8 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
try { try {
const status = await updater().setClientCfgRemote({ const status = await updater().setClientCfgRemote({
siteName: this.props.ui.siteName, clientCfg: this.props.ui.clientCfg,
siteDesc: this.props.ui.siteDesc, // captchaEnabled is omitted
bg: this.props.ui.bg,
}); });
if (status !== "") { if (status !== "") {
Env().alertMsg(this.props.msg.pkg.get("update.fail")); Env().alertMsg(this.props.msg.pkg.get("update.fail"));
@ -840,6 +859,8 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
align: "fixed", align: "fixed",
bgColor: "", bgColor: "",
}, },
allowSetBg: false,
autoTheme: true,
}); });
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
}; };
@ -871,6 +892,68 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
<div className="hr"></div> <div className="hr"></div>
<span className="inline-block margin-r-m">
<div className="label">{this.props.msg.pkg.get("siteName")}</div>
<input
type="text"
onChange={this.onChangeSiteName}
value={this.props.ui.clientCfg.siteName}
placeholder={this.props.msg.pkg.get("siteName")}
/>
</span>
<span className="inline-block margin-r-m">
<div className="label">{this.props.msg.pkg.get("siteDesc")}</div>
<input
type="text"
onChange={this.onChangeSiteDesc}
value={this.props.ui.clientCfg.siteDesc}
placeholder={this.props.msg.pkg.get("siteDesc")}
/>
</span>
<div>
<div className="label">{this.props.msg.pkg.get("allowSetBg")}</div>
<button
onClick={() => {
this.onChangeAllowSetBg(true);
}}
className="button-default inline-block margin-r-m"
>
{this.props.msg.pkg.get("term.enabled")}
</button>
<button
onClick={() => {
this.onChangeAllowSetBg(false);
}}
className="button-default inline-block margin-r-m"
>
{this.props.msg.pkg.get("term.disabled")}
</button>
</div>
<div>
<div className="label">{this.props.msg.pkg.get("autoTheme")}</div>
<button
onClick={() => {
this.onChangeAutoTheme(true);
}}
className="button-default inline-block margin-r-m"
>
{this.props.msg.pkg.get("term.enabled")}
</button>
<button
onClick={() => {
this.onChangeAutoTheme(false);
}}
className="button-default inline-block margin-r-m"
>
{this.props.msg.pkg.get("term.disabled")}
</button>
</div>
<div className="hr"></div>
<div> <div>
<div className="inline-block margin-r-m"> <div className="inline-block margin-r-m">
<div className="label">{this.props.msg.pkg.get("cfg.bg.url")}</div> <div className="label">{this.props.msg.pkg.get("cfg.bg.url")}</div>
@ -878,7 +961,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_url" name="bg_url"
type="text" type="text"
onChange={this.changeBgUrl} onChange={this.changeBgUrl}
value={this.props.ui.bg.url} value={this.props.ui.clientCfg.bg.url}
style={{ width: "20rem" }} style={{ width: "20rem" }}
placeholder={this.props.msg.pkg.get("cfg.bg.url")} placeholder={this.props.msg.pkg.get("cfg.bg.url")}
/> />
@ -892,7 +975,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_repeat" name="bg_repeat"
type="text" type="text"
onChange={this.changeBgRepeat} onChange={this.changeBgRepeat}
value={this.props.ui.bg.repeat} value={this.props.ui.clientCfg.bg.repeat}
placeholder={this.props.msg.pkg.get("cfg.bg.repeat")} placeholder={this.props.msg.pkg.get("cfg.bg.repeat")}
/> />
</div> </div>
@ -903,7 +986,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_pos" name="bg_pos"
type="text" type="text"
onChange={this.changeBgPos} onChange={this.changeBgPos}
value={this.props.ui.bg.position} value={this.props.ui.clientCfg.bg.position}
placeholder={this.props.msg.pkg.get("cfg.bg.pos")} placeholder={this.props.msg.pkg.get("cfg.bg.pos")}
/> />
</div> </div>
@ -916,7 +999,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_align" name="bg_align"
type="text" type="text"
onChange={this.changeBgAlign} onChange={this.changeBgAlign}
value={this.props.ui.bg.align} value={this.props.ui.clientCfg.bg.align}
placeholder={this.props.msg.pkg.get("cfg.bg.align")} placeholder={this.props.msg.pkg.get("cfg.bg.align")}
/> />
</div> </div>
@ -929,12 +1012,12 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_bgColor" name="bg_bgColor"
type="text" type="text"
onChange={this.changeBgBgColor} onChange={this.changeBgBgColor}
value={this.props.ui.bg.bgColor} value={this.props.ui.clientCfg.bg.bgColor}
placeholder={this.props.msg.pkg.get("cfg.bg.bgColor")} placeholder={this.props.msg.pkg.get("cfg.bg.bgColor")}
/> />
</div> </div>
</div> </div>
</div> </div >
); );
} }
} }

View file

@ -24,7 +24,7 @@ export interface Props {
update?: (updater: (prevState: ICoreState) => ICoreState) => void; update?: (updater: (prevState: ICoreState) => ICoreState) => void;
} }
export interface State {} export interface State { }
export class RootFrame extends React.Component<Props, State, {}> { export class RootFrame extends React.Component<Props, State, {}> {
constructor(p: Props) { constructor(p: Props) {
super(p); super(p);
@ -47,15 +47,15 @@ export class RootFrame extends React.Component<Props, State, {}> {
}; };
} }
if (this.props.ui.bg.url !== "") { if (this.props.ui.clientCfg.bg.url !== "") {
return { return {
background: `url("${this.props.ui.bg.url}") ${this.props.ui.bg.repeat} ${this.props.ui.bg.position} ${this.props.ui.bg.align}`, background: `url("${this.props.ui.clientCfg.bg.url}") ${this.props.ui.clientCfg.bg.repeat} ${this.props.ui.clientCfg.bg.position} ${this.props.ui.clientCfg.bg.align}`,
}; };
} }
if (this.props.ui.bg.bgColor !== "") { if (this.props.ui.clientCfg.bg.bgColor !== "") {
return { return {
backgroundColor: this.props.ui.bg.bgColor, backgroundColor: this.props.ui.clientCfg.bg.bgColor,
}; };
} }

View file

@ -19,6 +19,7 @@ import {
roleAdmin, roleAdmin,
visitorID, visitorID,
ClientConfigMsg, ClientConfigMsg,
ClientConfig,
Preferences, Preferences,
} from "../client"; } from "../client";
import { FilesClient, shareIDQuery, shareDirQuery } from "../client/files"; import { FilesClient, shareIDQuery, shareDirQuery } from "../client/files";
@ -561,6 +562,8 @@ export class Updater {
return initClientCfgStatus; return initClientCfgStatus;
} }
console.log(this.props.ui.control.controls.toJSON());
this.initControls(paramMap); this.initControls(paramMap);
this.initUITree(); this.initUITree();
@ -816,12 +819,10 @@ export class Updater {
return resp.status === 200 ? "" : errServer; return resp.status === 200 ? "" : errServer;
}; };
setClientCfg = (cfg: ClientConfigMsg) => { setClientCfg = (cfg: ClientConfig) => {
this.props.ui = { this.props.ui = {
...this.props.ui, ...this.props.ui,
siteName: cfg.siteName, clientCfg: cfg,
siteDesc: cfg.siteDesc,
bg: cfg.bg,
}; };
}; };
@ -841,11 +842,9 @@ export class Updater {
if (resp.status !== 200) { if (resp.status !== 200) {
return errServer; return errServer;
} }
const clientCfg = resp.data as ClientConfigMsg; const clientCfgMsg = resp.data as ClientConfigMsg;
this.props.ui.siteName = clientCfg.siteName; this.props.ui.clientCfg = clientCfgMsg.clientCfg;
this.props.ui.siteDesc = clientCfg.siteDesc; this.props.ui.captchaEnabled = clientCfgMsg.captchaEnabled;
this.props.ui.bg = clientCfg.bg;
this.props.ui.captchaEnabled = clientCfg.captchaEnabled;
return ""; return "";
}; };

View file

@ -146,4 +146,11 @@ export const msgs: Map<string, string> = Map({
theme: "Theme", theme: "Theme",
"theme.light": "Light", "theme.light": "Light",
"theme.dark": "Dark", "theme.dark": "Dark",
"siteSettings": "Site Settings",
"siteName": "Site Name",
"siteDesc": "Site Description",
"allowSetBg": "Allow user to customize background",
"autoTheme": "Enable auto theme switching",
"term.enabled": "Enabled",
"term.disabled": "Disabled",
}); });

View file

@ -143,4 +143,11 @@ export const msgs: Map<string, string> = Map({
theme: "主题", theme: "主题",
"theme.light": "光白", "theme.light": "光白",
"theme.dark": "暗黑", "theme.dark": "暗黑",
"siteSettings": "网站设置",
"siteName": "网站名",
"siteDesc": "网站描述",
"allowSetBg": "允许用户自定义背景",
"autoTheme": "自动切换主题",
"term.enabled": "启用",
"term.disabled": "关闭",
}); });

View file

@ -29,7 +29,7 @@ func (h *SettingsSvc) Health(c *gin.Context) {
} }
type ClientCfgMsg struct { type ClientCfgMsg struct {
ClientCfg *db.ClientConfig `json:"cfg"` ClientCfg *db.ClientConfig `json:"clientCfg"`
CaptchaEnabled bool `json:"captchaEnabled"` CaptchaEnabled bool `json:"captchaEnabled"`
} }