feat(settings): enable background settings

This commit is contained in:
hexxa 2021-10-09 21:14:15 +08:00 committed by Hexxa
parent b151c1b56e
commit f151f47581
13 changed files with 229 additions and 17 deletions

View file

@ -42,7 +42,7 @@ site:
siteName: "Quickshare"
siteDesc: "quick and simple file sharing"
bg:
url: "/static/img/textured_paper.png"
url: "/v1/fs/files?fp=qs/files/Screen%20Shot%202021-08-07%20at%2020.03.08%20PM.png"
repeat: "repeat"
position: "fixed"
align: "center"

View file

@ -25,7 +25,9 @@ export class SettingsClient extends BaseClient {
return this.do({
method: "patch",
url: `${this.url}/v1/settings/client`,
data: cfg,
data: {
clientCfg: cfg,
},
});
};
}

View file

@ -3,10 +3,11 @@ import { List, Map, Set } from "immutable";
import FileSize from "filesize";
import { alertMsg, confirmMsg } from "../common/env";
import { ICoreState, MsgProps } from "./core_state";
import { ICoreState, MsgProps, UIProps } from "./core_state";
import { User, Quota } from "../client";
import { updater } from "./state_updater";
import { Flexbox } from "./layout/flexbox";
import { Flowgrid } from "./layout/flowgrid";
export interface AdminProps {
users: Map<string, User>;
@ -15,6 +16,7 @@ export interface AdminProps {
export interface Props {
admin: AdminProps;
ui: UIProps;
msg: MsgProps;
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
}
@ -458,6 +460,14 @@ export class AdminPane extends React.Component<Props, State, {}> {
return (
<div className="font-size-m">
<div className="container padding-l">
<BgCfg
ui={this.props.ui}
msg={this.props.msg}
update={this.props.update}
/>
</div>
<div className="container padding-l">
<Flexbox
children={List([
@ -581,3 +591,160 @@ export class AdminPane extends React.Component<Props, State, {}> {
);
}
}
interface BgProps {
msg: MsgProps;
ui: UIProps;
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
}
interface BgState {}
export class BgCfg extends React.Component<BgProps, BgState, {}> {
changeSiteName = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ ...this.props.ui, siteName: ev.target.value });
this.props.update(updater().updateUI);
};
changeSiteDesc = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({ ...this.props.ui, siteDesc: ev.target.value });
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.update(updater().updateUI);
};
changeBgRepeat = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({
...this.props.ui,
bg: { ...this.props.ui.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.update(updater().updateUI);
};
changeBgAlign = (ev: React.ChangeEvent<HTMLInputElement>) => {
updater().setClientCfg({
...this.props.ui,
bg: { ...this.props.ui.bg, align: ev.target.value },
});
this.props.update(updater().updateUI);
};
constructor(p: BgProps) {
super(p);
}
setClientCfg = async () => {
return updater().setClientCfgRemote({
siteName: this.props.ui.siteName,
siteDesc: this.props.ui.siteDesc,
bg: this.props.ui.bg,
});
};
resetClientCfg = () => {
// TODO: move this to backend
updater().setClientCfg({
siteName: "Quickshare",
siteDesc: "Quickshare",
bg: {
url: "/static/img/textured_paper.png",
repeat: "repeat",
position: "fixed",
align: "center",
},
});
this.props.update(updater().updateUI);
};
render() {
return (
<div>
<Flexbox
children={List([
<span className="title-m bold">
{this.props.msg.pkg.get("cfg.bg")}
</span>,
<span>
<button onClick={this.resetClientCfg} className="margin-r-m">
{this.props.msg.pkg.get("reset")}
</button>
<button onClick={this.setClientCfg}>
{this.props.msg.pkg.get("update")}
</button>
</span>,
])}
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/>
<Flowgrid
grids={List([
<div className="padding-t-m padding-b-m padding-r-m">
<div className="font-size-s grey1-font">
{this.props.msg.pkg.get("cfg.bg.url")}
</div>
<input
name="bg_url"
type="text"
onChange={this.changeBgUrl}
value={this.props.ui.bg.url}
className="black0-font"
placeholder={this.props.msg.pkg.get("cfg.bg.url")}
/>
</div>,
<div className="padding-t-m padding-b-m padding-r-m">
<div className="font-size-s grey1-font">
{this.props.msg.pkg.get("cfg.bg.repeat")}
</div>
<input
name="bg_repeat"
type="text"
onChange={this.changeBgRepeat}
value={this.props.ui.bg.repeat}
className="black0-font"
placeholder={this.props.msg.pkg.get("cfg.bg.repeat")}
/>
</div>,
<div className="padding-t-m padding-b-m padding-r-m">
<div className="font-size-s grey1-font">
{this.props.msg.pkg.get("cfg.bg.pos")}
</div>
<input
name="bg_pos"
type="text"
onChange={this.changeBgPos}
value={this.props.ui.bg.position}
className="black0-font"
placeholder={this.props.msg.pkg.get("cfg.bg.pos")}
/>
</div>,
<div className="padding-t-m padding-b-m padding-r-m">
<div className="font-size-s grey1-font">
{this.props.msg.pkg.get("cfg.bg.align")}
</div>
<input
name="bg_align"
type="text"
onChange={this.changeBgAlign}
value={this.props.ui.bg.align}
className="black0-font"
placeholder={this.props.msg.pkg.get("cfg.bg.align")}
/>
</div>,
])}
/>
</div>
);
}
}

View file

@ -78,6 +78,7 @@ export class AuthPane extends React.Component<Props, State, {}> {
this.update(updater().updateLogin);
this.update(updater().updatePanes);
this.update(updater().updateAdmin);
this.update(updater().updateUI);
updater().initLan();
this.update(updater().updateMsg);

View file

@ -1,15 +1,16 @@
import * as React from "react";
import FileSize from "filesize";
import { List } from "immutable";
import { ICoreState, MsgProps } from "./core_state";
import { LoginProps } from "./pane_login";
import { Flexbox } from "./layout/flexbox";
import { updater } from "./state_updater";
import { alertMsg } from "../common/env";
import { List } from "immutable";
export interface Props {
login: LoginProps;
msg: MsgProps;
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
}
@ -20,7 +21,6 @@ export interface State {
}
export class PaneSettings extends React.Component<Props, State, {}> {
private update: (updater: (prevState: ICoreState) => ICoreState) => void;
changeOldPwd = (ev: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ oldPwd: ev.target.value });
};
@ -33,7 +33,6 @@ export class PaneSettings extends React.Component<Props, State, {}> {
constructor(p: Props) {
super(p);
this.update = p.update;
this.state = {
oldPwd: "",
newPwd1: "",
@ -130,9 +129,7 @@ export class PaneSettings extends React.Component<Props, State, {}> {
</span>
</div>
</div>
<div className="hr white0-bg margin-t-m margin-b-m"></div>
<div>
<Flexbox
children={List([
@ -188,9 +185,7 @@ export class PaneSettings extends React.Component<Props, State, {}> {
/>
</span>
</div>
<div className="hr white0-bg margin-t-m margin-b-m"></div>
<div className="margin-b-m">
<Flexbox
children={List([

View file

@ -3,7 +3,7 @@ import { Set, List } from "immutable";
import { updater } from "./state_updater";
import { Flexbox } from "./layout/flexbox";
import { ICoreState, MsgProps } from "./core_state";
import { ICoreState, MsgProps, UIProps } from "./core_state";
import { PaneSettings } from "./pane_settings";
import { AdminPane, AdminProps } from "./pane_admin";
import { AuthPane, LoginProps } from "./pane_login";
@ -16,6 +16,7 @@ export interface Props {
panes: PanesProps;
login: LoginProps;
admin: AdminProps;
ui: UIProps;
msg: MsgProps;
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
}
@ -88,6 +89,7 @@ export class Panes extends React.Component<Props, State, {}> {
<div className={`${showAdmin}`}>
<AdminPane
admin={this.props.admin}
ui={this.props.ui}
msg={this.props.msg}
update={this.props.update}
/>

View file

@ -44,6 +44,7 @@ export class RootFrame extends React.Component<Props, State, {}> {
panes={this.props.panes}
login={this.props.login}
admin={this.props.admin}
ui={this.props.ui}
msg={this.props.msg}
update={this.props.update}
/>

View file

@ -64,6 +64,7 @@ export class StateMgr extends React.Component<Props, State, {}> {
this.update(updater().updateLogin);
this.update(updater().updatePanes);
this.update(updater().updateAdmin);
this.update(updater().updateUI);
updater().initLan();
this.update(updater().updateMsg);

View file

@ -350,6 +350,10 @@ export class Updater {
.then(() => {
return this.isSharing(this.props.browser.dirPath.join("/"));
})
.then(() => {
// init settings
return this.getClientCfg();
})
.then(() => {
// init panes
return this.initPanes();
@ -561,11 +565,20 @@ export class Updater {
return resp.status === 200;
};
setClientCfg = async (cfg: ClientConfig): Promise<number> => {
setClientCfgRemote = async (cfg: ClientConfig): Promise<number> => {
const resp = await this.settingsClient.setClientCfg(cfg);
return resp.status;
};
setClientCfg = async (cfg: ClientConfig): Promise<void> => {
this.props.ui = {
...this.props.ui,
siteName: cfg.siteName,
siteDesc: cfg.siteDesc,
bg: cfg.bg,
};
};
getClientCfg = async (): Promise<number> => {
const resp = await this.settingsClient.getClientCfg();
if (resp.status === 200) {
@ -612,6 +625,13 @@ export class Updater {
msg: { ...prevState.msg, ...this.props.msg },
};
};
updateUI = (prevState: ICoreState): ICoreState => {
return {
...prevState,
ui: { ...prevState.ui, ...this.props.ui },
};
};
}
export let coreUpdater = new Updater();

View file

@ -8,7 +8,7 @@ import { PanesProps } from "./panes";
import { updater } from "./state_updater";
import { Flexbox } from "./layout/flexbox";
export interface State { }
export interface State {}
export interface Props {
login: LoginProps;
panes: PanesProps;
@ -51,7 +51,9 @@ export class TopBar extends React.Component<Props, State, {}> {
.logout()
.then((ok: boolean) => {
if (ok) {
const params = new URLSearchParams(document.location.search.substring(1));
const params = new URLSearchParams(
document.location.search.substring(1)
);
return updater().initAll(params);
} else {
alertMsg(this.props.msg.pkg.get("login.logout.fail"));
@ -65,6 +67,7 @@ export class TopBar extends React.Component<Props, State, {}> {
this.props.update(updater().updateLogin);
this.props.update(updater().updatePanes);
this.props.update(updater().updateAdmin);
this.props.update(updater().updateUI);
updater().initLan();
this.props.update(updater().updateMsg);
@ -82,7 +85,9 @@ export class TopBar extends React.Component<Props, State, {}> {
render() {
const showUserInfo = this.props.login.authed ? "" : "hidden";
const showLogin = this.props.login.authed ? "" : "hidden";
const showSettings = this.props.panes.paneNames.get("settings") ? "" : "hidden";
const showSettings = this.props.panes.paneNames.get("settings")
? ""
: "hidden";
const showAdmin = this.props.panes.paneNames.get("admin") ? "" : "hidden";
return (

View file

@ -87,4 +87,12 @@ export const msgs: Map<string, string> = Map({
"user.downLimit": "Download Speed Limit",
"user.upLimit": "Upload Speed Limit",
"user.spaceLimit": "Space Limit",
"cfg.siteName": "Site Name",
"cfg.siteDesc": "Site Description",
"cfg.bg": "Background",
"cfg.bg.url": "Background URL",
"cfg.bg.repeat": "Repeat",
"cfg.bg.pos": "Position",
"cfg.bg.align": "Align",
reset: "Reset",
});

View file

@ -79,11 +79,19 @@ export const msgs: Map<string, string> = Map({
"pane.admin": "管理",
"pane.settings": "设置",
"logout.confirm": "确定登出吗?",
"unauthed": "未授权动作",
unauthed: "未授权动作",
"err.tooManyUploads": "不可同时上传超过1000个文件",
"login.role": "角色",
"user.profile": "用户信息",
"user.downLimit": "下载速度限制",
"user.upLimit": "上传速度限制",
"user.spaceLimit": "空间限制",
"cfg.siteName": "站点名字",
"cfg.siteDesc": "站点描述",
"cfg.bg": "背景设置",
"cfg.bg.url": "图片链接",
"cfg.bg.repeat": "重复",
"cfg.bg.pos": "位置",
"cfg.bg.align": "对齐",
reset: "重置",
});

View file

@ -30,7 +30,7 @@ func (h *SettingsSvc) Health(c *gin.Context) {
}
type ClientCfgMsg struct {
ClientCfg *sitestore.ClientConfig
ClientCfg *sitestore.ClientConfig `json:"clientCfg"`
}
func (h *SettingsSvc) GetClientCfg(c *gin.Context) {
@ -51,6 +51,8 @@ func (h *SettingsSvc) SetClientCfg(c *gin.Context) {
c.JSON(q.ErrResp(c, 400, err))
return
}
h.deps.Log().Info(req.ClientCfg)
if err = validateClientCfg(req.ClientCfg); err != nil {
c.JSON(q.ErrResp(c, 400, err))
return