fix(fe/panels): replace rows built-in sorting with common sorting

This commit is contained in:
hexxa 2022-02-26 23:07:43 +08:00 committed by Hexxa
parent 4896fd47fc
commit 05d8adb6d5
13 changed files with 336 additions and 130 deletions

View file

@ -1,6 +1,11 @@
import { List, Map } from "immutable";
import { Row } from "../components/layout/rows";
export interface Row {
// elem: React.ReactNode; // element to display
val: Object; // original object value
sortVals: List<string>; // sortable values in order
}
export function getItemPath(dirPath: string, itemName: string): string {
return dirPath.endsWith("/")

View file

@ -0,0 +1,42 @@
import * as React from "react";
import { List } from "immutable";
import { Flexbox } from "../layout/flexbox";
import { getIconWithProps } from "../visual/icons";
export type BtnListCallBack = () => void;
export interface Props {
titleIcon?: string;
btnNames: List<string>;
btnCallbacks: List<BtnListCallBack>;
}
export const BtnList = (props: Props) => {
const titleIcon =
props.titleIcon != null ? (
getIconWithProps(props.titleIcon, {
size: "3rem",
className: "black-font margin-r-m",
})
) : (
<span></span>
);
const btns = props.btnNames.map((btnName: string, i: number) => {
const cb = props.btnCallbacks.get(i);
return (
<button key={`rows-${i}`} className="float" onClick={cb}>
{btnName}
</button>
);
});
return (
<div className="margin-b-l">
<Flexbox
children={List([titleIcon, <span>{btns}</span>])}
childrenStyles={List([{ flex: "0 0 auto" }, { flex: "0 0 auto" }])}
/>
</div>
);
};

View file

@ -1,14 +1,11 @@
import * as React from "react";
import { List, Map, update } from "immutable";
import { List, Map } from "immutable";
import { updater } from "../state_updater";
import { getIcon } from "../visual/icons";
import { Flexbox } from "../layout/flexbox";
import { ICoreState, MsgProps, UIProps } from "../core_state";
import { AdminProps } from "../pane_admin";
import { LoginProps } from "../pane_login";
import { alertMsg } from "../../common/env";
import { IconProps } from "../visual/icons";
import { IconProps, getIcon } from "../visual/icons";
import { colorClass } from "../visual/colors";
const defaultIconProps: IconProps = {
@ -20,8 +17,7 @@ const defaultIconProps: IconProps = {
export interface Props {
targetControl: string;
tabIcons: Map<string, IconProps>; // option name -> icon name
login: LoginProps;
admin: AdminProps;
titleIcon?: string;
ui: UIProps;
msg: MsgProps;
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
@ -45,6 +41,10 @@ export class Tabs extends React.Component<Props, State, {}> {
this.props.targetControl
);
const titleIcon =
this.props.titleIcon != null
? getIcon(this.props.titleIcon, "2rem", "black")
: null;
const options = this.props.ui.control.options.get(this.props.targetControl);
const tabs = options.map((option: string) => {
const iconProps = this.props.tabIcons.has(option)
@ -66,6 +66,7 @@ export class Tabs extends React.Component<Props, State, {}> {
>
<Flexbox
children={List([
<span>{titleIcon}</span>,
<span className="margin-r-s">{icon}</span>,
<span className={fontColor}>
{this.props.msg.pkg.get(

View file

@ -55,18 +55,29 @@ export function newState(): ICoreState {
}
export function initState(): ICoreState {
const defaultLanPackage = MsgPackage.get("en_US")
const filesOrderBy = defaultLanPackage.get("item.name");
const uploadingsOrderBy = defaultLanPackage.get("item.path");
const sharingsOrderBy = defaultLanPackage.get("item.path");
return {
filesInfo: {
dirPath: List<string>([]),
items: List<MetadataResp>([]),
isSharing: false,
orderBy: filesOrderBy,
order: true,
},
uploadingsInfo: {
uploadings: List<UploadEntry>([]),
uploadFiles: List<File>([]),
orderBy: uploadingsOrderBy,
order: true,
},
sharingsInfo: {
sharings: Map<string, string>(),
orderBy: sharingsOrderBy,
order: true,
},
admin: {
users: Map<string, User>(),
@ -100,7 +111,7 @@ export function initState(): ICoreState {
},
msg: {
lan: "en_US",
pkg: MsgPackage.get("en_US"),
pkg: defaultLanPackage,
},
ui: {
isVertical: isVertical(),

View file

@ -52,8 +52,6 @@ export class SettingsDialog extends React.Component<Props, State, {}> {
color: "cyan1",
},
})}
login={this.props.login}
admin={this.props.admin}
ui={this.props.ui}
msg={this.props.msg}
update={this.props.update}

View file

@ -13,86 +13,86 @@ export interface Row {
}
export interface Props {
sortKeys: List<string>; // display names in order for sorting
rows: List<Row>;
// sortKeys: List<string>; // display names in order for sorting
rows: List<React.ReactNode>;
id?: string;
style?: React.CSSProperties;
className?: string;
updateRows?: (rows: Object) => void; // this is a callback which update state with re-sorted rows
// updateRows?: (rows: Object) => void; // this is a callback which update state with re-sorted rows
}
export interface State {
orders: List<boolean>; // asc = true, desc = false
}
export interface State {}
// // orders: List<boolean>; // asc = true, desc = false
// }
export class Rows extends React.Component<Props, State, {}> {
constructor(p: Props) {
super(p);
this.state = {
orders: p.sortKeys.map((_: string, i: number) => {
return false;
}),
};
// this.state = {
// orders: p.sortKeys.map((_: string, i: number) => {
// return false;
// }),
// };
}
sortRows = (key: number) => {
if (this.props.updateRows == null) {
return;
}
const sortOption = this.props.sortKeys.get(key);
if (sortOption == null) {
return;
}
const currentOrder = this.state.orders.get(key);
if (currentOrder == null) {
return;
}
const expectedOrder = !currentOrder;
// sortRows = (key: number) => {
// if (this.props.updateRows == null) {
// return;
// }
// const sortOption = this.props.sortKeys.get(key);
// if (sortOption == null) {
// return;
// }
// const currentOrder = this.state.orders.get(key);
// if (currentOrder == null) {
// return;
// }
// const expectedOrder = !currentOrder;
const sortedRows = sortRows(this.props.rows, key, expectedOrder);
const sortedItems = sortedRows.map((row: Row): Object => {
return row.val;
});
const newOrders = this.state.orders.set(key, !currentOrder);
this.setState({ orders: newOrders });
this.props.updateRows(sortedItems);
};
// const sortedRows = sortRows(this.props.rows, key, expectedOrder);
// const sortedItems = sortedRows.map((row: Row): Object => {
// return row.val;
// });
// const newOrders = this.state.orders.set(key, !currentOrder);
// this.setState({ orders: newOrders });
// this.props.updateRows(sortedItems);
// };
render() {
const sortBtns = this.props.sortKeys.map(
(displayName: string, i: number): React.ReactNode => {
return (
<button
key={`rows-${i}`}
className="float"
onClick={() => {
this.sortRows(i);
}}
>
{displayName}
</button>
);
}
);
// const sortBtns = this.props.sortKeys.map(
// (displayName: string, i: number): React.ReactNode => {
// return (
// <button
// key={`rows-${i}`}
// className="float"
// onClick={() => {
// this.sortRows(i);
// }}
// >
// {displayName}
// </button>
// );
// }
// );
const bodyRows = this.props.rows.map(
(row: Row, i: number): React.ReactNode => {
return <div key={`rows-r-${i}`}>{row.elem}</div>;
(row: React.ReactNode, i: number): React.ReactNode => {
return <div key={`rows-r-${i}`}>{row}</div>;
}
);
const orderByList =
sortBtns.size > 0 ? (
<div className="margin-b-l">
<Flexbox
children={List([
<BiSortUp size="3rem" className="black-font margin-r-m" />,
<span>{sortBtns}</span>,
])}
childrenStyles={List([{ flex: "0 0 auto" }, { flex: "0 0 auto" }])}
/>
</div>
) : null;
// const orderByList =
// sortBtns.size > 0 ? (
// <div className="margin-b-l">
// <Flexbox
// children={List([
// <BiSortUp size="3rem" className="black-font margin-r-m" />,
// <span>{sortBtns}</span>,
// ])}
// childrenStyles={List([{ flex: "0 0 auto" }, { flex: "0 0 auto" }])}
// />
// </div>
// ) : null;
return (
<div
@ -100,7 +100,7 @@ export class Rows extends React.Component<Props, State, {}> {
style={this.props.style}
className={this.props.className}
>
{orderByList}
{/* {orderByList} */}
{bodyRows}
</div>
);

View file

@ -168,8 +168,8 @@ export class PaneSettings extends React.Component<Props, State, {}> {
}
};
prepareErrorRows = (): List<Row> => {
let errRows = List<Row>();
prepareErrorRows = (): List<React.ReactNode> => {
let errRows = List<React.ReactNode>();
ErrorLogger()
.readErrs()
@ -180,14 +180,10 @@ export class PaneSettings extends React.Component<Props, State, {}> {
<div className="hr"></div>
</div>
);
const val = clientErr;
const sortVals = List<string>([]);
// const val = clientErr;
// const sortVals = List<string>([]);
errRows = errRows.push({
elem,
val,
sortVals,
});
errRows = errRows.push(elem);
});
return errRows;
@ -218,7 +214,7 @@ export class PaneSettings extends React.Component<Props, State, {}> {
<div className="hr"></div>
<Rows rows={errRows} sortKeys={List([])} />
<Rows rows={errRows}/>
</Container>
) : null;

View file

@ -23,8 +23,9 @@ import { MetadataResp, roleVisitor, roleAdmin } from "../client";
import { Flexbox } from "./layout/flexbox";
import { Container } from "./layout/container";
import { Table, Cell, Head } from "./layout/table";
import { BtnList } from "./control/btn_list";
import { Segments } from "./layout/segments";
import { Rows, Row } from "./layout/rows";
import { Rows } from "./layout/rows";
import { Up } from "../worker/upload_mgr";
import { UploadEntry, UploadState } from "../worker/interface";
import { getIcon } from "./visual/icons";
@ -51,6 +52,8 @@ export interface FilesProps {
dirPath: List<string>;
isSharing: boolean;
items: List<MetadataResp>;
orderBy: string;
order: boolean;
}
export interface Props {
@ -602,12 +605,7 @@ export class FilesPanel extends React.Component<Props, State, {}> {
? "hidden"
: "";
const sortKeys = List<string>([
this.props.msg.pkg.get("item.type"),
this.props.msg.pkg.get("item.name"),
]);
const rows = sortedItems.map((item: MetadataResp): Row => {
const rows = sortedItems.map((item: MetadataResp): React.ReactNode => {
const isSelected = this.state.selectedItems.has(item.name);
const dirPath = this.props.filesInfo.dirPath.join("/");
const itemPath = dirPath.endsWith("/")
@ -753,21 +751,16 @@ export class FilesPanel extends React.Component<Props, State, {}> {
</div>
);
const sortVals = List<string>([item.isDir ? "d" : "f", itemPath]);
return {
elem,
sortVals,
val: item,
};
// const sortVals = List<string>([item.isDir ? "d" : "f", itemPath]);
return elem;
// return {
// elem,
// sortVals,
// val: item,
// };
});
return (
<Rows
sortKeys={sortKeys}
rows={List(rows)}
updateRows={this.updateItems}
/>
);
return <Rows rows={List(rows)} />;
};
setView = (opt: string) => {
@ -779,6 +772,11 @@ export class FilesPanel extends React.Component<Props, State, {}> {
this.props.update(updater().updateUI);
};
orderBy = (columnName: string) => {
updater().sortFiles(columnName);
this.props.update(updater().updateFilesInfo);
};
render() {
const showEndpoints =
this.props.login.userRole === roleAdmin ? "" : "hidden";
@ -867,6 +865,28 @@ export class FilesPanel extends React.Component<Props, State, {}> {
</div>
);
const orderByCallbacks = List([
() => {
this.orderBy(this.props.msg.pkg.get("item.name"));
},
() => {
this.orderBy(this.props.msg.pkg.get("item.type"));
},
() => {
this.orderBy(this.props.msg.pkg.get("item.modTime"));
},
]);
const orderByButtons = (
<BtnList
titleIcon="BiSortUp"
btnNames={List([
this.props.msg.pkg.get("item.name"),
this.props.msg.pkg.get("item.type"),
this.props.msg.pkg.get("item.modTime"),
])}
btnCallbacks={orderByCallbacks}
/>
);
const viewType = this.props.ui.control.controls.get(filesViewCtrl);
const view =
viewType === "rows" ? (
@ -1012,7 +1032,7 @@ export class FilesPanel extends React.Component<Props, State, {}> {
/>
<div className="hr grey0-bg"></div>
{orderByButtons}
{view}
</Container>
</div>

View file

@ -4,6 +4,7 @@ import { List, Map } from "immutable";
import { RiShareBoxLine } from "@react-icons/all-files/ri/RiShareBoxLine";
import { RiCloudOffFill } from "@react-icons/all-files/ri/RiCloudOffFill";
import { BtnList } from "./control/btn_list";
import { QRCodeIcon } from "./visual/qrcode";
import { getErrMsg } from "../common/utils";
import { alertMsg, confirmMsg } from "../common/env";
@ -19,6 +20,8 @@ import { CronTable } from "../common/cron";
export interface SharingsProps {
sharings: Map<string, string>;
orderBy: string;
order: boolean;
}
export interface Props {
@ -86,8 +89,8 @@ export class SharingsPanel extends React.Component<Props, State, {}> {
}
};
makeRows = (sharings: Map<string, string>): List<Row> => {
const sharingRows = sharings.keySeq().map((dirPath: string) => {
makeRows = (sharings: Map<string, string>): List<React.ReactNode> => {
const sharingRows = sharings.keySeq().map((dirPath: string):React.ReactNode => {
const shareID = sharings.get(dirPath);
const sharingURL = `${
document.location.href.split("?")[0]
@ -130,11 +133,12 @@ export class SharingsPanel extends React.Component<Props, State, {}> {
</div>
);
return {
elem,
sortVals: List([dirPath]),
val: dirPath,
};
return elem;
// return {
// elem,
// sortVals: List([dirPath]),
// val: dirPath,
// };
});
return sharingRows.toList();
@ -151,13 +155,31 @@ export class SharingsPanel extends React.Component<Props, State, {}> {
this.props.update(updater().updateSharingsInfo);
};
orderBy = (columnName: string) => {
updater().sortSharings(columnName);
this.props.update(updater().updateSharingsInfo);
};
render() {
const orderByCallbacks = List([
() => {
this.orderBy(this.props.msg.pkg.get("item.path"));
},
]);
const orderByButtons = (
<BtnList
titleIcon="BiSortUp"
btnNames={List([this.props.msg.pkg.get("item.path")])}
btnCallbacks={orderByCallbacks}
/>
);
const sharingRows = this.makeRows(this.props.sharingsInfo.sharings);
const view = (
<Rows
rows={sharingRows}
sortKeys={List([this.props.msg.pkg.get("item.path")])}
updateRows={this.updateSharings}
// sortKeys={List([this.props.msg.pkg.get("item.path")])}
// updateRows={this.updateSharings}
/>
);
const noSharingView = (
@ -212,6 +234,7 @@ export class SharingsPanel extends React.Component<Props, State, {}> {
className="margin-b-l"
/>
{orderByButtons}
{view}
</Container>
);

View file

@ -5,6 +5,7 @@ import FileSize from "filesize";
import { RiUploadCloudFill } from "@react-icons/all-files/ri/RiUploadCloudFill";
import { RiCloudOffFill } from "@react-icons/all-files/ri/RiCloudOffFill";
import { BtnList } from "./control/btn_list";
import { alertMsg } from "../common/env";
import { getErrMsg } from "../common/utils";
import { updater } from "./state_updater";
@ -20,6 +21,8 @@ import { HotkeyHandler } from "../common/hotkeys";
export interface UploadingsProps {
uploadings: List<UploadEntry>;
uploadFiles: List<File>;
orderBy: string;
order: boolean;
}
export interface Props {
uploadingsInfo: UploadingsProps;
@ -85,8 +88,8 @@ export class UploadingsPanel extends React.Component<Props, State, {}> {
this.props.update(updater().updateUploadingsInfo);
};
makeRowsInputs = (uploadings: List<UploadEntry>): List<Row> => {
const uploadingRows = uploadings.map((uploading: UploadEntry) => {
makeRowsInputs = (uploadings: List<UploadEntry>): List<React.ReactNode> => {
return uploadings.map((uploading: UploadEntry) => {
const pathParts = uploading.filePath.split("/");
const fileName = pathParts[pathParts.length - 1];
const progress =
@ -171,15 +174,9 @@ export class UploadingsPanel extends React.Component<Props, State, {}> {
);
// file path
const sortVals = List<string>([uploading.filePath]);
return {
elem,
sortVals,
val: uploading,
};
// const sortVals = List<string>([uploading.filePath]);
return elem;
});
return uploadingRows;
};
updateUploadings = (uploadings: Object) => {
@ -188,16 +185,34 @@ export class UploadingsPanel extends React.Component<Props, State, {}> {
this.props.update(updater().updateUploadingsInfo);
};
orderBy = (columnName: string) => {
updater().sortUploadings(columnName);
this.props.update(updater().updateUploadingsInfo);
};
render() {
const orderByCallbacks = List([
() => {
this.orderBy(this.props.msg.pkg.get("item.path"));
},
]);
const orderByButtons = (
<BtnList
titleIcon="BiSortUp"
btnNames={List([this.props.msg.pkg.get("item.path")])}
btnCallbacks={orderByCallbacks}
/>
);
const uploadingRows = this.makeRowsInputs(
this.props.uploadingsInfo.uploadings
);
const sortKeys = List([this.props.msg.pkg.get("item.path")]);
// const sortKeys = List([this.props.msg.pkg.get("item.path")]);
const view = (
<Rows
sortKeys={sortKeys}
// sortKeys={sortKeys}
rows={uploadingRows}
updateRows={this.updateUploadings}
// updateRows={this.updateUploadings}
/>
);
@ -254,6 +269,7 @@ export class UploadingsPanel extends React.Component<Props, State, {}> {
])}
/>
{orderByButtons}
{view}
</Container>
);

View file

@ -101,8 +101,6 @@ export class RootFrame extends React.Component<Props, State, {}> {
color: "cyan1",
},
})}
login={this.props.login}
admin={this.props.admin}
ui={this.props.ui}
msg={this.props.msg}
update={this.props.update}

View file

@ -1,7 +1,7 @@
import { List, Map, Set } from "immutable";
import { ICoreState } from "./core_state";
import { getItemPath, sortRows } from "../common/utils";
import { getItemPath, sortRows, Row } from "../common/utils";
import {
User,
ListUsersResp,
@ -241,7 +241,7 @@ export class Updater {
const status = await this.setItems(this.props.filesInfo.dirPath);
if (status !== "") {
return status;
};
}
// TODO: this part is duplicated in the panel_files.tsx
const sortKeys = List<string>([
@ -865,6 +865,100 @@ export class Updater {
return "";
};
setFilesOrderBy = (orderBy: string, order: boolean) => {
this.props.filesInfo.orderBy = orderBy;
this.props.filesInfo.order = order;
};
setUploadingsOrderBy = (orderBy: string, order: boolean) => {
this.props.uploadingsInfo.orderBy = orderBy;
this.props.uploadingsInfo.order = order;
};
setSharingsOrderBy = (orderBy: string, order: boolean) => {
this.props.sharingsInfo.orderBy = orderBy;
this.props.sharingsInfo.order = order;
};
sortFiles = (columnName: string) => {
let orderByKey = 0;
switch (columnName) {
case this.props.msg.pkg.get("item.name"):
orderByKey = 0;
break;
case this.props.msg.pkg.get("item.type"):
orderByKey = 1;
break;
default:
orderByKey = 2;
}
const order =
this.props.filesInfo.orderBy === columnName
? !this.props.filesInfo.order
: true;
const rows = this.props.filesInfo.items.map((item: MetadataResp): Row => {
return {
val: item,
sortVals: List([item.name, item.isDir ? "d" : "f", item.modTime]),
};
});
const sortedFiles = sortRows(rows, orderByKey, order).map(
(row): MetadataResp => {
return row.val as MetadataResp;
}
);
this.setFilesOrderBy(columnName, order);
this.updateItems(sortedFiles);
};
sortUploadings = (columnName: string) => {
const orderByKey = 0;
const order = !this.props.uploadingsInfo.order;
const rows = this.props.uploadingsInfo.uploadings.map(
(uploading: UploadEntry): Row => {
return {
val: uploading,
sortVals: List([uploading.filePath]),
};
}
);
const sorted = sortRows(rows, orderByKey, order).map((row) => {
return row.val as UploadEntry;
});
this.setUploadingsOrderBy(columnName, order);
this.updateUploadings(sorted);
};
sortSharings = (columnName: string) => {
const orderByKey = 0;
const order = !this.props.sharingsInfo.order;
const rows = this.props.sharingsInfo.sharings
.keySeq()
.map((sharingPath: string): Row => {
return {
val: sharingPath,
sortVals: List([sharingPath]),
};
})
.toList();
let sorted = Map<string, string>();
sortRows(rows, orderByKey, order).forEach((row) => {
const sharingPath = row.val as string;
sorted = sorted.set(
sharingPath,
this.props.sharingsInfo.sharings.get(sharingPath)
);
});
this.setSharingsOrderBy(columnName, order);
this.updateSharings(sorted);
};
updateAll = (prevState: ICoreState): ICoreState => {
return {
filesInfo: { ...prevState.filesInfo, ...this.props.filesInfo },

View file

@ -19,6 +19,7 @@ import { BiTable } from "@react-icons/all-files/bi/BiTable";
import { BiListUl } from "@react-icons/all-files/bi/BiListUl";
import { RiMore2Fill } from "@react-icons/all-files/ri/RiMore2Fill";
import { RiCheckboxBlankLine } from "@react-icons/all-files/ri/RiCheckboxBlankLine";
import { BiSortUp } from "@react-icons/all-files/bi/BiSortUp";
import { colorClass } from "./colors";
@ -46,6 +47,7 @@ const icons = Map<string, IconType>({
BiListUl: BiListUl,
RiMore2Fill: RiMore2Fill,
RiCheckboxBlankLine: RiCheckboxBlankLine,
BiSortUp: BiSortUp,
});
export function getIconWithProps(