Compare commits
6 Commits
b85b6833b6
...
2d48d2f536
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d48d2f536 | ||
|
|
4214ed9e10 | ||
|
|
198493cac1 | ||
|
|
f57347b834 | ||
|
|
f9dff68339 | ||
|
|
48bd884690 |
@@ -180,18 +180,6 @@ export default class LingChairClient {
|
|||||||
else
|
else
|
||||||
throw new CallbackError(re)
|
throw new CallbackError(re)
|
||||||
}
|
}
|
||||||
async register(args: {
|
|
||||||
nickname: string,
|
|
||||||
username?: string,
|
|
||||||
password: string,
|
|
||||||
}) {
|
|
||||||
try {
|
|
||||||
await this.registerOrThrow(args)
|
|
||||||
return true
|
|
||||||
} catch (_) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getBaseHttpUrl() {
|
getBaseHttpUrl() {
|
||||||
const url = new URL(this.server_url)
|
const url = new URL(this.server_url)
|
||||||
return (({
|
return (({
|
||||||
@@ -204,6 +192,17 @@ export default class LingChairClient {
|
|||||||
getUrlForFileByHash(file_hash?: string, defaultUrl?: string) {
|
getUrlForFileByHash(file_hash?: string, defaultUrl?: string) {
|
||||||
return file_hash ? (this.getBaseHttpUrl() + '/uploaded_files/' + file_hash) : defaultUrl
|
return file_hash ? (this.getBaseHttpUrl() + '/uploaded_files/' + file_hash) : defaultUrl
|
||||||
}
|
}
|
||||||
|
async register(args: {
|
||||||
|
nickname: string,
|
||||||
|
username?: string,
|
||||||
|
password: string,
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
return await this.registerOrThrow(args)
|
||||||
|
} catch (_) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
async registerOrThrow({
|
async registerOrThrow({
|
||||||
nickname,
|
nickname,
|
||||||
username,
|
username,
|
||||||
@@ -216,10 +215,11 @@ export default class LingChairClient {
|
|||||||
const re = await this.invoke('User.register', {
|
const re = await this.invoke('User.register', {
|
||||||
nickname,
|
nickname,
|
||||||
username,
|
username,
|
||||||
password,
|
password: crypto.createHash('sha256').update(password).digest('hex'),
|
||||||
})
|
})
|
||||||
if (re.code != 200)
|
if (re.code != 200)
|
||||||
throw new CallbackError(re)
|
throw new CallbackError(re)
|
||||||
|
return re.data!.user_id as string
|
||||||
}
|
}
|
||||||
async uploadFile({
|
async uploadFile({
|
||||||
chatId,
|
chatId,
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ import isMobileUI from "../utils/isMobileUI.ts"
|
|||||||
import useEventListener from "../utils/useEventListener.ts"
|
import useEventListener from "../utils/useEventListener.ts"
|
||||||
import AvatarMySelf from "./AvatarMySelf.tsx"
|
import AvatarMySelf from "./AvatarMySelf.tsx"
|
||||||
import MainSharedContext from './MainSharedContext.ts'
|
import MainSharedContext from './MainSharedContext.ts'
|
||||||
import React from "react"
|
import * as React from 'react'
|
||||||
import { BrowserRouter, Outlet, Route, Routes } from "react-router"
|
import { BrowserRouter, Outlet, Route, Routes } from "react-router"
|
||||||
import LoginDialog from "./main-page/LoginDialog.tsx"
|
import LoginDialog from "./main-page/LoginDialog.tsx"
|
||||||
import useAsyncEffect from "../utils/useAsyncEffect.ts"
|
import useAsyncEffect from "../utils/useAsyncEffect.ts"
|
||||||
import performAuth from "../performAuth.ts"
|
import performAuth from "../performAuth.ts"
|
||||||
import { CallbackError } from "lingchair-client-protocol"
|
import { CallbackError } from "lingchair-client-protocol"
|
||||||
|
import showCircleProgressDialog from "./showCircleProgressDialog.ts"
|
||||||
|
import RegisterDialog from "./main-page/RegisterDialog.tsx"
|
||||||
|
import sleep from "../utils/sleep.ts"
|
||||||
|
|
||||||
export default function Main() {
|
export default function Main() {
|
||||||
const [showLoginDialog, setShowLoginDialog] = React.useState(false)
|
|
||||||
|
|
||||||
// 多页面切换
|
// 多页面切换
|
||||||
const navigationRef = React.useRef<HTMLElement>()
|
const navigationRef = React.useRef<HTMLElement>()
|
||||||
const [currentShowPage, setCurrentShowPage] = React.useState('Recents')
|
const [currentShowPage, setCurrentShowPage] = React.useState('Recents')
|
||||||
@@ -20,14 +21,19 @@ export default function Main() {
|
|||||||
setCurrentShowPage((event.target as HTMLElementWithValue).value)
|
setCurrentShowPage((event.target as HTMLElementWithValue).value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [showLoginDialog, setShowLoginDialog] = React.useState(false)
|
||||||
|
const [showRegisterDialog, setShowRegisterDialog] = React.useState(false)
|
||||||
|
|
||||||
const sharedContext = {
|
const sharedContext = {
|
||||||
ui_functions: React.useRef({
|
ui_functions: React.useRef({
|
||||||
|
|
||||||
}),
|
}),
|
||||||
setShowLoginDialog,
|
setShowLoginDialog,
|
||||||
|
setShowRegisterDialog,
|
||||||
}
|
}
|
||||||
|
|
||||||
useAsyncEffect(async () => {
|
useAsyncEffect(async () => {
|
||||||
|
const waitingForAuth = showCircleProgressDialog("验证中...")
|
||||||
try {
|
try {
|
||||||
await performAuth({})
|
await performAuth({})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -35,6 +41,9 @@ export default function Main() {
|
|||||||
if (e.code == 401 || e.code == 400)
|
if (e.code == 401 || e.code == 400)
|
||||||
setShowLoginDialog(true)
|
setShowLoginDialog(true)
|
||||||
}
|
}
|
||||||
|
// 动画都没来得及, 稍微等一下 (
|
||||||
|
await sleep(100)
|
||||||
|
waitingForAuth.open = false
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -53,6 +62,7 @@ export default function Main() {
|
|||||||
<Outlet />
|
<Outlet />
|
||||||
}
|
}
|
||||||
<LoginDialog open={showLoginDialog} />
|
<LoginDialog open={showLoginDialog} />
|
||||||
|
<RegisterDialog open={showRegisterDialog} />
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Default: 侧边列表提供列表切换
|
* Default: 侧边列表提供列表切换
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { createContext } from 'react'
|
import { createContext } from 'react'
|
||||||
|
|
||||||
const MainSharedContext = createContext({})
|
type shared = {
|
||||||
|
ui_functions: React.MutableRefObject<{
|
||||||
|
|
||||||
|
}>
|
||||||
|
setShowLoginDialog: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
|
setShowRegisterDialog: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
|
}
|
||||||
|
const MainSharedContext = createContext({} as shared)
|
||||||
|
|
||||||
export default MainSharedContext
|
export default MainSharedContext
|
||||||
|
|||||||
@@ -2,17 +2,27 @@ import * as React from 'react'
|
|||||||
import { Button, Dialog, TextField } from "mdui"
|
import { Button, Dialog, TextField } from "mdui"
|
||||||
|
|
||||||
import performAuth from '../../performAuth.ts'
|
import performAuth from '../../performAuth.ts'
|
||||||
import useEventListener from '../../utils/useEventListener.ts'
|
|
||||||
import showSnackbar from '../../utils/showSnackbar.ts'
|
import showSnackbar from '../../utils/showSnackbar.ts'
|
||||||
|
import MainSharedContext from '../MainSharedContext.ts'
|
||||||
|
|
||||||
export default function LoginDialog({ ...props }: { open: boolean } & React.HTMLAttributes<Dialog>) {
|
export default function LoginDialog({ ...props }: { open: boolean } & React.HTMLAttributes<Dialog>) {
|
||||||
|
const shared = React.useContext(MainSharedContext)
|
||||||
|
|
||||||
const loginDialogRef = React.useRef<Dialog>(null)
|
const loginDialogRef = React.useRef<Dialog>(null)
|
||||||
const loginButtonRef = React.useRef<Button>(null)
|
|
||||||
const registerButtonRef = React.useRef<Button>(null)
|
|
||||||
const loginInputAccountRef = React.useRef<TextField>(null)
|
const loginInputAccountRef = React.useRef<TextField>(null)
|
||||||
const loginInputPasswordRef = React.useRef<TextField>(null)
|
const loginInputPasswordRef = React.useRef<TextField>(null)
|
||||||
|
|
||||||
useEventListener(loginButtonRef, 'click', async () => {
|
return (
|
||||||
|
<mdui-dialog {...props} headline="登录" ref={loginDialogRef}>
|
||||||
|
|
||||||
|
<mdui-text-field label="用户 ID / 用户名" ref={loginInputAccountRef}></mdui-text-field>
|
||||||
|
<div style={{
|
||||||
|
height: "10px",
|
||||||
|
}}></div>
|
||||||
|
<mdui-text-field label="密码" type="password" toggle-password ref={loginInputPasswordRef}></mdui-text-field>
|
||||||
|
|
||||||
|
<mdui-button slot="action" variant="text" onClick={() => shared.setShowRegisterDialog(true)}>注册</mdui-button>
|
||||||
|
<mdui-button slot="action" variant="text" onClick={async () => {
|
||||||
const account = loginInputAccountRef.current!.value
|
const account = loginInputAccountRef.current!.value
|
||||||
const password = loginInputPasswordRef.current!.value
|
const password = loginInputPasswordRef.current!.value
|
||||||
|
|
||||||
@@ -26,19 +36,7 @@ export default function LoginDialog({ ...props }: { open: boolean } & React.HTML
|
|||||||
if (e instanceof Error)
|
if (e instanceof Error)
|
||||||
showSnackbar({ message: '登录失败: ' + e.message })
|
showSnackbar({ message: '登录失败: ' + e.message })
|
||||||
}
|
}
|
||||||
})
|
}}>登录</mdui-button>
|
||||||
|
|
||||||
return (
|
|
||||||
<mdui-dialog {...props} headline="登录" ref={loginDialogRef}>
|
|
||||||
|
|
||||||
<mdui-text-field label="用户 ID / 用户名" ref={loginInputAccountRef}></mdui-text-field>
|
|
||||||
<div style={{
|
|
||||||
height: "10px",
|
|
||||||
}}></div>
|
|
||||||
<mdui-text-field label="密码" type="password" toggle-password ref={loginInputPasswordRef}></mdui-text-field>
|
|
||||||
|
|
||||||
<mdui-button slot="action" variant="text" ref={registerButtonRef}>注册</mdui-button>
|
|
||||||
<mdui-button slot="action" variant="text" ref={loginButtonRef}>登录</mdui-button>
|
|
||||||
</mdui-dialog>
|
</mdui-dialog>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
69
client/ui/main-page/RegisterDialog.tsx
Normal file
69
client/ui/main-page/RegisterDialog.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { Button, Dialog, TextField } from "mdui"
|
||||||
|
import MainSharedContext from '../MainSharedContext'
|
||||||
|
import showSnackbar from '../../utils/showSnackbar'
|
||||||
|
import showCircleProgressDialog from '../showCircleProgressDialog'
|
||||||
|
import getClient from '../../getClient'
|
||||||
|
import performAuth from '../../performAuth'
|
||||||
|
|
||||||
|
export default function RegisterDialog({ ...props }: { open: boolean } & React.HTMLAttributes<Dialog>) {
|
||||||
|
const shared = React.useContext(MainSharedContext)
|
||||||
|
|
||||||
|
const registerInputUserNameRef = React.useRef<TextField>(null)
|
||||||
|
const registerInputNickNameRef = React.useRef<TextField>(null)
|
||||||
|
const registerInputPasswordRef = React.useRef<TextField>(null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mdui-dialog headline="注册" {...props}>
|
||||||
|
|
||||||
|
<mdui-text-field label="用户名 (可选)" ref={registerInputUserNameRef}></mdui-text-field>
|
||||||
|
<div style={{
|
||||||
|
height: "10px",
|
||||||
|
}}></div>
|
||||||
|
<mdui-text-field label="昵称" ref={registerInputNickNameRef}></mdui-text-field>
|
||||||
|
<div style={{
|
||||||
|
height: "10px",
|
||||||
|
}}></div>
|
||||||
|
<mdui-text-field label="密码" type="password" toggle-password ref={registerInputPasswordRef}></mdui-text-field>
|
||||||
|
|
||||||
|
<mdui-button slot="action" variant="text" onClick={() => shared.setShowRegisterDialog(false)}>返回</mdui-button>
|
||||||
|
<mdui-button slot="action" variant="text" onClick={async () => {
|
||||||
|
const waitingForRegister = showCircleProgressDialog("注册中...")
|
||||||
|
|
||||||
|
const username = registerInputUserNameRef.current!.value
|
||||||
|
|
||||||
|
let user_id: string
|
||||||
|
try {
|
||||||
|
user_id = await getClient().registerOrThrow({
|
||||||
|
username: username,
|
||||||
|
nickname: registerInputNickNameRef.current!.value,
|
||||||
|
password: registerInputPasswordRef.current!.value,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
user_id = ''
|
||||||
|
if (e instanceof Error) {
|
||||||
|
waitingForRegister.open = false
|
||||||
|
showSnackbar({ message: '注册失败: ' + e.message })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
waitingForRegister.open = false
|
||||||
|
|
||||||
|
const waitingForLogin = showCircleProgressDialog("登录中...")
|
||||||
|
|
||||||
|
try {
|
||||||
|
await performAuth({
|
||||||
|
account: username == '' ? username : user_id,
|
||||||
|
password: registerInputPasswordRef.current!.value,
|
||||||
|
})
|
||||||
|
location.reload()
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
showSnackbar({ message: '登录失败: ' + e.message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
waitingForLogin.open = false
|
||||||
|
}}>注册</mdui-button>
|
||||||
|
</mdui-dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
23
client/ui/showCircleProgressDialog.ts
Normal file
23
client/ui/showCircleProgressDialog.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { $, dialog } from 'mdui'
|
||||||
|
|
||||||
|
export default function showCircleProgressDialog(text: string) {
|
||||||
|
const d = dialog({
|
||||||
|
body: `
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<mdui-circular-progress style="margin-left: 3px"></mdui-circular-progress>
|
||||||
|
<span style="margin-left: 20px;"></span>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
closeOnEsc: false,
|
||||||
|
closeOnOverlayClick: false,
|
||||||
|
})
|
||||||
|
$(d).addClass('waiting-dialog').find('span').text(text)
|
||||||
|
$(d.shadowRoot).append(`
|
||||||
|
<style>
|
||||||
|
.body {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`)
|
||||||
|
return d
|
||||||
|
}
|
||||||
1
client/utils/sleep.ts
Normal file
1
client/utils/sleep.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export default (t: number) => new Promise((res) => setTimeout(res, t))
|
||||||
Reference in New Issue
Block a user