From de1d582bf2ed7062fd400459f30d463d47ef9982 Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 24 Aug 2020 22:59:45 +0800 Subject: ... --- .../ClientApp/src/app/user/ChangeAvatarDialog.tsx | 612 ++++++++++----------- .../src/app/user/ChangeNicknameDialog.tsx | 56 +- Timeline/ClientApp/src/app/user/Login.tsx | 296 +++++----- Timeline/ClientApp/src/app/user/User.tsx | 154 +++--- Timeline/ClientApp/src/app/user/UserInfoCard.tsx | 210 +++---- Timeline/ClientApp/src/app/user/UserPage.tsx | 38 +- Timeline/ClientApp/src/app/user/api.ts | 20 +- Timeline/ClientApp/src/app/user/user-page.sass | 21 +- 8 files changed, 703 insertions(+), 704 deletions(-) (limited to 'Timeline/ClientApp/src/app/user') diff --git a/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx b/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx index f7b25252..7d9f9514 100644 --- a/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx +++ b/Timeline/ClientApp/src/app/user/ChangeAvatarDialog.tsx @@ -1,306 +1,306 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { - Modal, - ModalHeader, - Row, - Button, - ModalBody, - ModalFooter, -} from 'reactstrap'; -import { AxiosError } from 'axios'; - -import ImageCropper, { Clip, applyClipToImage } from '../common/ImageCropper'; -import { UiLogicError } from '../common'; - -export interface ChangeAvatarDialogProps { - open: boolean; - close: () => void; - process: (blob: Blob) => Promise; -} - -const ChangeAvatarDialog: React.FC = (props) => { - const { t } = useTranslation(); - - const [file, setFile] = React.useState(null); - const [fileUrl, setFileUrl] = React.useState(null); - const [clip, setClip] = React.useState(null); - const [ - cropImgElement, - setCropImgElement, - ] = React.useState(null); - const [resultBlob, setResultBlob] = React.useState(null); - const [resultUrl, setResultUrl] = React.useState(null); - - const [state, setState] = React.useState< - | 'select' - | 'crop' - | 'processcrop' - | 'preview' - | 'uploading' - | 'success' - | 'error' - >('select'); - - const [message, setMessage] = useState< - string | { type: 'custom'; text: string } | null - >('userPage.dialogChangeAvatar.prompt.select'); - - const trueMessage = - message == null - ? null - : typeof message === 'string' - ? t(message) - : message.text; - - const closeDialog = props.close; - - const toggle = React.useCallback((): void => { - if (!(state === 'uploading')) { - closeDialog(); - } - }, [state, closeDialog]); - - useEffect(() => { - if (file != null) { - const url = URL.createObjectURL(file); - setClip(null); - setFileUrl(url); - setState('crop'); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setFileUrl(null); - setState('select'); - } - }, [file]); - - React.useEffect(() => { - if (resultBlob != null) { - const url = URL.createObjectURL(resultBlob); - setResultUrl(url); - setState('preview'); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setResultUrl(null); - } - }, [resultBlob]); - - const onSelectFile = React.useCallback( - (e: React.ChangeEvent): void => { - const files = e.target.files; - if (files == null || files.length === 0) { - setFile(null); - } else { - setFile(files[0]); - } - }, - [] - ); - - const onCropNext = React.useCallback(() => { - if ( - cropImgElement == null || - clip == null || - clip.width === 0 || - file == null - ) { - throw new UiLogicError(); - } - - setState('processcrop'); - void applyClipToImage(cropImgElement, clip, file.type).then((b) => { - setResultBlob(b); - }); - }, [cropImgElement, clip, file]); - - const onCropPrevious = React.useCallback(() => { - setFile(null); - setState('select'); - }, []); - - const onPreviewPrevious = React.useCallback(() => { - setResultBlob(null); - setState('crop'); - }, []); - - const process = props.process; - - const upload = React.useCallback(() => { - if (resultBlob == null) { - throw new UiLogicError(); - } - - setState('uploading'); - process(resultBlob).then( - () => { - setState('success'); - }, - (e: unknown) => { - setState('error'); - setMessage({ type: 'custom', text: (e as AxiosError).message }); - } - ); - }, [resultBlob, process]); - - const createPreviewRow = (): React.ReactElement => { - if (resultUrl == null) { - throw new UiLogicError(); - } - return ( - - {t('userPage.dialogChangeAvatar.previewImgAlt')} - - ); - }; - - return ( - - {t('userPage.dialogChangeAvatar.title')} - {(() => { - if (state === 'select') { - return ( - <> - - {t('userPage.dialogChangeAvatar.prompt.select')} - - - - - - - - - ); - } else if (state === 'crop') { - if (fileUrl == null) { - throw new UiLogicError(); - } - return ( - <> - - - - - {t('userPage.dialogChangeAvatar.prompt.crop')} - - - - - - - - ); - } else if (state === 'processcrop') { - return ( - <> - - - {t('userPage.dialogChangeAvatar.prompt.processingCrop')} - - - - - - - - ); - } else if (state === 'preview') { - return ( - <> - - {createPreviewRow()} - {t('userPage.dialogChangeAvatar.prompt.preview')} - - - - - - - - ); - } else if (state === 'uploading') { - return ( - <> - - {createPreviewRow()} - {t('userPage.dialogChangeAvatar.prompt.uploading')} - - - - ); - } else if (state === 'success') { - return ( - <> - - - {t('operationDialog.success')} - - - - - - - ); - } else { - return ( - <> - - {createPreviewRow()} - {trueMessage} - - - - - - - ); - } - })()} - - ); -}; - -export default ChangeAvatarDialog; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { + Modal, + ModalHeader, + Row, + Button, + ModalBody, + ModalFooter, +} from "reactstrap"; +import { AxiosError } from "axios"; + +import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper"; +import { UiLogicError } from "../common"; + +export interface ChangeAvatarDialogProps { + open: boolean; + close: () => void; + process: (blob: Blob) => Promise; +} + +const ChangeAvatarDialog: React.FC = (props) => { + const { t } = useTranslation(); + + const [file, setFile] = React.useState(null); + const [fileUrl, setFileUrl] = React.useState(null); + const [clip, setClip] = React.useState(null); + const [ + cropImgElement, + setCropImgElement, + ] = React.useState(null); + const [resultBlob, setResultBlob] = React.useState(null); + const [resultUrl, setResultUrl] = React.useState(null); + + const [state, setState] = React.useState< + | "select" + | "crop" + | "processcrop" + | "preview" + | "uploading" + | "success" + | "error" + >("select"); + + const [message, setMessage] = useState< + string | { type: "custom"; text: string } | null + >("userPage.dialogChangeAvatar.prompt.select"); + + const trueMessage = + message == null + ? null + : typeof message === "string" + ? t(message) + : message.text; + + const closeDialog = props.close; + + const toggle = React.useCallback((): void => { + if (!(state === "uploading")) { + closeDialog(); + } + }, [state, closeDialog]); + + useEffect(() => { + if (file != null) { + const url = URL.createObjectURL(file); + setClip(null); + setFileUrl(url); + setState("crop"); + return () => { + URL.revokeObjectURL(url); + }; + } else { + setFileUrl(null); + setState("select"); + } + }, [file]); + + React.useEffect(() => { + if (resultBlob != null) { + const url = URL.createObjectURL(resultBlob); + setResultUrl(url); + setState("preview"); + return () => { + URL.revokeObjectURL(url); + }; + } else { + setResultUrl(null); + } + }, [resultBlob]); + + const onSelectFile = React.useCallback( + (e: React.ChangeEvent): void => { + const files = e.target.files; + if (files == null || files.length === 0) { + setFile(null); + } else { + setFile(files[0]); + } + }, + [] + ); + + const onCropNext = React.useCallback(() => { + if ( + cropImgElement == null || + clip == null || + clip.width === 0 || + file == null + ) { + throw new UiLogicError(); + } + + setState("processcrop"); + void applyClipToImage(cropImgElement, clip, file.type).then((b) => { + setResultBlob(b); + }); + }, [cropImgElement, clip, file]); + + const onCropPrevious = React.useCallback(() => { + setFile(null); + setState("select"); + }, []); + + const onPreviewPrevious = React.useCallback(() => { + setResultBlob(null); + setState("crop"); + }, []); + + const process = props.process; + + const upload = React.useCallback(() => { + if (resultBlob == null) { + throw new UiLogicError(); + } + + setState("uploading"); + process(resultBlob).then( + () => { + setState("success"); + }, + (e: unknown) => { + setState("error"); + setMessage({ type: "custom", text: (e as AxiosError).message }); + } + ); + }, [resultBlob, process]); + + const createPreviewRow = (): React.ReactElement => { + if (resultUrl == null) { + throw new UiLogicError(); + } + return ( + + {t("userPage.dialogChangeAvatar.previewImgAlt")} + + ); + }; + + return ( + + {t("userPage.dialogChangeAvatar.title")} + {(() => { + if (state === "select") { + return ( + <> + + {t("userPage.dialogChangeAvatar.prompt.select")} + + + + + + + + + ); + } else if (state === "crop") { + if (fileUrl == null) { + throw new UiLogicError(); + } + return ( + <> + + + + + {t("userPage.dialogChangeAvatar.prompt.crop")} + + + + + + + + ); + } else if (state === "processcrop") { + return ( + <> + + + {t("userPage.dialogChangeAvatar.prompt.processingCrop")} + + + + + + + + ); + } else if (state === "preview") { + return ( + <> + + {createPreviewRow()} + {t("userPage.dialogChangeAvatar.prompt.preview")} + + + + + + + + ); + } else if (state === "uploading") { + return ( + <> + + {createPreviewRow()} + {t("userPage.dialogChangeAvatar.prompt.uploading")} + + + + ); + } else if (state === "success") { + return ( + <> + + + {t("operationDialog.success")} + + + + + + + ); + } else { + return ( + <> + + {createPreviewRow()} + {trueMessage} + + + + + + + ); + } + })()} + + ); +}; + +export default ChangeAvatarDialog; diff --git a/Timeline/ClientApp/src/app/user/ChangeNicknameDialog.tsx b/Timeline/ClientApp/src/app/user/ChangeNicknameDialog.tsx index 393d88f6..251b18c5 100644 --- a/Timeline/ClientApp/src/app/user/ChangeNicknameDialog.tsx +++ b/Timeline/ClientApp/src/app/user/ChangeNicknameDialog.tsx @@ -1,28 +1,28 @@ -import React from 'react'; - -import OperationDialog from '../common/OperationDialog'; - -export interface ChangeNicknameDialogProps { - open: boolean; - close: () => void; - onProcess: (newNickname: string) => Promise; -} - -const ChangeNicknameDialog: React.FC = props => { - return ( - { - return props.onProcess(newNickname as string); - }} - close={props.close} - /> - ); -}; - -export default ChangeNicknameDialog; +import React from "react"; + +import OperationDialog from "../common/OperationDialog"; + +export interface ChangeNicknameDialogProps { + open: boolean; + close: () => void; + onProcess: (newNickname: string) => Promise; +} + +const ChangeNicknameDialog: React.FC = (props) => { + return ( + { + return props.onProcess(newNickname as string); + }} + close={props.close} + /> + ); +}; + +export default ChangeNicknameDialog; diff --git a/Timeline/ClientApp/src/app/user/Login.tsx b/Timeline/ClientApp/src/app/user/Login.tsx index 2f2a3188..4f9cd05d 100644 --- a/Timeline/ClientApp/src/app/user/Login.tsx +++ b/Timeline/ClientApp/src/app/user/Login.tsx @@ -1,148 +1,148 @@ -import React, { Fragment, useState, useEffect } from 'react'; -import { useHistory } from 'react-router'; -import { useTranslation } from 'react-i18next'; - -import AppBar from '../common/AppBar'; - -import { useUser, userService } from '../data/user'; -import { - Label, - FormGroup, - Input, - Form, - FormFeedback, - Spinner, - Button, -} from 'reactstrap'; - -const Login: React.FC = (_) => { - const { t } = useTranslation(); - const history = useHistory(); - const [username, setUsername] = useState(''); - const [usernameDirty, setUsernameDirty] = useState(false); - const [password, setPassword] = useState(''); - const [passwordDirty, setPasswordDirty] = useState(false); - const [rememberMe, setRememberMe] = useState(true); - const [process, setProcess] = useState(false); - const [error, setError] = useState(null); - - const user = useUser(); - - useEffect(() => { - if (user != null) { - const id = setTimeout(() => history.push('/'), 3000); - return () => { - clearTimeout(id); - }; - } - }, [history, user]); - - if (user != null) { - return ( - <> - -

