diff --git a/app/client/web/src/access/Access.tsx b/app/client/web/src/access/Access.tsx
index e96fb058..ddf473eb 100644
--- a/app/client/web/src/access/Access.tsx
+++ b/app/client/web/src/access/Access.tsx
@@ -17,15 +17,15 @@ import left from '../images/login.png'
import {
IconLock,
IconUser,
+ IconUsers,
IconSettings,
IconServer,
IconKey,
} from '@tabler/icons-react'
+import { modals } from '@mantine/modals';
export function Access() {
const { state, actions } = useAccess()
- const [alertOpened, { open: alertOpen, close: alertClose }] =
- useDisclosure(false)
const [urlOpened, { open: urlOpen, close: urlClose }] = useDisclosure(false)
const [otpOpened, { open: otpOpen, close: otpClose }] = useDisclosure(false)
const [disabled, setDisabled] = useState(false)
@@ -58,7 +58,19 @@ export function Access() {
}
otpOpen()
} else {
- alertOpen()
+ modals.openConfirmModal({
+ title: state.strings.operationFailed,
+ withCloseButton: true,
+ overlayProps: {
+ backgroundOpacity: 0.55,
+ blur: 3,
+ },
+ children: (
+
{state.strings.tryAgain}
+ ),
+ cancelProps: { display: 'none' },
+ confirmProps: { display: 'none' },
+ });
}
}
actions.setLoading(false)
@@ -228,6 +240,7 @@ export function Access() {
value={state.username}
leftSectionPointerEvents="none"
leftSection={}
+ rightSection={state.taken ? : null}
placeholder={state.strings.username}
onChange={(event) =>
actions.setUsername(event.currentTarget.value)
@@ -331,6 +344,7 @@ export function Access() {
opened={urlOpened}
onClose={urlClose}
withCloseButton={false}
+ overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
centered
>
actions.setNode(event.currentTarget.value)}
/>
-
- {state.strings.tryAgain}
-
diff --git a/app/client/web/src/identity/Identity.tsx b/app/client/web/src/identity/Identity.tsx
index 377108a3..a6343e23 100644
--- a/app/client/web/src/identity/Identity.tsx
+++ b/app/client/web/src/identity/Identity.tsx
@@ -18,6 +18,10 @@ export function Identity({ settings, contacts }: { settings: () => void, contact
const logout = () => modals.openConfirmModal({
title: state.strings.confirmLogout,
withCloseButton: false,
+ overlayProps: {
+ backgroundOpacity: 0.55,
+ blur: 3,
+ },
children: (
actions.setAll(ev.currentTarget.checked)} />
),
diff --git a/app/client/web/src/session/Session.tsx b/app/client/web/src/session/Session.tsx
index 38177dfd..893e4d59 100644
--- a/app/client/web/src/session/Session.tsx
+++ b/app/client/web/src/session/Session.tsx
@@ -26,7 +26,7 @@ export function Session() {
<>
{ tab === 'settings' && (
-
+
)}
@@ -61,7 +61,7 @@ export function Session() {
-
+
)}
diff --git a/app/client/web/src/settings/Settings.module.css b/app/client/web/src/settings/Settings.module.css
index a009e5eb..a68b8062 100644
--- a/app/client/web/src/settings/Settings.module.css
+++ b/app/client/web/src/settings/Settings.module.css
@@ -1,3 +1,16 @@
+.change {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.control {
+ display: flex;
+ gap: 16px;
+ padding-top: 8px;
+ justify-content: flex-end;
+}
+
.settings {
display: flex;
flex-direction: column;
diff --git a/app/client/web/src/settings/Settings.tsx b/app/client/web/src/settings/Settings.tsx
index 2f62ee18..6e0db941 100644
--- a/app/client/web/src/settings/Settings.tsx
+++ b/app/client/web/src/settings/Settings.tsx
@@ -1,16 +1,22 @@
import { useSettings } from './useSettings.hook';
-import { Radio, Group, Select, Switch, Text, Image, Button, UnstyledButton } from '@mantine/core';
+import { Modal, TextInput, PasswordInput, Radio, Group, Select, Switch, Text, Image, Button, UnstyledButton } from '@mantine/core';
import classes from './Settings.module.css';
-import { IconClock, IconCalendar, IconVideo, IconMicrophone, IconWorld, IconBrightness, IconTicket, IconCloudLock, IconBell, IconEye, IconBook, IconMapPin, IconLogout, IconLogin } from '@tabler/icons-react'
+import { IconLock, IconUser, IconClock, IconCalendar, IconUsers, IconVideo, IconMicrophone, IconWorld, IconBrightness, IconTicket, IconCloudLock, IconBell, IconEye, IconBook, IconMapPin, IconLogout, IconLogin } from '@tabler/icons-react'
import avatar from '../images/avatar.png'
import { modals } from '@mantine/modals';
+import { useDisclosure } from '@mantine/hooks'
-export function Settings() {
+export function Settings({ showLogout }) {
const { state, actions } = useSettings();
+ const [changeOpened, { open: changeOpen, close: changeClose }] = useDisclosure(false)
const logout = () => modals.openConfirmModal({
title: state.strings.confirmLogout,
withCloseButton: false,
+ overlayProps: {
+ backgroundOpacity: 0.55,
+ blur: 3,
+ },
children: (
actions.setAll(ev.currentTarget.checked)} />
),
@@ -18,6 +24,31 @@ export function Settings() {
onConfirm: actions.logout,
});
+ const setLogin = async () => {
+ try {
+throw new Error("NO");
+ await actions.setLogin();
+ changeClose();
+ }
+ catch (err) {
+ console.log(err);
+ modals.openConfirmModal({
+ title: state.strings.operationFailed,
+ withCloseButton: true,
+ overlayProps: {
+ backgroundOpacity: 0.55,
+ blur: 3,
+ },
+ children: (
+ {state.strings.tryAgain}
+ ),
+ cancelProps: { display: 'none' },
+ confirmProps: { display: 'none' },
+ });
+
+ }
+ }
+
return (
<>
{ state.profileSet && (
@@ -93,12 +124,14 @@ export function Settings() {
-
-
-
+ { showLogout && (
+
+
+
+
+
{ state.strings.logout }
-
{ state.strings.logout }
-
+ )}
@@ -110,7 +143,7 @@ export function Settings() {
-
{ state.strings.changeLogin }
+
{ state.strings.changeLogin }
@@ -205,6 +238,62 @@ export function Settings() {
)}
+
+
+
}
+ rightSection={state.taken ?
: null}
+ placeholder={state.strings.username}
+ onChange={(event) =>
+ actions.setUsername(event.currentTarget.value)
+ }
+ error={state.taken}
+ />
+
}
+ placeholder={state.strings.password}
+ onChange={(event) =>
+ actions.setPassword(event.currentTarget.value)
+ }
+ />
+
}
+ placeholder={state.strings.confirmPassword}
+ onChange={(event) =>
+ actions.setConfirm(event.currentTarget.value)
+ }
+ />
+
+
+
+
+
+
>
);
}
diff --git a/app/client/web/src/settings/useSettings.hook.ts b/app/client/web/src/settings/useSettings.hook.ts
index ba3c0d28..6735b577 100644
--- a/app/client/web/src/settings/useSettings.hook.ts
+++ b/app/client/web/src/settings/useSettings.hook.ts
@@ -1,13 +1,16 @@
-import { useEffect, useState, useContext } from 'react'
+import { useEffect, useState, useContext, useRef } from 'react'
import { AppContext } from '../context/AppContext';
import { DisplayContext } from '../context/DisplayContext';
import { ContextType } from '../context/ContextType';
import { Session, Settings, Identity, type Profile, type Config } from 'databag-client-sdk'
+const DEBOUNCE_MS = 1000;
+
export function useSettings() {
const display = useContext(DisplayContext) as ContextType;
const app = useContext(AppContext) as ContextType;
+ const debounce = useRef(useRef(setTimeout(() => {}, 0)));
const [state, setState] = useState({
config: {} as Config,
@@ -26,6 +29,11 @@ export function useSettings() {
timeFormat: '12h',
dateFormat: 'mm/dd',
all: false,
+ password: '',
+ confirm: '',
+ username: '',
+ taken: false,
+ checked: true,
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -50,7 +58,7 @@ export function useSettings() {
}
settings.addConfigListener(setConfig);
const setProfile = (profile: Profile) => {
- updateState({ profile, profileSet: true, imageUrl: identity.getProfileImageUrl() })
+ updateState({ profile, username: profile.handle, profileSet: true, imageUrl: identity.getProfileImageUrl() })
}
identity.addProfileListener(setProfile)
return () => {
@@ -168,6 +176,30 @@ console.log(audioInputs, videoInputs);
logout: async () => {
await app.actions.accountLogout(state.all);
},
+ setUsername: (username) => {
+ updateState({ username, taken: false, checked: false });
+ clearTimeout(debounce.current);
+ if (!username || username === state.profile.handle) {
+ updateState({ available: true, checked: true});
+ }
+ else {
+ debounce.current = setTimeout(async () => {
+ const { settings } = getSession();
+ const available = await settings.getUsernameStatus(username);
+ updateState({ taken: !available, checked: true });
+ }, DEBOUNCE_MS);
+ }
+ },
+ setPassword: (password) => {
+ updateState({ password });
+ },
+ setConfirm: (confirm) => {
+ updateState({ confirm });
+ },
+ setLogin: async () => {
+ const { settings } = getSession();
+ await settings.setLogin(state.username, state.password);
+ }
}
return { state, actions }
diff --git a/app/sdk/src/index.ts b/app/sdk/src/index.ts
index 5e4e0bba..81433342 100644
--- a/app/sdk/src/index.ts
+++ b/app/sdk/src/index.ts
@@ -51,7 +51,7 @@ export class DatabagSDK {
}
public async username(name: string, token: string, node: string, secure: boolean): Promise {
- return await getUsername(name, token, node, secure);
+ return await getUsername(name, token, null, node, secure);
}
public async login(handle: string, password: string, node: string, secure: boolean, mfaCode: string | null, params: SessionParams): Promise {
diff --git a/app/sdk/src/net/getUsername.ts b/app/sdk/src/net/getUsername.ts
index 73c6a180..5d5f410a 100644
--- a/app/sdk/src/net/getUsername.ts
+++ b/app/sdk/src/net/getUsername.ts
@@ -1,7 +1,7 @@
import axios from 'redaxios';
-export async function getUsername(name: string, token: string, node: string, secure: boolean): Promise {
- const param = token ? `&token=${token}` : '';
+export async function getUsername(name: string, token: string | null, agent: string | null, node: string, secure: boolean): Promise {
+ const param = token ? `&token=${token}` : agent ? `&agent=${agent}` : '';
const username = encodeURIComponent(name)
const endpoint = `http${secure ? 's' : ''}://${node}/account/username?name=${username}${param}`;
const response = await axios.get(endpoint);
diff --git a/app/sdk/src/settings.ts b/app/sdk/src/settings.ts
index 43c42fc8..beae7b61 100644
--- a/app/sdk/src/settings.ts
+++ b/app/sdk/src/settings.ts
@@ -211,7 +211,7 @@ export class SettingsModule implements Settings {
public async getUsernameStatus(username: string): Promise {
const { node, secure, token } = this;
- return await getUsername(username, token, node, secure);
+ return await getUsername(username, null, token, node, secure);
}
public async setLogin(username: string, password: string): Promise {