From 47587812b809fee2a95c76266d9d0e42fc4ac1ca Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 15 Jun 2021 14:14:28 +0800 Subject: ... --- FrontEnd/src/views/admin/UserAdmin.tsx | 396 +++++++++++++++++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 FrontEnd/src/views/admin/UserAdmin.tsx (limited to 'FrontEnd/src/views/admin/UserAdmin.tsx') diff --git a/FrontEnd/src/views/admin/UserAdmin.tsx b/FrontEnd/src/views/admin/UserAdmin.tsx new file mode 100644 index 00000000..4e9cd600 --- /dev/null +++ b/FrontEnd/src/views/admin/UserAdmin.tsx @@ -0,0 +1,396 @@ +import React, { useState, useEffect } from "react"; +import classnames from "classnames"; +import { ListGroup, Row, Col, Spinner, Button } from "react-bootstrap"; + +import OperationDialog, { + OperationDialogBoolInput, +} from "../common/OperationDialog"; + +import { AuthUser } from "@/services/user"; +import { + getHttpUserClient, + HttpUser, + kUserPermissionList, + UserPermission, +} from "http/user"; +import { Trans, useTranslation } from "react-i18next"; + +interface DialogProps { + open: boolean; + close: () => void; + data: TData; + onSuccess: (data: TReturn) => void; +} + +const CreateUserDialog: React.FC> = ({ + open, + close, + onSuccess, +}) => { + return ( + + getHttpUserClient().post({ + username, + password, + }) + } + close={close} + open={open} + onSuccessAndClose={onSuccess} + /> + ); +}; + +const UsernameLabel: React.FC = (props) => { + return {props.children}; +}; + +const UserDeleteDialog: React.FC> = + ({ open, close, data: { username }, onSuccess }) => { + return ( + ( + + 0{username}2 + + )} + onProcess={() => getHttpUserClient().delete(username)} + onSuccessAndClose={onSuccess} + /> + ); + }; + +const UserModifyDialog: React.FC< + DialogProps< + { + oldUser: HttpUser; + }, + HttpUser + > +> = ({ open, close, data: { oldUser }, onSuccess }) => { + return ( + ( + + 0{oldUser.username}2 + + )} + inputScheme={ + [ + { + type: "text", + label: "admin:user.username", + initValue: oldUser.username, + }, + { type: "text", label: "admin:user.password" }, + { + type: "text", + label: "admin:user.nickname", + initValue: oldUser.nickname, + }, + ] as const + } + onProcess={([username, password, nickname]) => + getHttpUserClient().patch(oldUser.username, { + username: username !== oldUser.username ? username : undefined, + password: password !== "" ? password : undefined, + nickname: nickname !== oldUser.nickname ? nickname : undefined, + }) + } + onSuccessAndClose={onSuccess} + /> + ); +}; + +const UserPermissionModifyDialog: React.FC< + DialogProps< + { + username: string; + permissions: UserPermission[]; + }, + UserPermission[] + > +> = ({ open, close, data: { username, permissions }, onSuccess }) => { + const oldPermissionBoolList: boolean[] = kUserPermissionList.map( + (permission) => permissions.includes(permission) + ); + + return ( + ( + + 0{username}2 + + )} + inputScheme={kUserPermissionList.map( + (permission, index) => ({ + type: "bool", + label: permission, + initValue: oldPermissionBoolList[index], + }) + )} + onProcess={async (newPermissionBoolList): Promise => { + for (let index = 0; index < kUserPermissionList.length; index++) { + const oldValue = oldPermissionBoolList[index]; + const newValue = newPermissionBoolList[index]; + const permission = kUserPermissionList[index]; + if (oldValue === newValue) continue; + if (newValue) { + await getHttpUserClient().putUserPermission(username, permission); + } else { + await getHttpUserClient().deleteUserPermission( + username, + permission + ); + } + } + return newPermissionBoolList; + }} + onSuccessAndClose={(newPermissionBoolList: boolean[]) => { + const permissions: UserPermission[] = []; + for (let index = 0; index < kUserPermissionList.length; index++) { + if (newPermissionBoolList[index]) { + permissions.push(kUserPermissionList[index]); + } + } + onSuccess(permissions); + }} + /> + ); +}; + +const kModify = "modify"; +const kModifyPermission = "permission"; +const kDelete = "delete"; + +type TModify = typeof kModify; +type TModifyPermission = typeof kModifyPermission; +type TDelete = typeof kDelete; + +type ContextMenuItem = TModify | TModifyPermission | TDelete; + +interface UserItemProps { + on: { [key in ContextMenuItem]: () => void }; + user: HttpUser; +} + +const UserItem: React.FC = ({ user, on }) => { + const { t } = useTranslation(); + + const [editMaskVisible, setEditMaskVisible] = React.useState(false); + + return ( + + setEditMaskVisible(true)} + /> +

{user.username}

+
+ {t("admin:user.nickname")} + {user.nickname} +
+
+ {t("admin:user.uniqueId")} + {user.uniqueId} +
+
+ {t("admin:user.permissions")} + {user.permissions.map((permission) => { + return ( + + {permission}{" "} + + ); + })} +
+
setEditMaskVisible(false)} + > + + + +
+
+ ); +}; + +interface UserAdminProps { + user: AuthUser; +} + +const UserAdmin: React.FC = () => { + const { t } = useTranslation(); + + type DialogInfo = + | null + | { + type: "create"; + } + | { + type: TModify; + user: HttpUser; + } + | { + type: TModifyPermission; + username: string; + permissions: UserPermission[]; + } + | { type: TDelete; username: string }; + + const [users, setUsers] = useState(null); + const [dialog, setDialog] = useState(null); + const [usersVersion, setUsersVersion] = useState(0); + const updateUsers = (): void => { + setUsersVersion(usersVersion + 1); + }; + + useEffect(() => { + let subscribe = true; + void getHttpUserClient() + .list() + .then((us) => { + if (subscribe) { + setUsers(us); + } + }); + return () => { + subscribe = false; + }; + }, [usersVersion]); + + let dialogNode: React.ReactNode; + if (dialog) { + switch (dialog.type) { + case "create": + dialogNode = ( + setDialog(null)} + data={undefined} + onSuccess={updateUsers} + /> + ); + break; + case kDelete: + dialogNode = ( + setDialog(null)} + data={{ username: dialog.username }} + onSuccess={updateUsers} + /> + ); + break; + case kModify: + dialogNode = ( + setDialog(null)} + data={{ oldUser: dialog.user }} + onSuccess={updateUsers} + /> + ); + break; + case kModifyPermission: + dialogNode = ( + setDialog(null)} + data={{ + username: dialog.username, + permissions: dialog.permissions, + }} + onSuccess={updateUsers} + /> + ); + break; + } + } + + if (users) { + const userComponents = users.map((user) => { + return ( + { + setDialog({ + type: "modify", + user, + }); + }, + permission: () => { + setDialog({ + type: kModifyPermission, + username: user.username, + permissions: user.permissions, + }); + }, + delete: () => { + setDialog({ + type: "delete", + username: user.username, + }); + }, + }} + /> + ); + }); + + return ( + <> + + + + + + {userComponents} + {dialogNode} + + ); + } else { + return ; + } +}; + +export default UserAdmin; -- cgit v1.2.3 From e0b766203d7576ab67b16ba556ba14120d0bc876 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 15 Jun 2021 15:02:49 +0800 Subject: ... --- BackEnd/Timeline/FrontEndMode.cs | 1 - BackEnd/Timeline/Properties/launchSettings.json | 2 +- BackEnd/Timeline/Startup.cs | 4 --- FrontEnd/index.html | 29 ++++++++++++++++++++++ FrontEnd/package.json | 5 ++-- FrontEnd/postcss.config.js | 11 +++----- FrontEnd/src/index.ejs | 29 ---------------------- FrontEnd/src/services/TimelinePostBuilder.ts | 4 +-- FrontEnd/src/services/timeline.ts | 4 +-- FrontEnd/src/services/user.ts | 6 ++--- FrontEnd/src/tsconfig.json | 23 ----------------- FrontEnd/src/views/admin/UserAdmin.tsx | 2 +- FrontEnd/src/views/center/CenterBoards.tsx | 6 ++--- FrontEnd/src/views/center/TimelineBoard.tsx | 4 +-- FrontEnd/src/views/center/TimelineCreateDialog.tsx | 2 +- FrontEnd/src/views/common/user/UserAvatar.tsx | 2 +- FrontEnd/src/views/home/TimelineListView.tsx | 4 +-- FrontEnd/src/views/home/index.tsx | 4 +-- FrontEnd/src/views/search/index.tsx | 10 ++++---- FrontEnd/src/views/settings/ChangeAvatarDialog.tsx | 2 +- .../src/views/settings/ChangeNicknameDialog.tsx | 2 +- .../timeline-common/ConnectionStatusBadge.tsx | 2 +- .../src/views/timeline-common/MarkdownPostEdit.tsx | 2 +- .../timeline-common/PostPropertyChangeDialog.tsx | 2 +- FrontEnd/src/views/timeline-common/Timeline.tsx | 6 ++--- .../src/views/timeline-common/TimelineMember.tsx | 8 +++--- .../timeline-common/TimelinePageCardTemplate.tsx | 4 +-- .../views/timeline-common/TimelinePageTemplate.tsx | 4 +-- .../timeline-common/TimelinePagedPostListView.tsx | 2 +- .../timeline-common/TimelinePostContentView.tsx | 4 +-- .../src/views/timeline-common/TimelinePostEdit.tsx | 6 ++--- .../views/timeline-common/TimelinePostListView.tsx | 2 +- .../src/views/timeline-common/TimelinePostView.tsx | 2 +- .../TimelinePropertyChangeDialog.tsx | 2 +- FrontEnd/src/views/timeline/TimelineCard.tsx | 2 +- .../src/views/timeline/TimelineDeleteDialog.tsx | 2 +- FrontEnd/src/views/user/index.tsx | 2 +- FrontEnd/tsconfig.json | 25 +++++++++++++++++++ FrontEnd/vite.config.js | 3 +++ 39 files changed, 116 insertions(+), 120 deletions(-) create mode 100644 FrontEnd/index.html delete mode 100644 FrontEnd/src/index.ejs delete mode 100644 FrontEnd/src/tsconfig.json create mode 100644 FrontEnd/tsconfig.json (limited to 'FrontEnd/src/views/admin/UserAdmin.tsx') diff --git a/BackEnd/Timeline/FrontEndMode.cs b/BackEnd/Timeline/FrontEndMode.cs index 63503292..eb718028 100644 --- a/BackEnd/Timeline/FrontEndMode.cs +++ b/BackEnd/Timeline/FrontEndMode.cs @@ -4,7 +4,6 @@ { Disable, Mock, - Proxy, Normal } } diff --git a/BackEnd/Timeline/Properties/launchSettings.json b/BackEnd/Timeline/Properties/launchSettings.json index f683ca2d..7f68b709 100644 --- a/BackEnd/Timeline/Properties/launchSettings.json +++ b/BackEnd/Timeline/Properties/launchSettings.json @@ -4,7 +4,7 @@ "commandName": "Project", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_FRONTEND": "Proxy" + "ASPNETCORE_FRONTEND": "Disable" } }, "Dev-Mock": { diff --git a/BackEnd/Timeline/Startup.cs b/BackEnd/Timeline/Startup.cs index 18097e2c..26ffb4b1 100644 --- a/BackEnd/Timeline/Startup.cs +++ b/BackEnd/Timeline/Startup.cs @@ -166,10 +166,6 @@ namespace Timeline { app.UseSpa(spa => { - if (_frontEndMode == FrontEndMode.Proxy) - { - spa.UseProxyToSpaDevelopmentServer(new UriBuilder("http", "localhost", 3000).Uri); - } }); } } diff --git a/FrontEnd/index.html b/FrontEnd/index.html new file mode 100644 index 00000000..87e19743 --- /dev/null +++ b/FrontEnd/index.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + Timeline + + + +
+ + + diff --git a/FrontEnd/package.json b/FrontEnd/package.json index 1afb5ad0..5d12899e 100644 --- a/FrontEnd/package.json +++ b/FrontEnd/package.json @@ -33,8 +33,9 @@ "xregexp": "^5.0.2" }, "scripts": { - "start": "webpack serve --config ./webpack.config.dev.js", - "build": "webpack --config ./webpack.config.prod.js", + "start": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "eslint src/ --ext .js --ext .jsx --ext .ts --ext .tsx", "lint:fix": "eslint src/ --ext .js --ext .jsx --ext .ts --ext .tsx --fix" }, diff --git a/FrontEnd/postcss.config.js b/FrontEnd/postcss.config.js index 74ee8155..9129aa1f 100644 --- a/FrontEnd/postcss.config.js +++ b/FrontEnd/postcss.config.js @@ -1,10 +1,5 @@ +import postcssPresetEnv from "postcss-preset-env"; + module.exports = { - plugins: [ - [ - "postcss-preset-env", - { - // Options - }, - ], - ], + plugins: [postcssPresetEnv()], }; diff --git a/FrontEnd/src/index.ejs b/FrontEnd/src/index.ejs deleted file mode 100644 index c2ff4182..00000000 --- a/FrontEnd/src/index.ejs +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - <%= htmlWebpackPlugin.options.title %> - - - -
- - - diff --git a/FrontEnd/src/services/TimelinePostBuilder.ts b/FrontEnd/src/services/TimelinePostBuilder.ts index fe4c7a9a..40279eca 100644 --- a/FrontEnd/src/services/TimelinePostBuilder.ts +++ b/FrontEnd/src/services/TimelinePostBuilder.ts @@ -2,8 +2,8 @@ import { Remarkable } from "remarkable"; import { UiLogicError } from "@/common"; -import { base64 } from "http/common"; -import { HttpTimelinePostPostRequest } from "http/timeline"; +import { base64 } from "@/http/common"; +import { HttpTimelinePostPostRequest } from "@/http/timeline"; export default class TimelinePostBuilder { private _onChange: () => void; diff --git a/FrontEnd/src/services/timeline.ts b/FrontEnd/src/services/timeline.ts index 4ebb705d..d8c0ae00 100644 --- a/FrontEnd/src/services/timeline.ts +++ b/FrontEnd/src/services/timeline.ts @@ -1,9 +1,9 @@ -import { TimelineVisibility } from "http/timeline"; +import { TimelineVisibility } from "@/http/timeline"; import XRegExp from "xregexp"; import { Observable } from "rxjs"; import { HubConnectionBuilder, HubConnectionState } from "@microsoft/signalr"; -import { getHttpToken } from "http/common"; +import { getHttpToken } from "@/http/common"; const timelineNameReg = XRegExp("^[-_\\p{L}]*$", "u"); diff --git a/FrontEnd/src/services/user.ts b/FrontEnd/src/services/user.ts index 3375c88a..9a8e5687 100644 --- a/FrontEnd/src/services/user.ts +++ b/FrontEnd/src/services/user.ts @@ -3,12 +3,12 @@ import { BehaviorSubject, Observable } from "rxjs"; import { UiLogicError } from "@/common"; -import { HttpNetworkError, setHttpToken } from "http/common"; +import { HttpNetworkError, setHttpToken } from "@/http/common"; import { getHttpTokenClient, HttpCreateTokenBadCredentialError, -} from "http/token"; -import { getHttpUserClient, HttpUser, UserPermission } from "http/user"; +} from "@/http/token"; +import { getHttpUserClient, HttpUser, UserPermission } from "@/http/user"; import { pushAlert } from "./alert"; diff --git a/FrontEnd/src/tsconfig.json b/FrontEnd/src/tsconfig.json deleted file mode 100644 index 817c50bd..00000000 --- a/FrontEnd/src/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react", - "types": ["vite/client"], - "sourceMap": true, - "baseUrl": "./", - "paths": { - "@/*": ["*"] - }, - "lib": ["dom", "dom.iterable", "esnext"] - }, - "include": ["."] -} diff --git a/FrontEnd/src/views/admin/UserAdmin.tsx b/FrontEnd/src/views/admin/UserAdmin.tsx index 4e9cd600..558d3aee 100644 --- a/FrontEnd/src/views/admin/UserAdmin.tsx +++ b/FrontEnd/src/views/admin/UserAdmin.tsx @@ -12,7 +12,7 @@ import { HttpUser, kUserPermissionList, UserPermission, -} from "http/user"; +} from "@/http/user"; import { Trans, useTranslation } from "react-i18next"; interface DialogProps { diff --git a/FrontEnd/src/views/center/CenterBoards.tsx b/FrontEnd/src/views/center/CenterBoards.tsx index 431d1e9a..f5200415 100644 --- a/FrontEnd/src/views/center/CenterBoards.tsx +++ b/FrontEnd/src/views/center/CenterBoards.tsx @@ -5,9 +5,9 @@ import { useTranslation } from "react-i18next"; import { pushAlert } from "@/services/alert"; import { useUserLoggedIn } from "@/services/user"; -import { getHttpTimelineClient } from "http/timeline"; -import { getHttpBookmarkClient } from "http/bookmark"; -import { getHttpHighlightClient } from "http/highlight"; +import { getHttpTimelineClient } from "@/http/timeline"; +import { getHttpBookmarkClient } from "@/http/bookmark"; +import { getHttpHighlightClient } from "@/http/highlight"; import TimelineBoard from "./TimelineBoard"; diff --git a/FrontEnd/src/views/center/TimelineBoard.tsx b/FrontEnd/src/views/center/TimelineBoard.tsx index bb80266b..840c0415 100644 --- a/FrontEnd/src/views/center/TimelineBoard.tsx +++ b/FrontEnd/src/views/center/TimelineBoard.tsx @@ -4,7 +4,7 @@ import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Spinner } from "react-bootstrap"; -import { HttpTimelineInfo } from "http/timeline"; +import { HttpTimelineInfo } from "@/http/timeline"; import TimelineLogo from "../common/TimelineLogo"; import UserTimelineLogo from "../common/UserTimelineLogo"; @@ -34,7 +34,7 @@ const TimelineBoardItem: React.FC = ({ actions, }) => { const { name, title } = timeline; - const isPersonal = name.startsWith("@"); + const isPersonal = name.startsWith("src"); const url = isPersonal ? `/users/${timeline.owner.username}` : `/timelines/${name}`; diff --git a/FrontEnd/src/views/center/TimelineCreateDialog.tsx b/FrontEnd/src/views/center/TimelineCreateDialog.tsx index a2437ae5..b4e25ba1 100644 --- a/FrontEnd/src/views/center/TimelineCreateDialog.tsx +++ b/FrontEnd/src/views/center/TimelineCreateDialog.tsx @@ -3,7 +3,7 @@ import { useHistory } from "react-router"; import { validateTimelineName } from "@/services/timeline"; import OperationDialog from "../common/OperationDialog"; -import { getHttpTimelineClient, HttpTimelineInfo } from "http/timeline"; +import { getHttpTimelineClient, HttpTimelineInfo } from "@/http/timeline"; interface TimelineCreateDialogProps { open: boolean; diff --git a/FrontEnd/src/views/common/user/UserAvatar.tsx b/FrontEnd/src/views/common/user/UserAvatar.tsx index 901697db..9e822528 100644 --- a/FrontEnd/src/views/common/user/UserAvatar.tsx +++ b/FrontEnd/src/views/common/user/UserAvatar.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { getHttpUserClient } from "http/user"; +import { getHttpUserClient } from "@/http/user"; export interface UserAvatarProps extends React.ImgHTMLAttributes { diff --git a/FrontEnd/src/views/home/TimelineListView.tsx b/FrontEnd/src/views/home/TimelineListView.tsx index 975875af..2fb54820 100644 --- a/FrontEnd/src/views/home/TimelineListView.tsx +++ b/FrontEnd/src/views/home/TimelineListView.tsx @@ -2,7 +2,7 @@ import React from "react"; import { convertI18nText, I18nText } from "@/common"; -import { HttpTimelineInfo } from "http/timeline"; +import { HttpTimelineInfo } from "@/http/timeline"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; @@ -13,7 +13,7 @@ interface TimelineListItemProps { const TimelineListItem: React.FC = ({ timeline }) => { const url = React.useMemo( () => - timeline.name.startsWith("@") + timeline.name.startsWith("src") ? `/users/${timeline.owner.username}` : `/timelines/${timeline.name}`, [timeline] diff --git a/FrontEnd/src/views/home/index.tsx b/FrontEnd/src/views/home/index.tsx index efc364d7..0eca23ee 100644 --- a/FrontEnd/src/views/home/index.tsx +++ b/FrontEnd/src/views/home/index.tsx @@ -1,8 +1,8 @@ import React from "react"; import { useHistory } from "react-router"; -import { HttpTimelineInfo } from "http/timeline"; -import { getHttpHighlightClient } from "http/highlight"; +import { HttpTimelineInfo } from "@/http/timeline"; +import { getHttpHighlightClient } from "@/http/highlight"; import SearchInput from "../common/SearchInput"; import TimelineListView from "./TimelineListView"; diff --git a/FrontEnd/src/views/search/index.tsx b/FrontEnd/src/views/search/index.tsx index 14a9709c..9a26802d 100644 --- a/FrontEnd/src/views/search/index.tsx +++ b/FrontEnd/src/views/search/index.tsx @@ -4,9 +4,9 @@ import { Container, Row } from "react-bootstrap"; import { useHistory, useLocation } from "react-router"; import { Link } from "react-router-dom"; -import { HttpNetworkError } from "http/common"; -import { getHttpSearchClient } from "http/search"; -import { HttpTimelineInfo } from "http/timeline"; +import { HttpNetworkError } from "@/http/common"; +import { getHttpSearchClient } from "@/http/search"; +import { HttpTimelineInfo } from "@/http/timeline"; import SearchInput from "../common/SearchInput"; import UserAvatar from "../common/user/UserAvatar"; @@ -14,7 +14,7 @@ import UserAvatar from "../common/user/UserAvatar"; const TimelineSearchResultItemView: React.FC<{ timeline: HttpTimelineInfo; }> = ({ timeline }) => { - const link = timeline.name.startsWith("@") + const link = timeline.name.startsWith("src") ? `users/${timeline.owner.username}` : `timelines/${timeline.name}`; @@ -33,7 +33,7 @@ const TimelineSearchResultItemView: React.FC<{ /> {timeline.owner.nickname} - @{timeline.owner.username} + src{timeline.owner.username} diff --git a/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx b/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx index 338d2112..c4f6f492 100644 --- a/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx +++ b/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx @@ -7,7 +7,7 @@ import { UiLogicError } from "@/common"; import { useUserLoggedIn } from "@/services/user"; -import { getHttpUserClient } from "http/user"; +import { getHttpUserClient } from "@/http/user"; import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper"; diff --git a/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx b/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx index e6420f36..4b44cdd6 100644 --- a/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx +++ b/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx @@ -1,4 +1,4 @@ -import { getHttpUserClient } from "http/user"; +import { getHttpUserClient } from "@/http/user"; import { useUserLoggedIn } from "@/services/user"; import React from "react"; diff --git a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx b/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx index df43d8d2..1b9d6d2a 100644 --- a/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx +++ b/FrontEnd/src/views/timeline-common/ConnectionStatusBadge.tsx @@ -1,6 +1,6 @@ import React from "react"; import classnames from "classnames"; -import { HubConnectionState } from "@microsoft/signalr"; +import { HubConnectionState } from "srcmicrosoft/signalr"; import { useTranslation } from "react-i18next"; export interface ConnectionStatusBadgeProps { diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx index 1514d28f..685e17be 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx @@ -4,7 +4,7 @@ import { Form, Spinner } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { Prompt } from "react-router"; -import { getHttpTimelineClient, HttpTimelinePostInfo } from "http/timeline"; +import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import FlatButton from "../common/FlatButton"; import TabPages from "../common/TabPages"; diff --git a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx b/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx index 21c5272e..001e52d7 100644 --- a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx +++ b/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { getHttpTimelineClient, HttpTimelinePostInfo } from "http/timeline"; +import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import OperationDialog from "../common/OperationDialog"; diff --git a/FrontEnd/src/views/timeline-common/Timeline.tsx b/FrontEnd/src/views/timeline-common/Timeline.tsx index 40619e64..31ea5870 100644 --- a/FrontEnd/src/views/timeline-common/Timeline.tsx +++ b/FrontEnd/src/views/timeline-common/Timeline.tsx @@ -1,12 +1,12 @@ import React from "react"; -import { HubConnectionState } from "@microsoft/signalr"; +import { HubConnectionState } from "srcmicrosoft/signalr"; import { HttpForbiddenError, HttpNetworkError, HttpNotFoundError, -} from "http/common"; -import { getHttpTimelineClient, HttpTimelinePostInfo } from "http/timeline"; +} from "@/http/common"; +import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import { getTimelinePostUpdate$ } from "@/services/timeline"; diff --git a/FrontEnd/src/views/timeline-common/TimelineMember.tsx b/FrontEnd/src/views/timeline-common/TimelineMember.tsx index 3d4de8b8..1ef085a7 100644 --- a/FrontEnd/src/views/timeline-common/TimelineMember.tsx +++ b/FrontEnd/src/views/timeline-common/TimelineMember.tsx @@ -4,12 +4,12 @@ import { Container, ListGroup, Modal, Row, Col, Button } from "react-bootstrap"; import { convertI18nText, I18nText } from "@/common"; -import { HttpUser } from "http/user"; -import { getHttpSearchClient } from "http/search"; +import { HttpUser } from "@/http/user"; +import { getHttpSearchClient } from "@/http/search"; import SearchInput from "../common/SearchInput"; import UserAvatar from "../common/user/UserAvatar"; -import { getHttpTimelineClient, HttpTimelineInfo } from "http/timeline"; +import { getHttpTimelineClient, HttpTimelineInfo } from "@/http/timeline"; const TimelineMemberItem: React.FC<{ user: HttpUser; @@ -27,7 +27,7 @@ const TimelineMemberItem: React.FC<{ {user.nickname} - {"@" + user.username} + {"src" + user.username} {onAction ? ( diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index 038ea3ab..623d643f 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -2,8 +2,8 @@ import React from "react"; import classnames from "classnames"; import { useTranslation } from "react-i18next"; -import { getHttpHighlightClient } from "http/highlight"; -import { getHttpBookmarkClient } from "http/bookmark"; +import { getHttpHighlightClient } from "@/http/highlight"; +import { getHttpBookmarkClient } from "@/http/bookmark"; import { useUser } from "@/services/user"; import { pushAlert } from "@/services/alert"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx index 44926cc6..658ce502 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx @@ -3,8 +3,8 @@ import { useTranslation } from "react-i18next"; import { Container } from "react-bootstrap"; import { HubConnectionState } from "@microsoft/signalr"; -import { HttpNetworkError, HttpNotFoundError } from "http/common"; -import { getHttpTimelineClient, HttpTimelineInfo } from "http/timeline"; +import { HttpNetworkError, HttpNotFoundError } from "@/http/common"; +import { getHttpTimelineClient, HttpTimelineInfo } from "@/http/timeline"; import { getAlertHost } from "@/services/alert"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx b/FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx index d569a2d7..37f02a82 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePagedPostListView.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { HttpTimelinePostInfo } from "http/timeline"; +import { HttpTimelinePostInfo } from "@/http/timeline"; import useScrollToTop from "@/utilities/useScrollToTop"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx b/FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx index f1b53335..607b72c9 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostContentView.tsx @@ -4,8 +4,8 @@ import { Remarkable } from "remarkable"; import { UiLogicError } from "@/common"; -import { HttpNetworkError } from "http/common"; -import { getHttpTimelineClient, HttpTimelinePostInfo } from "http/timeline"; +import { HttpNetworkError } from "@/http/common"; +import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import { useUser } from "@/services/user"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx index 0f470fd6..1f9f02a5 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx @@ -10,10 +10,10 @@ import { HttpTimelineInfo, HttpTimelinePostInfo, HttpTimelinePostPostRequestData, -} from "http/timeline"; +} from "@/http/timeline"; import { pushAlert } from "@/services/alert"; -import { base64 } from "http/common"; +import { base64 } from "@/http/common"; import BlobImage from "../common/BlobImage"; import LoadingButton from "../common/LoadingButton"; @@ -138,7 +138,7 @@ const TimelinePostEdit: React.FC = (props) => { (kind === "text" && text.length !== 0) || (kind === "image" && image != null); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // eslint-disable-next-line srctypescript-eslint/no-non-null-assertion const containerRef = React.useRef(null!); const notifyHeightChange = (): void => { diff --git a/FrontEnd/src/views/timeline-common/TimelinePostListView.tsx b/FrontEnd/src/views/timeline-common/TimelinePostListView.tsx index 49284720..ba204b72 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostListView.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostListView.tsx @@ -1,7 +1,7 @@ import React, { Fragment } from "react"; import classnames from "classnames"; -import { HttpTimelinePostInfo } from "http/timeline"; +import { HttpTimelinePostInfo } from "@/http/timeline"; import TimelinePostView from "./TimelinePostView"; import TimelineDateLabel from "./TimelineDateLabel"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx b/FrontEnd/src/views/timeline-common/TimelinePostView.tsx index e8b32c71..f7b81478 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostView.tsx @@ -3,7 +3,7 @@ import classnames from "classnames"; import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { getHttpTimelineClient, HttpTimelinePostInfo } from "http/timeline"; +import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import { pushAlert } from "@/services/alert"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx b/FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx index 83b24d01..70f72025 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePropertyChangeDialog.tsx @@ -6,7 +6,7 @@ import { HttpTimelinePatchRequest, kTimelineVisibilities, TimelineVisibility, -} from "http/timeline"; +} from "@/http/timeline"; import OperationDialog from "../common/OperationDialog"; diff --git a/FrontEnd/src/views/timeline/TimelineCard.tsx b/FrontEnd/src/views/timeline/TimelineCard.tsx index e031b565..86063843 100644 --- a/FrontEnd/src/views/timeline/TimelineCard.tsx +++ b/FrontEnd/src/views/timeline/TimelineCard.tsx @@ -29,7 +29,7 @@ const TimelineCard: React.FC = (props) => { /> {timeline.owner.nickname} - @{timeline.owner.username} + src{timeline.owner.username} diff --git a/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx b/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx index 8821507d..dbca62ca 100644 --- a/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx +++ b/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx @@ -2,7 +2,7 @@ import React from "react"; import { useHistory } from "react-router"; import { Trans } from "react-i18next"; -import { getHttpTimelineClient, HttpTimelineInfo } from "http/timeline"; +import { getHttpTimelineClient, HttpTimelineInfo } from "@/http/timeline"; import OperationDialog from "../common/OperationDialog"; diff --git a/FrontEnd/src/views/user/index.tsx b/FrontEnd/src/views/user/index.tsx index 57454d0d..0013b254 100644 --- a/FrontEnd/src/views/user/index.tsx +++ b/FrontEnd/src/views/user/index.tsx @@ -14,7 +14,7 @@ const UserPage: React.FC = () => { return ( <> setReloadKey(reloadKey + 1)} diff --git a/FrontEnd/tsconfig.json b/FrontEnd/tsconfig.json new file mode 100644 index 00000000..3afe2c3e --- /dev/null +++ b/FrontEnd/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react", + "noEmit": true, + "types": ["vite/client"], + "sourceMap": true, + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["./src"] +} diff --git a/FrontEnd/vite.config.js b/FrontEnd/vite.config.js index 6e8cde1b..2e85c36a 100644 --- a/FrontEnd/vite.config.js +++ b/FrontEnd/vite.config.js @@ -7,6 +7,9 @@ import { defineConfig } from "vite"; export default defineConfig({ plugins: [reactRefresh()], + resolve: { + alias: [{ find: "@", replacement: "/src" }], + }, server: { port: 13000, proxy: { -- cgit v1.2.3 From 8c56e3fd388005bcb7aced75b73d7018511ceac8 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 15 Jun 2021 16:41:03 +0800 Subject: ... --- FrontEnd/src/index.css | 53 ------------------------- FrontEnd/src/palette.ts | 43 ++++++++++---------- FrontEnd/src/views/admin/UserAdmin.tsx | 20 +++++----- FrontEnd/src/views/center/TimelineBoard.tsx | 3 -- FrontEnd/src/views/common/button/FlatButton.tsx | 2 +- FrontEnd/src/views/common/button/TextButton.css | 36 +++++++++++++++++ FrontEnd/src/views/common/button/TextButton.tsx | 36 +++++++++++++++++ 7 files changed, 105 insertions(+), 88 deletions(-) create mode 100644 FrontEnd/src/views/common/button/TextButton.css create mode 100644 FrontEnd/src/views/common/button/TextButton.tsx (limited to 'FrontEnd/src/views/admin/UserAdmin.tsx') diff --git a/FrontEnd/src/index.css b/FrontEnd/src/index.css index ca0d4829..ff8c1866 100644 --- a/FrontEnd/src/index.css +++ b/FrontEnd/src/index.css @@ -81,59 +81,6 @@ textarea { color: #ffc107; } -.text-button { - background: transparent; - border: none; -} -.text-button.primary { - color: #0d6efd; -} -.text-button.primary:hover { - color: #599bfe; -} -.text-button.secondary { - color: #6c757d; -} -.text-button.secondary:hover { - color: #939ba2; -} -.text-button.success { - color: #198754; -} -.text-button.success:hover { - color: #25c87c; -} -.text-button.info { - color: #0dcaf0; -} -.text-button.info:hover { - color: #54dbf6; -} -.text-button.warning { - color: #ffc107; -} -.text-button.warning:hover { - color: #ffd454; -} -.text-button.danger { - color: #dc3545; -} -.text-button.danger:hover { - color: #e77681; -} -.text-button.light { - color: #f8f9fa; -} -.text-button.light:hover { - color: white; -} -.text-button.dark { - color: #212529; -} -.text-button.dark:hover { - color: #434b53; -} - .touch-action-none { touch-action: none; } diff --git a/FrontEnd/src/palette.ts b/FrontEnd/src/palette.ts index 11b3de85..6385df66 100644 --- a/FrontEnd/src/palette.ts +++ b/FrontEnd/src/palette.ts @@ -19,18 +19,19 @@ export interface PaletteColor { [key: string]: string; } -export interface Palette { - primary: PaletteColor; - primaryEnhance: PaletteColor; - secondary: PaletteColor; - textPrimary: PaletteColor; - textOnPrimary: PaletteColor; - danger: PaletteColor; - success: PaletteColor; - [key: string]: PaletteColor; -} +const paletteColorList = [ + "primary", + "primary-enhance", + "secondary", + "text-primary", + "text-on-primary", + "danger", + "success", +] as const; + +export type PaletteColorType = typeof paletteColorList[number]; -export type PaletteColorType = keyof Palette; +export type Palette = Record; export function generatePaletteColor(color: string): PaletteColor { const c = Color(color); @@ -60,26 +61,24 @@ export function generatePalette(options: { return { primary: generatePaletteColor(p.toString()), - primaryEnhance: generatePaletteColor(pe.toString()), + "primary-enhance": generatePaletteColor(pe.toString()), secondary: generatePaletteColor(s.toString()), - textPrimary: generatePaletteColor("#111111"), - textOnPrimary: generatePaletteColor(p.lightness() > 60 ? "black" : "white"), + "text-primary": generatePaletteColor("#111111"), + "text-on-primary": generatePaletteColor( + p.lightness() > 60 ? "black" : "white" + ), danger: generatePaletteColor("red"), success: generatePaletteColor("green"), }; } export function generatePaletteCSS(palette: Palette): string { - function toSnakeCase(s: string): string { - return s.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`); - } - const colors: [string, string][] = []; - for (const paletteColorName in palette) { - const paletteColor = palette[paletteColorName]; + for (const colorType of paletteColorList) { + const paletteColor = palette[colorType]; for (const variant in paletteColor) { - let key = `--tl-${toSnakeCase(paletteColorName)}`; - if (variant !== "color") key += `-${toSnakeCase(variant)}`; + let key = `--tl-${colorType}`; + if (variant !== "color") key += `-${variant}`; key += "-color"; colors.push([key, paletteColor[variant]]); } diff --git a/FrontEnd/src/views/admin/UserAdmin.tsx b/FrontEnd/src/views/admin/UserAdmin.tsx index 558d3aee..eb141520 100644 --- a/FrontEnd/src/views/admin/UserAdmin.tsx +++ b/FrontEnd/src/views/admin/UserAdmin.tsx @@ -14,6 +14,7 @@ import { UserPermission, } from "@/http/user"; import { Trans, useTranslation } from "react-i18next"; +import TextButton from "../common/button/TextButton"; interface DialogProps { open: boolean; @@ -230,15 +231,16 @@ const UserItem: React.FC = ({ user, on }) => { className={classnames("edit-mask", !editMaskVisible && "d-none")} onClick={() => setEditMaskVisible(false)} > - - - + + + ); diff --git a/FrontEnd/src/views/center/TimelineBoard.tsx b/FrontEnd/src/views/center/TimelineBoard.tsx index a6a60b3d..e0a2d80e 100644 --- a/FrontEnd/src/views/center/TimelineBoard.tsx +++ b/FrontEnd/src/views/center/TimelineBoard.tsx @@ -1,7 +1,6 @@ import React from "react"; import classnames from "classnames"; import { Link } from "react-router-dom"; -import { useTranslation } from "react-i18next"; import { Spinner } from "react-bootstrap"; import { HttpTimelineInfo } from "@/http/timeline"; @@ -220,8 +219,6 @@ interface TimelineBoardUIProps { const TimelineBoardUI: React.FC = (props) => { const { title, timelines, className, editHandler } = props; - const { t } = useTranslation(); - const editable = editHandler != null; const [editing, setEditing] = React.useState(false); diff --git a/FrontEnd/src/views/common/button/FlatButton.tsx b/FrontEnd/src/views/common/button/FlatButton.tsx index 0727eb88..24f47785 100644 --- a/FrontEnd/src/views/common/button/FlatButton.tsx +++ b/FrontEnd/src/views/common/button/FlatButton.tsx @@ -1,11 +1,11 @@ import React from "react"; import { useTranslation } from "react-i18next"; +import classNames from "classnames"; import { convertI18nText, I18nText } from "@/common"; import { PaletteColorType } from "@/palette"; import "./FlatButton.css"; -import classNames from "classnames"; function _FlatButton( { diff --git a/FrontEnd/src/views/common/button/TextButton.css b/FrontEnd/src/views/common/button/TextButton.css new file mode 100644 index 00000000..dc5abaaa --- /dev/null +++ b/FrontEnd/src/views/common/button/TextButton.css @@ -0,0 +1,36 @@ +.cru-text-button { + background: transparent; + border: none; +} + +.cru-text-button.primary { + color: var(--tl-primary-color); +} + +.cru-text-button.primary:hover { + color: var(--tl-primary-lighter-color); +} + +.cru-text-button.secondary { + color: var(--tl-secondary-color); +} + +.cru-text-button.secondary:hover { + color: var(--tl-secondary-lighter-color); +} + +.cru-text-button.success { + color: var(--tl-success-color); +} + +.cru-text-button.success:hover { + color: var(--tl-success-lighter-color); +} + +.cru-text-button.danger { + color: var(--tl-danger-color); +} + +.cru-text-button.danger:hover { + color: var(--tl-danger-lighter-color); +} diff --git a/FrontEnd/src/views/common/button/TextButton.tsx b/FrontEnd/src/views/common/button/TextButton.tsx new file mode 100644 index 00000000..2014158a --- /dev/null +++ b/FrontEnd/src/views/common/button/TextButton.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import classNames from "classnames"; + +import { convertI18nText, I18nText } from "@/common"; +import { PaletteColorType } from "@/palette"; + +import "./TextButton.css"; + +function _TextButton( + { + text, + color, + onClick, + }: { + text: I18nText; + color?: PaletteColorType; + onClick?: () => void; + }, + ref: React.ForwardedRef +): React.ReactElement | null { + const { t } = useTranslation(); + + return ( + + ); +} + +const TextButton = React.forwardRef(_TextButton); +export default TextButton; -- cgit v1.2.3