{t('login.alreadyLogin')}

- - ); - } - - function onSubmit(event: React.SyntheticEvent): void { - if (username === '' || password === '') { - setUsernameDirty(true); - setPasswordDirty(true); - return; - } - - setProcess(true); - userService - .login( - { - username: username, - password: password, - }, - rememberMe - ) - .then( - () => { - if (history.length === 0) { - history.push('/'); - } else { - history.goBack(); - } - }, - (e: Error) => { - setProcess(false); - setError(e.message); - } - ); - event.preventDefault(); - } - - return ( - - -
-

{t('welcome')}

-
- - - { - setUsername(e.target.value); - setUsernameDirty(true); - }} - value={username} - invalid={usernameDirty && username === ''} - /> - {usernameDirty && username === '' && ( - {t('login.emptyUsername')} - )} - - - - { - setPassword(e.target.value); - setPasswordDirty(true); - }} - value={password} - invalid={passwordDirty && password === ''} - /> - {passwordDirty && password === '' && ( - {t('login.emptyPassword')} - )} - - - { - const v = (e.target as HTMLInputElement).checked; - setRememberMe(v); - }} - /> - - - {error ?

{t(error)}

: null} -
- {process ? ( - - ) : ( - - )} -
-
-
-
- ); -}; - -export default Login; +import React, { Fragment, useState, useEffect } from "react"; +import { useHistory } from "react-router"; +import { useTranslation } from "react-i18next"; + +import AppBar from "../common/AppBar"; + +import { useUser, userService } from "../data/user"; +import { + Label, + FormGroup, + Input, + Form, + FormFeedback, + Spinner, + Button, +} from "reactstrap"; + +const Login: React.FC = (_) => { + const { t } = useTranslation(); + const history = useHistory(); + const [username, setUsername] = useState(""); + const [usernameDirty, setUsernameDirty] = useState(false); + const [password, setPassword] = useState(""); + const [passwordDirty, setPasswordDirty] = useState(false); + const [rememberMe, setRememberMe] = useState(true); + const [process, setProcess] = useState(false); + const [error, setError] = useState(null); + + const user = useUser(); + + useEffect(() => { + if (user != null) { + const id = setTimeout(() => history.push("/"), 3000); + return () => { + clearTimeout(id); + }; + } + }, [history, user]); + + if (user != null) { + return ( + <> + +

{t("login.alreadyLogin")}

+ + ); + } + + function onSubmit(event: React.SyntheticEvent): void { + if (username === "" || password === "") { + setUsernameDirty(true); + setPasswordDirty(true); + return; + } + + setProcess(true); + userService + .login( + { + username: username, + password: password, + }, + rememberMe + ) + .then( + () => { + if (history.length === 0) { + history.push("/"); + } else { + history.goBack(); + } + }, + (e: Error) => { + setProcess(false); + setError(e.message); + } + ); + event.preventDefault(); + } + + return ( + + +
+

{t("welcome")}

+
+ + + { + setUsername(e.target.value); + setUsernameDirty(true); + }} + value={username} + invalid={usernameDirty && username === ""} + /> + {usernameDirty && username === "" && ( + {t("login.emptyUsername")} + )} + + + + { + setPassword(e.target.value); + setPasswordDirty(true); + }} + value={password} + invalid={passwordDirty && password === ""} + /> + {passwordDirty && password === "" && ( + {t("login.emptyPassword")} + )} + + + { + const v = (e.target as HTMLInputElement).checked; + setRememberMe(v); + }} + /> + + + {error ?

{t(error)}

: null} +
+ {process ? ( + + ) : ( + + )} +
+
+
+
+ ); +}; + +export default Login; diff --git a/Timeline/ClientApp/src/app/user/User.tsx b/Timeline/ClientApp/src/app/user/User.tsx index 67c9d921..6f61cacf 100644 --- a/Timeline/ClientApp/src/app/user/User.tsx +++ b/Timeline/ClientApp/src/app/user/User.tsx @@ -1,77 +1,77 @@ -import React, { useState } from 'react'; -import { useParams } from 'react-router'; - -import { UiLogicError } from '../common'; -import { useUser, userInfoService } from '../data/user'; -import { changeNickname } from './api'; - -import UserPage from './UserPage'; -import ChangeNicknameDialog from './ChangeNicknameDialog'; -import ChangeAvatarDialog from './ChangeAvatarDialog'; -import TimelinePageTemplate from '../timeline/TimelinePageTemplate'; -import { PersonalTimelineManageItem } from './UserInfoCard'; - -const User: React.FC = (_) => { - const { username } = useParams<{ username: string }>(); - - const user = useUser(); - - const [dialog, setDialog] = useState(null); - const [dataKey, setDataKey] = useState(0); - - let dialogElement: React.ReactElement | undefined; - - const closeDialogHandler = (): void => { - setDialog(null); - }; - - if (dialog === 'nickname') { - if (user == null) { - throw new UiLogicError('Change nickname without login.'); - } - - dialogElement = ( - { - const p = changeNickname(user.token, username, newNickname); - return p.then((_) => { - setDataKey(dataKey + 1); - }); - }} - /> - ); - } else if (dialog === 'avatar') { - if (user == null) { - throw new UiLogicError('Change avatar without login.'); - } - - dialogElement = ( - userInfoService.setAvatar(username, file)} - /> - ); - } - - const onManage = React.useCallback((item: PersonalTimelineManageItem) => { - setDialog(item); - }, []); - - return ( - <> - - {dialogElement} - - ); -}; - -export default User; +import React, { useState } from "react"; +import { useParams } from "react-router"; + +import { UiLogicError } from "../common"; +import { useUser, userInfoService } from "../data/user"; +import { changeNickname } from "./api"; + +import UserPage from "./UserPage"; +import ChangeNicknameDialog from "./ChangeNicknameDialog"; +import ChangeAvatarDialog from "./ChangeAvatarDialog"; +import TimelinePageTemplate from "../timeline/TimelinePageTemplate"; +import { PersonalTimelineManageItem } from "./UserInfoCard"; + +const User: React.FC = (_) => { + const { username } = useParams<{ username: string }>(); + + const user = useUser(); + + const [dialog, setDialog] = useState(null); + const [dataKey, setDataKey] = useState(0); + + let dialogElement: React.ReactElement | undefined; + + const closeDialogHandler = (): void => { + setDialog(null); + }; + + if (dialog === "nickname") { + if (user == null) { + throw new UiLogicError("Change nickname without login."); + } + + dialogElement = ( + { + const p = changeNickname(user.token, username, newNickname); + return p.then((_) => { + setDataKey(dataKey + 1); + }); + }} + /> + ); + } else if (dialog === "avatar") { + if (user == null) { + throw new UiLogicError("Change avatar without login."); + } + + dialogElement = ( + userInfoService.setAvatar(username, file)} + /> + ); + } + + const onManage = React.useCallback((item: PersonalTimelineManageItem) => { + setDialog(item); + }, []); + + return ( + <> + + {dialogElement} + + ); +}; + +export default User; diff --git a/Timeline/ClientApp/src/app/user/UserInfoCard.tsx b/Timeline/ClientApp/src/app/user/UserInfoCard.tsx index 0f321f32..9ce46e3c 100644 --- a/Timeline/ClientApp/src/app/user/UserInfoCard.tsx +++ b/Timeline/ClientApp/src/app/user/UserInfoCard.tsx @@ -1,105 +1,105 @@ -import React from 'react'; -import clsx from 'clsx'; -import { - Dropdown, - DropdownToggle, - DropdownMenu, - DropdownItem, - Button, -} from 'reactstrap'; -import { useTranslation } from 'react-i18next'; -import { fromEvent } from 'rxjs'; - -import { timelineVisibilityTooltipTranslationMap } from '../data/timeline'; -import { useAvatar } from '../data/user'; - -import { TimelineCardComponentProps } from '../timeline/TimelinePageTemplateUI'; -import BlobImage from '../common/BlobImage'; - -export type PersonalTimelineManageItem = 'avatar' | 'nickname'; - -export type UserInfoCardProps = TimelineCardComponentProps< - PersonalTimelineManageItem ->; - -const UserInfoCard: React.FC = (props) => { - const { onHeight, onManage } = props; - const { t } = useTranslation(); - - const avatar = useAvatar(props.timeline.owner.username); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const containerRef = React.useRef(null!); - - const notifyHeight = React.useCallback((): void => { - if (onHeight) { - onHeight(containerRef.current.getBoundingClientRect().height); - } - }, [onHeight]); - - React.useEffect(() => { - const subscription = fromEvent(window, 'resize').subscribe(notifyHeight); - return () => subscription.unsubscribe(); - }); - - const [manageDropdownOpen, setManageDropdownOpen] = React.useState( - false - ); - const toggleManageDropdown = React.useCallback( - (): void => setManageDropdownOpen((old) => !old), - [] - ); - - return ( -
- -
- {props.timeline.owner.nickname} - - @{props.timeline.owner.username} - -
-

{props.timeline.description}

- - {t(timelineVisibilityTooltipTranslationMap[props.timeline.visibility])} - -
- {onManage != null ? ( - - - {t('timeline.manage')} - - - onManage('nickname')}> - {t('timeline.manageItem.nickname')} - - onManage('avatar')}> - {t('timeline.manageItem.avatar')} - - onManage('property')}> - {t('timeline.manageItem.property')} - - - {t('timeline.manageItem.member')} - - - - ) : ( - - )} -
-
- ); -}; - -export default UserInfoCard; +import React from "react"; +import clsx from "clsx"; +import { + Dropdown, + DropdownToggle, + DropdownMenu, + DropdownItem, + Button, +} from "reactstrap"; +import { useTranslation } from "react-i18next"; +import { fromEvent } from "rxjs"; + +import { timelineVisibilityTooltipTranslationMap } from "../data/timeline"; +import { useAvatar } from "../data/user"; + +import { TimelineCardComponentProps } from "../timeline/TimelinePageTemplateUI"; +import BlobImage from "../common/BlobImage"; + +export type PersonalTimelineManageItem = "avatar" | "nickname"; + +export type UserInfoCardProps = TimelineCardComponentProps< + PersonalTimelineManageItem +>; + +const UserInfoCard: React.FC = (props) => { + const { onHeight, onManage } = props; + const { t } = useTranslation(); + + const avatar = useAvatar(props.timeline.owner.username); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const containerRef = React.useRef(null!); + + const notifyHeight = React.useCallback((): void => { + if (onHeight) { + onHeight(containerRef.current.getBoundingClientRect().height); + } + }, [onHeight]); + + React.useEffect(() => { + const subscription = fromEvent(window, "resize").subscribe(notifyHeight); + return () => subscription.unsubscribe(); + }); + + const [manageDropdownOpen, setManageDropdownOpen] = React.useState( + false + ); + const toggleManageDropdown = React.useCallback( + (): void => setManageDropdownOpen((old) => !old), + [] + ); + + return ( +
+ +
+ {props.timeline.owner.nickname} + + @{props.timeline.owner.username} + +
+

{props.timeline.description}

+ + {t(timelineVisibilityTooltipTranslationMap[props.timeline.visibility])} + +
+ {onManage != null ? ( + + + {t("timeline.manage")} + + + onManage("nickname")}> + {t("timeline.manageItem.nickname")} + + onManage("avatar")}> + {t("timeline.manageItem.avatar")} + + onManage("property")}> + {t("timeline.manageItem.property")} + + + {t("timeline.manageItem.member")} + + + + ) : ( + + )} +
+
+ ); +}; + +export default UserInfoCard; diff --git a/Timeline/ClientApp/src/app/user/UserPage.tsx b/Timeline/ClientApp/src/app/user/UserPage.tsx index 4c9bd747..42f174d9 100644 --- a/Timeline/ClientApp/src/app/user/UserPage.tsx +++ b/Timeline/ClientApp/src/app/user/UserPage.tsx @@ -1,19 +1,19 @@ -import React from 'react'; - -import { ExcludeKey } from '../utilities/type'; - -import TimelinePageTemplateUI, { - TimelinePageTemplateUIProps, -} from '../timeline/TimelinePageTemplateUI'; -import UserInfoCard, { PersonalTimelineManageItem } from './UserInfoCard'; - -export type UserPageProps = ExcludeKey< - TimelinePageTemplateUIProps, - 'CardComponent' ->; - -const UserPage: React.FC = (props) => { - return ; -}; - -export default UserPage; +import React from "react"; + +import { ExcludeKey } from "../utilities/type"; + +import TimelinePageTemplateUI, { + TimelinePageTemplateUIProps, +} from "../timeline/TimelinePageTemplateUI"; +import UserInfoCard, { PersonalTimelineManageItem } from "./UserInfoCard"; + +export type UserPageProps = ExcludeKey< + TimelinePageTemplateUIProps, + "CardComponent" +>; + +const UserPage: React.FC = (props) => { + return ; +}; + +export default UserPage; diff --git a/Timeline/ClientApp/src/app/user/api.ts b/Timeline/ClientApp/src/app/user/api.ts index b2026861..e69acbdb 100644 --- a/Timeline/ClientApp/src/app/user/api.ts +++ b/Timeline/ClientApp/src/app/user/api.ts @@ -1,10 +1,10 @@ -import { getHttpUserClient } from '../http/user'; -import { User } from '../data/user'; - -export function changeNickname( - token: string, - username: string, - newNickname: string -): Promise { - return getHttpUserClient().patch(username, { nickname: newNickname }, token); -} +import { getHttpUserClient } from "../http/user"; +import { User } from "../data/user"; + +export function changeNickname( + token: string, + username: string, + newNickname: string +): Promise { + return getHttpUserClient().patch(username, { nickname: newNickname }, token); +} diff --git a/Timeline/ClientApp/src/app/user/user-page.sass b/Timeline/ClientApp/src/app/user/user-page.sass index 1e2e802b..ca2d10f5 100644 --- a/Timeline/ClientApp/src/app/user/user-page.sass +++ b/Timeline/ClientApp/src/app/user/user-page.sass @@ -1,11 +1,10 @@ -.login-container - max-width: 600px - -.change-avatar-cropper-row - max-height: 400px - -.change-avatar-img - min-width: 50% - max-width: 100% - max-height: 400px - +.login-container + max-width: 600px + +.change-avatar-cropper-row + max-height: 400px + +.change-avatar-img + min-width: 50% + max-width: 100% + max-height: 400px -- cgit v1.2.3