From 928ba0ce419bacba113951095278a5138ead34cf Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 10 Apr 2022 16:04:03 +0800 Subject: ... --- FrontEnd/src/views/timeline/TimelineMember.tsx | 193 +++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 FrontEnd/src/views/timeline/TimelineMember.tsx (limited to 'FrontEnd/src/views/timeline/TimelineMember.tsx') diff --git a/FrontEnd/src/views/timeline/TimelineMember.tsx b/FrontEnd/src/views/timeline/TimelineMember.tsx new file mode 100644 index 00000000..59d4c371 --- /dev/null +++ b/FrontEnd/src/views/timeline/TimelineMember.tsx @@ -0,0 +1,193 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { convertI18nText, I18nText } from "@/common"; + +import { HttpUser } from "@/http/user"; +import { getHttpSearchClient } from "@/http/search"; +import { getHttpTimelineClient, HttpTimelineInfo } from "@/http/timeline"; + +import SearchInput from "../common/SearchInput"; +import UserAvatar from "../common/user/UserAvatar"; +import Button from "../common/button/Button"; +import Dialog from "../common/dailog/Dialog"; + +import "./TimelineMember.css"; + +const TimelineMemberItem: React.FC<{ + user: HttpUser; + add?: boolean; + onAction?: (username: string) => void; +}> = ({ user, add, onAction }) => { + return ( +
+
+
+ +
+
+
{user.nickname}
+ {"@" + user.username} +
+ {onAction ? ( +
+
+ ) : null} +
+
+ ); +}; + +const TimelineMemberUserSearch: React.FC<{ + timeline: HttpTimelineInfo; + onChange: () => void; +}> = ({ timeline, onChange }) => { + const { t } = useTranslation(); + + const [userSearchText, setUserSearchText] = useState(""); + const [userSearchState, setUserSearchState] = useState< + | { + type: "users"; + data: HttpUser[]; + } + | { type: "error"; data: I18nText } + | { type: "loading" } + | { type: "init" } + >({ type: "init" }); + + return ( + <> + { + setUserSearchText(v); + }} + loading={userSearchState.type === "loading"} + onButtonClick={() => { + if (userSearchText === "") { + setUserSearchState({ + type: "error", + data: "login.emptyUsername", + }); + return; + } + setUserSearchState({ type: "loading" }); + getHttpSearchClient() + .searchUsers(userSearchText) + .then( + (users) => { + users = users.filter( + (user) => + timeline.members.findIndex( + (m) => m.username === user.username + ) === -1 && timeline.owner.username !== user.username + ); + setUserSearchState({ type: "users", data: users }); + }, + (e) => { + setUserSearchState({ + type: "error", + data: { type: "custom", value: String(e) }, + }); + } + ); + }} + /> + {(() => { + if (userSearchState.type === "users") { + const users = userSearchState.data; + if (users.length === 0) { + return
{t("timeline.member.noUserAvailableToAdd")}
; + } else { + return ( +
+ {users.map((user) => ( + { + void getHttpTimelineClient() + .memberPut(timeline.name, user.username) + .then(() => { + setUserSearchText(""); + setUserSearchState({ type: "init" }); + onChange(); + }); + }} + /> + ))} +
+ ); + } + } else if (userSearchState.type === "error") { + return ( +
+ {convertI18nText(userSearchState.data, t)} +
+ ); + } + })()} + + ); +}; + +export interface TimelineMemberProps { + timeline: HttpTimelineInfo; + onChange: () => void; +} + +const TimelineMember: React.FC = (props) => { + const { timeline, onChange } = props; + const members = [timeline.owner, ...timeline.members]; + + return ( +
+
+ {members.map((member, index) => ( + { + void getHttpTimelineClient() + .memberDelete(timeline.name, member.username) + .then(onChange); + } + : undefined + } + /> + ))} +
+ {timeline.manageable ? ( + + ) : null} +
+ ); +}; + +export default TimelineMember; + +export interface TimelineMemberDialogProps extends TimelineMemberProps { + open: boolean; + onClose: () => void; +} + +export const TimelineMemberDialog: React.FC = ( + props +) => { + return ( + + + + ); +}; -- cgit v1.2.3