Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Axios from "axios";
import React, { ChangeEvent, useContext, useRef, useState } from "react";
import React, { ChangeEvent, useContext, useEffect, useState } from "react";
import classNames from "classnames";
import { Region } from "flat-components";
import { observer } from "mobx-react-lite";
Expand All @@ -12,32 +12,32 @@ import { uploadAvatarFinish, uploadAvatarStart } from "../../../api-middleware/f
import { globalStore } from "../../../stores/global-store";

export interface UploadAvatarProps {
fileRef?: React.MutableRefObject<File | undefined>;
onUpload?: (file: File) => Promise<void>;
}

export function useFileRef(): React.MutableRefObject<File | undefined> {
return useRef<File>();
}

export const UploadAvatar = observer<UploadAvatarProps>(function UploadAvatar({ fileRef }) {
export const UploadAvatar = observer<UploadAvatarProps>(function UploadAvatar({ onUpload }) {
const globalStore = useContext(GlobalStoreContext);
const sp = useSafePromise();
const { t } = useTranslation();

const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState(globalStore.userInfo?.avatar || "");

const updateInput = (event: ChangeEvent<HTMLInputElement>): void => {
const file: File | undefined = (event.target.files || [])[0];
if (fileRef) {
fileRef.current = file;
useEffect(() => {
if (globalStore.userInfo?.avatar) {
setImageUrl(globalStore.userInfo.avatar);
}
}, [globalStore.userInfo?.avatar]);

const updateInput = async (event: ChangeEvent<HTMLInputElement>): Promise<void> => {
const file: File | undefined = (event.target.files || [])[0];
if (file) {
setLoading(true);
sp(fileToDataUrl(file)).then(url => {
setLoading(false);
setImageUrl(url);
});
sp(fileToDataUrl(file)).then(setImageUrl);
if (onUpload) {
await sp(onUpload(file).catch(console.error));
}
setLoading(false);
} else {
setImageUrl(globalStore.userInfo?.avatar || "");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ConfigStoreContext, GlobalStoreContext } from "../../../components/Stor
import { useSafePromise } from "../../../utils/hooks/lifecycle";
import { loginCheck, rename } from "../../../api-middleware/flatServer";
import { ConfirmButtons } from "./ConfirmButtons";
import { UploadAvatar, uploadAvatar, useFileRef } from "./UploadAvatar";
import { UploadAvatar, uploadAvatar } from "./UploadAvatar";

enum SelectLanguage {
Chinese,
Expand All @@ -26,7 +26,6 @@ export const GeneralSettingPage = (): React.ReactElement => {

const [name, setName] = useState(globalStore.userName || "");
const [isRenaming, setRenaming] = useState(false);
const fileRef = useFileRef();

async function changeUserName(): Promise<void> {
if (name !== globalStore.userName) {
Expand All @@ -40,16 +39,12 @@ export const GeneralSettingPage = (): React.ReactElement => {
}
}

async function changeAvatar(): Promise<void> {
if (fileRef.current) {
try {
await uploadAvatar(fileRef.current);
} catch (error) {
console.error(error);
message.info(t("upload-avatar-failed"));
} finally {
fileRef.current = undefined;
}
async function onUpload(file: File): Promise<void> {
try {
await uploadAvatar(file);
} catch (error) {
message.info(t("upload-avatar-failed"));
throw error;
}
}

Expand Down Expand Up @@ -93,10 +88,11 @@ export const GeneralSettingPage = (): React.ReactElement => {
<div className="general-setting-user-profile">
<span className="general-setting-title">{t("user-profile")}</span>
<div className="general-setting-user-avatar-wrapper">
<UploadAvatar fileRef={fileRef} />
<ConfirmButtons onConfirm={changeAvatar} />
<span className="general-setting-subtitle">{t("avatar")}</span>
<UploadAvatar onUpload={onUpload} />
</div>
<div>
<span className="general-setting-subtitle">{t("username")}</span>
<Input
disabled={isRenaming}
id="username"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
}
}

.general-setting-subtitle {
color: var(--text-strong);
padding-right: 16px;
}

.general-setting-user-avatar-wrapper {
display: flex;
align-items: center;
Expand All @@ -36,7 +41,7 @@
height: 96px;
margin-right: 16px;
border-radius: 50%;
border: 1px solid var(--grey-6);
border: 1px solid #dbe1ea;
overflow: hidden;
position: relative;
transition: all 0.2s ease-in-out;
Expand All @@ -45,10 +50,6 @@
opacity: 0.5;
}

&:hover {
border-color: var(--primary);
}

.user-avatar-input {
display: block;
position: absolute;
Expand All @@ -74,6 +75,12 @@
justify-content: center;
}
}
.flat-color-scheme-dark .general-setting-user-avatar {
border-color: var(--grey-6);
}
.general-setting-user-avatar:hover {
border-color: var(--primary);
}

.general-setting-checkbox {
margin-bottom: 8px;
Expand Down
2 changes: 2 additions & 0 deletions packages/flat-i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@
"chinese_1": "Tin Word Format"
},
"user-profile": "User Profile",
"username": "Name",
"avatar": "Avatar",
"upload-avatar": "Upload Avatar",
"upload-avatar-failed": "Upload avatar failed"
}
2 changes: 2 additions & 0 deletions packages/flat-i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@
"chinese_1": "田字格"
},
"user-profile": "用户资料",
"username": "昵称",
"avatar": "头像",
"upload-avatar": "上传头像",
"upload-avatar-failed": "上传头像失败"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Axios from "axios";
import React, { ChangeEvent, useContext, useRef, useState } from "react";
import React, { ChangeEvent, useContext, useEffect, useState } from "react";
import classNames from "classnames";
import { Region } from "flat-components";
import { observer } from "mobx-react-lite";
Expand All @@ -12,32 +12,32 @@ import { globalStore } from "../../../stores/GlobalStore";
import { CLOUD_STORAGE_OSS_ALIBABA_CONFIG } from "../../../constants/process";

export interface UploadAvatarProps {
fileRef?: React.MutableRefObject<File | undefined>;
onUpload?: (file: File) => Promise<void>;
}

export function useFileRef(): React.MutableRefObject<File | undefined> {
return useRef<File>();
}

export const UploadAvatar = observer<UploadAvatarProps>(function UploadAvatar({ fileRef }) {
export const UploadAvatar = observer<UploadAvatarProps>(function UploadAvatar({ onUpload }) {
const globalStore = useContext(GlobalStoreContext);
const sp = useSafePromise();
const { t } = useTranslation();

const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState(globalStore.userInfo?.avatar || "");

const updateInput = (event: ChangeEvent<HTMLInputElement>): void => {
const file: File | undefined = (event.target.files || [])[0];
if (fileRef) {
fileRef.current = file;
useEffect(() => {
if (globalStore.userInfo?.avatar) {
setImageUrl(globalStore.userInfo.avatar);
}
}, [globalStore.userInfo?.avatar]);

const updateInput = async (event: ChangeEvent<HTMLInputElement>): Promise<void> => {
const file: File | undefined = (event.target.files || [])[0];
if (file) {
setLoading(true);
sp(fileToDataUrl(file)).then(url => {
setLoading(false);
setImageUrl(url);
});
sp(fileToDataUrl(file)).then(setImageUrl);
if (onUpload) {
await sp(onUpload(file).catch(console.error));
}
setLoading(false);
} else {
setImageUrl(globalStore.userInfo?.avatar || "");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
}
}

.general-setting-subtitle {
color: var(--text-strong);
padding-right: 16px;
}

.general-setting-user-avatar-wrapper {
display: flex;
align-items: center;
Expand All @@ -36,7 +41,7 @@
height: 96px;
margin-right: 16px;
border-radius: 50%;
border: 1px solid var(--grey-6);
border: 1px solid #dbe1ea;
overflow: hidden;
position: relative;
transition: all 0.2s ease-in-out;
Expand All @@ -45,10 +50,6 @@
opacity: 0.5;
}

&:hover {
border-color: var(--primary);
}

.user-avatar-input {
display: block;
position: absolute;
Expand All @@ -74,6 +75,12 @@
justify-content: center;
}
}
.flat-color-scheme-dark .general-setting-user-avatar {
border-color: var(--grey-6);
}
.general-setting-user-avatar:hover {
border-color: var(--primary);
}

.general-setting-select-language {
> span {
Expand Down
24 changes: 10 additions & 14 deletions web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ConfigStoreContext, GlobalStoreContext } from "../../../components/Stor
import { useSafePromise } from "../../../utils/hooks/lifecycle";
import { loginCheck, rename } from "../../../api-middleware/flatServer";
import { ConfirmButtons } from "./ConfirmButtons";
import { uploadAvatar, UploadAvatar, useFileRef } from "./UploadAvatar";
import { uploadAvatar, UploadAvatar } from "./UploadAvatar";

enum SelectLanguage {
Chinese,
Expand All @@ -27,7 +27,6 @@ export const GeneralSettingPage = (): React.ReactElement => {

const [name, setName] = useState(globalStore.userName || "");
const [isRenaming, setRenaming] = useState(false);
const fileRef = useFileRef();

async function changeUserName(): Promise<void> {
if (name !== globalStore.userName) {
Expand All @@ -41,16 +40,12 @@ export const GeneralSettingPage = (): React.ReactElement => {
}
}

async function changeAvatar(): Promise<void> {
if (fileRef.current) {
try {
await uploadAvatar(fileRef.current);
} catch (error) {
console.error(error);
message.info(t("upload-avatar-failed"));
} finally {
fileRef.current = undefined;
}
async function onUpload(file: File): Promise<void> {
try {
await uploadAvatar(file);
} catch (error) {
message.info(t("upload-avatar-failed"));
throw error;
}
}

Expand All @@ -70,10 +65,11 @@ export const GeneralSettingPage = (): React.ReactElement => {
<div className="general-setting-user-profile">
<span className="general-setting-title">{t("user-profile")}</span>
<div className="general-setting-user-avatar-wrapper">
<UploadAvatar fileRef={fileRef} />
<ConfirmButtons onConfirm={changeAvatar} />
<span className="general-setting-subtitle">{t("avatar")}</span>
<UploadAvatar onUpload={onUpload} />
</div>
<div>
<span className="general-setting-subtitle">{t("username")}</span>
<Input
disabled={isRenaming}
id="username"
Expand Down