Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
bcee0bbf
Unverified
Commit
bcee0bbf
authored
Feb 18, 2023
by
boojack
Committed by
GitHub
Feb 18, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add avatar to user in frontend (#1108)
parent
096a71c5
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
120 additions
and
80 deletions
+120
-80
MyAccountSection.tsx
web/src/components/Settings/MyAccountSection.tsx
+5
-3
UpdateAccountDialog.tsx
web/src/components/UpdateAccountDialog.tsx
+50
-9
UserAvatar.tsx
web/src/components/UserAvatar.tsx
+17
-0
UserBanner.tsx
web/src/components/UserBanner.tsx
+30
-22
Dropdown.tsx
web/src/components/common/Dropdown.tsx
+2
-2
consts.ts
web/src/helpers/consts.ts
+2
-0
utils.ts
web/src/helpers/utils.ts
+9
-0
toast.less
web/src/less/toast.less
+2
-1
user-banner.less
web/src/less/user-banner.less
+0
-43
en.json
web/src/locales/en.json
+1
-0
user.d.ts
web/src/types/modules/user.d.ts
+2
-0
No files found.
web/src/components/Settings/MyAccountSection.tsx
View file @
bcee0bbf
...
@@ -3,6 +3,7 @@ import { useUserStore } from "../../store/module";
...
@@ -3,6 +3,7 @@ import { useUserStore } from "../../store/module";
import
{
showCommonDialog
}
from
"../Dialog/CommonDialog"
;
import
{
showCommonDialog
}
from
"../Dialog/CommonDialog"
;
import
showChangePasswordDialog
from
"../ChangePasswordDialog"
;
import
showChangePasswordDialog
from
"../ChangePasswordDialog"
;
import
showUpdateAccountDialog
from
"../UpdateAccountDialog"
;
import
showUpdateAccountDialog
from
"../UpdateAccountDialog"
;
import
UserAvatar
from
"../UserAvatar"
;
import
"../../less/settings/my-account-section.less"
;
import
"../../less/settings/my-account-section.less"
;
const
MyAccountSection
=
()
=>
{
const
MyAccountSection
=
()
=>
{
...
@@ -30,14 +31,15 @@ const MyAccountSection = () => {
...
@@ -30,14 +31,15 @@ const MyAccountSection = () => {
<>
<>
<
div
className=
"section-container account-section-container"
>
<
div
className=
"section-container account-section-container"
>
<
p
className=
"title-text"
>
{
t
(
"setting.account-section.title"
)
}
</
p
>
<
p
className=
"title-text"
>
{
t
(
"setting.account-section.title"
)
}
</
p
>
<
div
className=
"flex flex-row justify-start items-end"
>
<
div
className=
"flex flex-row justify-start items-center"
>
<
UserAvatar
className=
"mr-2"
avatarUrl=
{
user
.
avatarUrl
}
/>
<
span
className=
"text-2xl leading-10 font-medium"
>
{
user
.
nickname
}
</
span
>
<
span
className=
"text-2xl leading-10 font-medium"
>
{
user
.
nickname
}
</
span
>
<
span
className=
"text-base ml-1 text-gray-500 leading-
8
"
>
(
{
user
.
username
}
)
</
span
>
<
span
className=
"text-base ml-1 text-gray-500 leading-
10
"
>
(
{
user
.
username
}
)
</
span
>
</
div
>
</
div
>
<
div
className=
"flex flex-row justify-start items-center text-base text-gray-600"
>
{
user
.
email
}
</
div
>
<
div
className=
"flex flex-row justify-start items-center text-base text-gray-600"
>
{
user
.
email
}
</
div
>
<
div
className=
"w-full flex flex-row justify-start items-center mt-2 space-x-2"
>
<
div
className=
"w-full flex flex-row justify-start items-center mt-2 space-x-2"
>
<
button
className=
"btn-normal"
onClick=
{
showUpdateAccountDialog
}
>
<
button
className=
"btn-normal"
onClick=
{
showUpdateAccountDialog
}
>
{
t
(
"
setting.account-section.update-information
"
)
}
{
t
(
"
common.edit
"
)
}
</
button
>
</
button
>
<
button
className=
"btn-normal"
onClick=
{
showChangePasswordDialog
}
>
<
button
className=
"btn-normal"
onClick=
{
showChangePasswordDialog
}
>
{
t
(
"setting.account-section.change-password"
)
}
{
t
(
"setting.account-section.change-password"
)
}
...
...
web/src/components/UpdateAccountDialog.tsx
View file @
bcee0bbf
...
@@ -3,9 +3,11 @@ import { useEffect, useState } from "react";
...
@@ -3,9 +3,11 @@ import { useEffect, useState } from "react";
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useUserStore
}
from
"../store/module"
;
import
{
useUserStore
}
from
"../store/module"
;
import
{
validate
,
ValidatorConfig
}
from
"../helpers/validator"
;
import
{
validate
,
ValidatorConfig
}
from
"../helpers/validator"
;
import
{
convertFileToBase64
}
from
"../helpers/utils"
;
import
Icon
from
"./Icon"
;
import
Icon
from
"./Icon"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
toastHelper
from
"./Toast"
;
import
toastHelper
from
"./Toast"
;
import
UserAvatar
from
"./UserAvatar"
;
const
validateConfig
:
ValidatorConfig
=
{
const
validateConfig
:
ValidatorConfig
=
{
minLength
:
4
,
minLength
:
4
,
...
@@ -17,6 +19,7 @@ const validateConfig: ValidatorConfig = {
...
@@ -17,6 +19,7 @@ const validateConfig: ValidatorConfig = {
type
Props
=
DialogProps
;
type
Props
=
DialogProps
;
interface
State
{
interface
State
{
avatarUrl
:
string
;
username
:
string
;
username
:
string
;
nickname
:
string
;
nickname
:
string
;
email
:
string
;
email
:
string
;
...
@@ -27,6 +30,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
...
@@ -27,6 +30,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
const
userStore
=
useUserStore
();
const
userStore
=
useUserStore
();
const
user
=
userStore
.
state
.
user
as
User
;
const
user
=
userStore
.
state
.
user
as
User
;
const
[
state
,
setState
]
=
useState
<
State
>
({
const
[
state
,
setState
]
=
useState
<
State
>
({
avatarUrl
:
user
.
avatarUrl
,
username
:
user
.
username
,
username
:
user
.
username
,
nickname
:
user
.
nickname
,
nickname
:
user
.
nickname
,
email
:
user
.
email
,
email
:
user
.
email
,
...
@@ -40,6 +44,31 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
...
@@ -40,6 +44,31 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
destroy
();
destroy
();
};
};
const
handleAvatarChanged
=
async
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
files
=
e
.
target
.
files
;
if
(
files
&&
files
.
length
>
0
)
{
const
image
=
files
[
0
];
if
(
image
.
size
>
2
*
1024
*
1024
)
{
toastHelper
.
error
(
"Max file size is 2MB"
);
return
;
}
try
{
const
base64
=
await
convertFileToBase64
(
image
);
setState
((
state
)
=>
{
return
{
...
state
,
avatarUrl
:
base64
,
};
});
}
catch
(
error
)
{
console
.
error
(
error
);
toastHelper
.
error
(
`Failed to convert image to base64`
);
}
}
else
{
toastHelper
.
error
(
"Image not found"
);
}
};
const
handleNicknameChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
handleNicknameChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
setState
((
state
)
=>
{
setState
((
state
)
=>
{
return
{
return
{
...
@@ -48,6 +77,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
...
@@ -48,6 +77,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
};
};
});
});
};
};
const
handleUsernameChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
handleUsernameChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
setState
((
state
)
=>
{
setState
((
state
)
=>
{
return
{
return
{
...
@@ -56,6 +86,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
...
@@ -56,6 +86,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
};
};
});
});
};
};
const
handleEmailChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
handleEmailChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
setState
((
state
)
=>
{
setState
((
state
)
=>
{
return
{
return
{
...
@@ -82,6 +113,9 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
...
@@ -82,6 +113,9 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
const
userPatch
:
UserPatch
=
{
const
userPatch
:
UserPatch
=
{
id
:
user
.
id
,
id
:
user
.
id
,
};
};
if
(
!
isEqual
(
user
.
avatarUrl
,
state
.
avatarUrl
))
{
userPatch
.
avatarUrl
=
state
.
avatarUrl
;
}
if
(
!
isEqual
(
user
.
nickname
,
state
.
nickname
))
{
if
(
!
isEqual
(
user
.
nickname
,
state
.
nickname
))
{
userPatch
.
nickname
=
state
.
nickname
;
userPatch
.
nickname
=
state
.
nickname
;
}
}
...
@@ -108,23 +142,30 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
...
@@ -108,23 +142,30 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
<
Icon
.
X
/>
<
Icon
.
X
/>
</
button
>
</
button
>
</
div
>
</
div
>
<
div
className=
"dialog-content-container"
>
<
div
className=
"dialog-content-container space-y-2"
>
<
p
className=
"text-sm mb-1"
>
<
div
className=
"w-full flex flex-row justify-start items-center"
>
{
t
(
"common.nickname"
)
}
<
span
className=
"text-sm mr-2"
>
{
t
(
"common.avatar"
)
}
</
span
>
<
span
className=
"text-sm text-gray-400 ml-1"
>
(Display in the banner)
</
span
>
<
label
className=
"relative cursor-pointer hover:opacity-80"
>
</
p
>
<
UserAvatar
className=
"!w-12 !h-12"
avatarUrl=
{
state
.
avatarUrl
}
/>
<
input
type=
"text"
className=
"input-text"
value=
{
state
.
nickname
}
onChange=
{
handleNicknameChanged
}
/>
<
input
type=
"file"
accept=
"image/*"
className=
"absolute invisible w-full h-full inset-0"
onChange=
{
handleAvatarChanged
}
/>
<
p
className=
"text-sm mb-1 mt-2"
>
</
label
>
</
div
>
<
p
className=
"text-sm"
>
{
t
(
"common.username"
)
}
{
t
(
"common.username"
)
}
<
span
className=
"text-sm text-gray-400 ml-1"
>
(Using to sign in)
</
span
>
<
span
className=
"text-sm text-gray-400 ml-1"
>
(Using to sign in)
</
span
>
</
p
>
</
p
>
<
input
type=
"text"
className=
"input-text"
value=
{
state
.
username
}
onChange=
{
handleUsernameChanged
}
/>
<
input
type=
"text"
className=
"input-text"
value=
{
state
.
username
}
onChange=
{
handleUsernameChanged
}
/>
<
p
className=
"text-sm mb-1 mt-2"
>
<
p
className=
"text-sm"
>
{
t
(
"common.nickname"
)
}
<
span
className=
"text-sm text-gray-400 ml-1"
>
(Display in the banner)
</
span
>
</
p
>
<
input
type=
"text"
className=
"input-text"
value=
{
state
.
nickname
}
onChange=
{
handleNicknameChanged
}
/>
<
p
className=
"text-sm"
>
{
t
(
"common.email"
)
}
{
t
(
"common.email"
)
}
<
span
className=
"text-sm text-gray-400 ml-1"
>
(Optional)
</
span
>
<
span
className=
"text-sm text-gray-400 ml-1"
>
(Optional)
</
span
>
</
p
>
</
p
>
<
input
type=
"text"
className=
"input-text"
value=
{
state
.
email
}
onChange=
{
handleEmailChanged
}
/>
<
input
type=
"text"
className=
"input-text"
value=
{
state
.
email
}
onChange=
{
handleEmailChanged
}
/>
<
div
className=
"
mt-4
w-full flex flex-row justify-end items-center space-x-2"
>
<
div
className=
"
pt-2
w-full flex flex-row justify-end items-center space-x-2"
>
<
span
className=
"btn-text"
onClick=
{
handleCloseBtnClick
}
>
<
span
className=
"btn-text"
onClick=
{
handleCloseBtnClick
}
>
{
t
(
"common.cancel"
)
}
{
t
(
"common.cancel"
)
}
</
span
>
</
span
>
...
...
web/src/components/UserAvatar.tsx
0 → 100644
View file @
bcee0bbf
import
{
MEMOS_LOGO_URL
}
from
"../helpers/consts"
;
interface
Props
{
avatarUrl
?:
string
;
className
?:
string
;
}
const
UserAvatar
=
(
props
:
Props
)
=>
{
const
{
avatarUrl
,
className
}
=
props
;
return
(
<
div
className=
{
`${className ?? ""} w-8 h-8 rounded-full bg-gray-100 dark:bg-zinc-800`
}
>
<
img
className=
"w-full h-full"
src=
{
avatarUrl
||
MEMOS_LOGO_URL
}
alt=
""
/>
</
div
>
);
};
export
default
UserAvatar
;
web/src/components/UserBanner.tsx
View file @
bcee0bbf
...
@@ -3,11 +3,10 @@ import { useTranslation } from "react-i18next";
...
@@ -3,11 +3,10 @@ import { useTranslation } from "react-i18next";
import
{
useLocationStore
,
useMemoStore
,
useTagStore
,
useUserStore
}
from
"../store/module"
;
import
{
useLocationStore
,
useMemoStore
,
useTagStore
,
useUserStore
}
from
"../store/module"
;
import
{
getMemoStats
}
from
"../helpers/api"
;
import
{
getMemoStats
}
from
"../helpers/api"
;
import
*
as
utils
from
"../helpers/utils"
;
import
*
as
utils
from
"../helpers/utils"
;
import
Icon
from
"./Icon"
;
import
Dropdown
from
"./common/Dropdown"
;
import
Dropdown
from
"./common/Dropdown"
;
import
showArchivedMemoDialog
from
"./ArchivedMemoDialog"
;
import
showArchivedMemoDialog
from
"./ArchivedMemoDialog"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
import
"../less/user-banner.less
"
;
import
UserAvatar
from
"./UserAvatar
"
;
const
UserBanner
=
()
=>
{
const
UserBanner
=
()
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
...
@@ -65,20 +64,29 @@ const UserBanner = () => {
...
@@ -65,20 +64,29 @@ const UserBanner = () => {
return
(
return
(
<>
<>
<
div
className=
"user-banner-container"
>
<
div
className=
"flex flex-row justify-between items-center relative w-full h-auto px-3 flex-nowrap shrink-0"
>
<
div
className=
"username-container"
onClick=
{
handleUsernameClick
}
>
<
span
className=
"username-text"
>
{
username
}
</
span
>
{
!
isVisitorMode
&&
user
?.
role
===
"HOST"
?
<
span
className=
"tag"
>
MOD
</
span
>
:
null
}
</
div
>
<
Dropdown
<
Dropdown
trigger=
{
<
Icon
.
MoreHorizontal
className=
"ml-2 w-5 h-auto cursor-pointer dark:text-gray-200"
/>
}
className=
"w-full"
actionsClassName=
"min-w-36"
trigger=
{
<
div
className=
"px-2 py-1 max-w-full flex flex-row justify-start items-center cursor-pointer rounded hover:shadow hover:bg-white dark:hover:bg-zinc-700"
onClick=
{
handleUsernameClick
}
>
<
UserAvatar
avatarUrl=
{
user
?.
avatarUrl
}
/>
<
span
className=
"px-1 text-lg font-medium text-slate-800 dark:text-gray-200 shrink truncate"
>
{
username
}
</
span
>
{
!
isVisitorMode
&&
user
?.
role
===
"HOST"
?
(
<
span
className=
"text-xs px-1 bg-blue-600 dark:bg-blue-800 rounded text-white dark:text-gray-200 shadow"
>
MOD
</
span
>
)
:
null
}
</
div
>
}
actionsClassName=
"min-w-[128px] max-w-full"
positionClassName=
"top-full mt-2"
actions=
{
actions=
{
<>
<>
{
!
userStore
.
isVisitorMode
()
&&
(
{
!
userStore
.
isVisitorMode
()
&&
(
<>
<>
<
button
<
button
className=
"w-full px-3
whitespace-nowrap
text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
className=
"w-full px-3
truncate
text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
onClick=
{
handleArchivedBtnClick
}
onClick=
{
handleArchivedBtnClick
}
>
>
<
span
className=
"mr-1"
>
🗃️
</
span
>
{
t
(
"sidebar.archived"
)
}
<
span
className=
"mr-1"
>
🗃️
</
span
>
{
t
(
"sidebar.archived"
)
}
...
@@ -86,14 +94,14 @@ const UserBanner = () => {
...
@@ -86,14 +94,14 @@ const UserBanner = () => {
</>
</>
)
}
)
}
<
button
<
button
className=
"w-full px-3
whitespace-nowrap
text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
className=
"w-full px-3
truncate
text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
onClick=
{
handleAboutBtnClick
}
onClick=
{
handleAboutBtnClick
}
>
>
<
span
className=
"mr-1"
>
🤠
</
span
>
{
t
(
"common.about"
)
}
<
span
className=
"mr-1"
>
🤠
</
span
>
{
t
(
"common.about"
)
}
</
button
>
</
button
>
{
!
userStore
.
isVisitorMode
()
&&
(
{
!
userStore
.
isVisitorMode
()
&&
(
<
button
<
button
className=
"w-full px-3
whitespace-nowrap
text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
className=
"w-full px-3
truncate
text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
onClick=
{
handleSignOutBtnClick
}
onClick=
{
handleSignOutBtnClick
}
>
>
<
span
className=
"mr-1"
>
👋
</
span
>
{
t
(
"common.sign-out"
)
}
<
span
className=
"mr-1"
>
👋
</
span
>
{
t
(
"common.sign-out"
)
}
...
@@ -103,18 +111,18 @@ const UserBanner = () => {
...
@@ -103,18 +111,18 @@ const UserBanner = () => {
}
}
/>
/>
</
div
>
</
div
>
<
div
className=
"
amount-text-container
"
>
<
div
className=
"
flex flex-row justify-between items-start w-full px-6 select-none shrink-0 pb-2
"
>
<
div
className=
"
status-text memos-tex
t"
>
<
div
className=
"
flex flex-col justify-start items-star
t"
>
<
span
className=
"
amount-text
"
>
{
memoAmount
}
</
span
>
<
span
className=
"
font-bold text-2xl opacity-80 leading-10 text-slate-600 dark:text-gray-300
"
>
{
memoAmount
}
</
span
>
<
span
className=
"t
ype-text
"
>
{
t
(
"amount-text.memo"
,
{
count
:
memoAmount
})
}
</
span
>
<
span
className=
"t
ext-gray-400 text-xs font-mono
"
>
{
t
(
"amount-text.memo"
,
{
count
:
memoAmount
})
}
</
span
>
</
div
>
</
div
>
<
div
className=
"
status-text tags-tex
t"
>
<
div
className=
"
flex flex-col justify-start items-star
t"
>
<
span
className=
"
amount-text
"
>
{
tags
.
length
}
</
span
>
<
span
className=
"
font-bold text-2xl opacity-80 leading-10 text-slate-600 dark:text-gray-300
"
>
{
tags
.
length
}
</
span
>
<
span
className=
"t
ype-text
"
>
{
t
(
"amount-text.tag"
,
{
count
:
tags
.
length
})
}
</
span
>
<
span
className=
"t
ext-gray-400 text-xs font-mono
"
>
{
t
(
"amount-text.tag"
,
{
count
:
tags
.
length
})
}
</
span
>
</
div
>
</
div
>
<
div
className=
"
status-text duration-tex
t"
>
<
div
className=
"
flex flex-col justify-start items-star
t"
>
<
span
className=
"
amount-text
"
>
{
createdDays
}
</
span
>
<
span
className=
"
font-bold text-2xl opacity-80 leading-10 text-slate-600 dark:text-gray-300
"
>
{
createdDays
}
</
span
>
<
span
className=
"t
ype-text
"
>
{
t
(
"amount-text.day"
,
{
count
:
createdDays
})
}
</
span
>
<
span
className=
"t
ext-gray-400 text-xs font-mono
"
>
{
t
(
"amount-text.day"
,
{
count
:
createdDays
})
}
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
</>
</>
...
...
web/src/components/common/Dropdown.tsx
View file @
bcee0bbf
...
@@ -44,8 +44,8 @@ const Dropdown: React.FC<Props> = (props: Props) => {
...
@@ -44,8 +44,8 @@ const Dropdown: React.FC<Props> = (props: Props) => {
)
}
)
}
<
div
<
div
className=
{
`w-auto absolute flex flex-col justify-start items-start bg-white dark:bg-zinc-700 z-10 p-1 rounded-md shadow ${
className=
{
`w-auto absolute flex flex-col justify-start items-start bg-white dark:bg-zinc-700 z-10 p-1 rounded-md shadow ${
actionsClassName ?? "
"
dropdownStatus ? "" : "!hidden
"
} ${
dropdownStatus ? "" : "!hidden
"} ${positionClassName ?? "top-full right-0 mt-1"}`
}
} ${
actionsClassName ?? "
"} ${positionClassName ?? "top-full right-0 mt-1"}`
}
>
>
{
actions
}
{
actions
}
</
div
>
</
div
>
...
...
web/src/helpers/consts.ts
View file @
bcee0bbf
...
@@ -23,3 +23,5 @@ export const TAB_SPACE_WIDTH = 2;
...
@@ -23,3 +23,5 @@ export const TAB_SPACE_WIDTH = 2;
// default fetch memo amount
// default fetch memo amount
export
const
DEFAULT_MEMO_LIMIT
=
30
;
export
const
DEFAULT_MEMO_LIMIT
=
30
;
export
const
MEMOS_LOGO_URL
=
"https://usememos.com/logo.png"
;
web/src/helpers/utils.ts
View file @
bcee0bbf
...
@@ -148,3 +148,12 @@ export function getSystemColorScheme() {
...
@@ -148,3 +148,12 @@ export function getSystemColorScheme() {
return
"light"
;
return
"light"
;
}
}
}
}
export
function
convertFileToBase64
(
file
:
File
):
Promise
<
string
>
{
return
new
Promise
<
string
>
((
resolve
,
reject
)
=>
{
const
reader
=
new
FileReader
();
reader
.
readAsDataURL
(
file
);
reader
.
onload
=
()
=>
resolve
(
reader
.
result
?.
toString
()
||
""
);
reader
.
onerror
=
(
error
)
=>
reject
(
error
);
});
}
web/src/less/toast.less
View file @
bcee0bbf
.toast-list-container {
.toast-list-container {
@apply flex flex-col justify-start items-end fixed top-2 right-4 z-1000 max-h-full;
@apply flex flex-col justify-start items-end fixed top-2 right-4 max-h-full;
z-index: 9999;
> .toast-wrapper {
> .toast-wrapper {
@apply flex flex-col justify-start items-start relative left-full invisible text-base cursor-pointer shadow-lg rounded bg-white mt-6 py-2 px-4;
@apply flex flex-col justify-start items-start relative left-full invisible text-base cursor-pointer shadow-lg rounded bg-white mt-6 py-2 px-4;
...
...
web/src/less/user-banner.less
deleted
100644 → 0
View file @
096a71c5
.user-banner-container {
@apply flex flex-row justify-between items-center relative w-full h-10 px-6 flex-nowrap mb-1 shrink-0;
> .username-container {
@apply shrink flex flex-row justify-start items-center flex-nowrap truncate;
> .username-text {
@apply font-bold text-lg pr-1 text-slate-800 dark:text-gray-200 cursor-pointer shrink truncate;
}
> .tag {
@apply text-xs px-1 bg-blue-600 dark:bg-blue-800 rounded text-white dark:text-gray-200 shadow;
}
}
> .action-btn {
@apply shrink-0 select-none border-none;
&.menu-popup-btn {
@apply flex flex-col justify-center items-center w-9 h-10 -mr-2 cursor-pointer;
> .icon-img {
@apply w-5 h-auto;
}
}
}
}
.amount-text-container {
@apply flex flex-row justify-between items-start w-full px-6 select-none shrink-0 pb-4;
> .status-text {
@apply flex flex-col justify-start items-start;
> .amount-text {
@apply font-bold text-2xl opacity-80 leading-10 text-slate-600 dark:text-gray-300;
}
> .type-text {
@apply text-gray-400 text-xs font-mono;
}
}
}
web/src/locales/en.json
View file @
bcee0bbf
...
@@ -7,6 +7,7 @@
...
@@ -7,6 +7,7 @@
"repeat-password"
:
"Repeat the password"
,
"repeat-password"
:
"Repeat the password"
,
"new-password"
:
"New password"
,
"new-password"
:
"New password"
,
"repeat-new-password"
:
"Repeat the new password"
,
"repeat-new-password"
:
"Repeat the new password"
,
"avatar"
:
"Avatar"
,
"username"
:
"Username"
,
"username"
:
"Username"
,
"nickname"
:
"Nickname"
,
"nickname"
:
"Nickname"
,
"save"
:
"Save"
,
"save"
:
"Save"
,
...
...
web/src/types/modules/user.d.ts
View file @
bcee0bbf
...
@@ -13,6 +13,7 @@ interface User {
...
@@ -13,6 +13,7 @@ interface User {
email
:
string
;
email
:
string
;
nickname
:
string
;
nickname
:
string
;
openId
:
string
;
openId
:
string
;
avatarUrl
:
string
;
userSettingList
:
UserSetting
[];
userSettingList
:
UserSetting
[];
setting
:
Setting
;
setting
:
Setting
;
...
@@ -31,6 +32,7 @@ interface UserPatch {
...
@@ -31,6 +32,7 @@ interface UserPatch {
username
?:
string
;
username
?:
string
;
email
?:
string
;
email
?:
string
;
nickname
?:
string
;
nickname
?:
string
;
avatarUrl
?:
string
;
password
?:
string
;
password
?:
string
;
resetOpenId
?:
boolean
;
resetOpenId
?:
boolean
;
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment