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;
}
export interface ClientConfigMsg {
export interface ClientConfig {
siteName: string;
siteDesc: string;
bg: BgConfig;
allowSetBg: boolean;
autoTheme: boolean;
}
export interface ClientConfigMsg {
clientCfg: ClientConfig;
captchaEnabled?: boolean;
}

View file

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

View file

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

View file

@ -142,7 +142,7 @@ describe("State Manager", () => {
expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US"));
// 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 () => {
@ -257,7 +257,7 @@ describe("State Manager", () => {
expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US"));
// 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 () => {
@ -352,7 +352,7 @@ describe("State Manager", () => {
expect(coreState.msg.pkg).toEqual(MsgPackage.get("en_US"));
// ui
expect(coreState.ui.bg).toEqual({
expect(coreState.ui.clientCfg.bg).toEqual({
align: "clientCfg_bg_align",
position: "clientCfg_bg_position",
repeat: "clientCfg_bg_repeat",

View file

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

View file

@ -575,6 +575,19 @@ export class AdminPane extends React.Component<Props, State, {}> {
return (
<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>
<BgCfg
ui={this.props.ui}
@ -713,51 +726,58 @@ interface BgProps {
interface BgState { }
export class BgCfg extends React.Component<BgProps, BgState, {}> {
changeSiteName = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ ...this.props.ui, siteName: ev.target.value });
onChangeSiteName = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ ...this.props.ui.clientCfg, siteName: ev.target.value });
this.props.update(updater().updateUI);
};
changeSiteDesc = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ ...this.props.ui, siteDesc: ev.target.value });
onChangeSiteDesc = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ ...this.props.ui.clientCfg, 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);
};
changeBgUrl = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({
...this.props.ui,
bg: { ...this.props.ui.bg, url: ev.target.value },
...this.props.ui.clientCfg,
bg: { ...this.props.ui.clientCfg.bg, url: ev.target.value },
});
this.props.update(updater().updateUI);
};
changeBgRepeat = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({
...this.props.ui,
bg: { ...this.props.ui.bg, repeat: ev.target.value },
...this.props.ui.clientCfg,
bg: { ...this.props.ui.clientCfg.bg, repeat: ev.target.value },
});
this.props.update(updater().updateUI);
};
changeBgPos = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({
...this.props.ui,
bg: { ...this.props.ui.bg, position: ev.target.value },
...this.props.ui.clientCfg,
bg: { ...this.props.ui.clientCfg.bg, position: ev.target.value },
});
this.props.update(updater().updateUI);
};
changeBgAlign = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({
...this.props.ui,
bg: { ...this.props.ui.bg, align: ev.target.value },
...this.props.ui.clientCfg,
bg: { ...this.props.ui.clientCfg.bg, align: ev.target.value },
});
this.props.update(updater().updateUI);
};
changeBgBgColor = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({
...this.props.ui,
bg: { ...this.props.ui.bg, bgColor: ev.target.value },
...this.props.ui.clientCfg,
bg: { ...this.props.ui.clientCfg.bg, bgColor: ev.target.value },
});
this.props.update(updater().updateUI);
};
@ -772,13 +792,13 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
};
setClientCfg = async () => {
const bgURL = this.props.ui.bg.url;
const bgURL = this.props.ui.clientCfg.bg.url;
if (bgURL.length >= 4096) {
Env().alertMsg(this.props.msg.pkg.get("bg.url.alert"));
return;
}
const bgRepeat = this.props.ui.bg.repeat;
const bgRepeat = this.props.ui.clientCfg.bg.repeat;
if (
bgRepeat !== "repeat-x" &&
bgRepeat !== "repeat-y" &&
@ -791,7 +811,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
return;
}
const bgPos = this.props.ui.bg.position;
const bgPos = this.props.ui.clientCfg.bg.position;
if (
bgPos !== "top" &&
bgPos !== "bottom" &&
@ -803,7 +823,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
return;
}
const bgAlign = this.props.ui.bg.align;
const bgAlign = this.props.ui.clientCfg.bg.align;
if (bgAlign !== "scroll" && bgAlign !== "fixed" && bgAlign !== "local") {
Env().alertMsg(this.props.msg.pkg.get("bg.align.alert"));
return;
@ -813,9 +833,8 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
try {
const status = await updater().setClientCfgRemote({
siteName: this.props.ui.siteName,
siteDesc: this.props.ui.siteDesc,
bg: this.props.ui.bg,
clientCfg: this.props.ui.clientCfg,
// captchaEnabled is omitted
});
if (status !== "") {
Env().alertMsg(this.props.msg.pkg.get("update.fail"));
@ -840,6 +859,8 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
align: "fixed",
bgColor: "",
},
allowSetBg: false,
autoTheme: true,
});
this.props.update(updater().updateUI);
};
@ -871,6 +892,68 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
<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 className="inline-block margin-r-m">
<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"
type="text"
onChange={this.changeBgUrl}
value={this.props.ui.bg.url}
value={this.props.ui.clientCfg.bg.url}
style={{ width: "20rem" }}
placeholder={this.props.msg.pkg.get("cfg.bg.url")}
/>
@ -892,7 +975,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_repeat"
type="text"
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")}
/>
</div>
@ -903,7 +986,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_pos"
type="text"
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")}
/>
</div>
@ -916,7 +999,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_align"
type="text"
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")}
/>
</div>
@ -929,7 +1012,7 @@ export class BgCfg extends React.Component<BgProps, BgState, {}> {
name="bg_bgColor"
type="text"
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")}
/>
</div>

View file

@ -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 {
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 {
backgroundColor: this.props.ui.bg.bgColor,
backgroundColor: this.props.ui.clientCfg.bg.bgColor,
};
}

View file

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

View file

@ -146,4 +146,11 @@ export const msgs: Map<string, string> = Map({
theme: "Theme",
"theme.light": "Light",
"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.light": "光白",
"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 {
ClientCfg *db.ClientConfig `json:"cfg"`
ClientCfg *db.ClientConfig `json:"clientCfg"`
CaptchaEnabled bool `json:"captchaEnabled"`
}