mirror of
https://github.com/balzack/databag.git
synced 2025-05-04 07:25:15 +00:00
adding profile detail modal
This commit is contained in:
parent
4c7f5315cc
commit
214782d9c9
@ -16,7 +16,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
gap: 8px;
|
gap: 2px;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
@ -115,20 +115,19 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.radio {
|
.radio {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -4px;
|
top: -8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entryIcon {
|
.entryIcon {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
display: flex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrySet {
|
.entrySet {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
white-space: preserve;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entryUnset {
|
.entryUnset {
|
||||||
@ -166,6 +165,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
max-width: 40%;
|
max-width: 40%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
top: -4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import { useSettings } from './useSettings.hook';
|
import { useSettings } from './useSettings.hook';
|
||||||
import { Modal, TextInput, PasswordInput, Radio, Group, Select, Switch, Text, Image, Button, UnstyledButton } from '@mantine/core';
|
import { Modal, Textarea, TextInput, PasswordInput, Radio, Group, Select, Switch, Text, Image, Button, UnstyledButton } from '@mantine/core';
|
||||||
import classes from './Settings.module.css';
|
import classes from './Settings.module.css';
|
||||||
import { IconLock, IconUser, IconClock, IconCalendar, IconUsers, IconVideo, IconMicrophone, IconWorld, IconBrightness, IconTicket, IconCloudLock, IconBell, IconEye, IconBook, IconMapPin, IconLogout, IconLogin } from '@tabler/icons-react'
|
import { IconLock, IconUser, IconClock, IconIdBadge, 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 avatar from '../images/avatar.png'
|
||||||
import { modals } from '@mantine/modals';
|
import { modals } from '@mantine/modals';
|
||||||
import { useDisclosure } from '@mantine/hooks'
|
import { useDisclosure } from '@mantine/hooks'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
export function Settings({ showLogout }) {
|
export function Settings({ showLogout }) {
|
||||||
const { state, actions } = useSettings();
|
const { state, actions } = useSettings();
|
||||||
const [changeOpened, { open: changeOpen, close: changeClose }] = useDisclosure(false)
|
const [changeOpened, { open: changeOpen, close: changeClose }] = useDisclosure(false)
|
||||||
|
const [detailsOpened, { open: detailsOpen, close: detailsClose }] = useDisclosure(false)
|
||||||
|
const [savingLogin, setSavingLogin] = useState(false);
|
||||||
|
const [savingDetails, setSavingDetails] = useState(false);
|
||||||
|
|
||||||
const logout = () => modals.openConfirmModal({
|
const logout = () => modals.openConfirmModal({
|
||||||
title: state.strings.confirmLogout,
|
title: state.strings.confirmLogout,
|
||||||
@ -24,28 +28,57 @@ export function Settings({ showLogout }) {
|
|||||||
onConfirm: actions.logout,
|
onConfirm: actions.logout,
|
||||||
});
|
});
|
||||||
|
|
||||||
const setLogin = async () => {
|
const setDetails = async () => {
|
||||||
try {
|
if (!savingDetails) {
|
||||||
throw new Error("NO");
|
setSavingDetails(true);
|
||||||
await actions.setLogin();
|
try {
|
||||||
changeClose();
|
await actions.setDetails();
|
||||||
|
detailsClose();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: state.strings.operationFailed,
|
||||||
|
withCloseButton: true,
|
||||||
|
overlayProps: {
|
||||||
|
backgroundOpacity: 0.55,
|
||||||
|
blur: 3,
|
||||||
|
},
|
||||||
|
children: (
|
||||||
|
<Text>{state.strings.tryAgain}</Text>
|
||||||
|
),
|
||||||
|
cancelProps: { display: 'none' },
|
||||||
|
confirmProps: { display: 'none' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSavingDetails(false);
|
||||||
}
|
}
|
||||||
catch (err) {
|
}
|
||||||
console.log(err);
|
|
||||||
modals.openConfirmModal({
|
|
||||||
title: state.strings.operationFailed,
|
|
||||||
withCloseButton: true,
|
|
||||||
overlayProps: {
|
|
||||||
backgroundOpacity: 0.55,
|
|
||||||
blur: 3,
|
|
||||||
},
|
|
||||||
children: (
|
|
||||||
<Text>{state.strings.tryAgain}</Text>
|
|
||||||
),
|
|
||||||
cancelProps: { display: 'none' },
|
|
||||||
confirmProps: { display: 'none' },
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const setLogin = async () => {
|
||||||
|
if (!savingLogin) {
|
||||||
|
setSavingLogin(true);
|
||||||
|
try {
|
||||||
|
await actions.setLogin();
|
||||||
|
changeClose();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: state.strings.operationFailed,
|
||||||
|
withCloseButton: true,
|
||||||
|
overlayProps: {
|
||||||
|
backgroundOpacity: 0.55,
|
||||||
|
blur: 3,
|
||||||
|
},
|
||||||
|
children: (
|
||||||
|
<Text>{state.strings.tryAgain}</Text>
|
||||||
|
),
|
||||||
|
cancelProps: { display: 'none' },
|
||||||
|
confirmProps: { display: 'none' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSavingLogin(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +105,7 @@ throw new Error("NO");
|
|||||||
</div>
|
</div>
|
||||||
<div className={classes.section}>
|
<div className={classes.section}>
|
||||||
<div className={classes.divider} />
|
<div className={classes.divider} />
|
||||||
<UnstyledButton className={classes.sectionEdit}>{ state.strings.edit }</UnstyledButton>
|
<UnstyledButton className={classes.sectionEdit} onClick={detailsOpen}>{ state.strings.edit }</UnstyledButton>
|
||||||
</div>
|
</div>
|
||||||
{ !state.profile.name && (
|
{ !state.profile.name && (
|
||||||
<Text className={classes.nameUnset}>{state.strings.name}</Text>
|
<Text className={classes.nameUnset}>{state.strings.name}</Text>
|
||||||
@ -146,43 +179,41 @@ throw new Error("NO");
|
|||||||
<Text className={classes.entryLabel} onClick={changeOpen}>{ state.strings.changeLogin }</Text>
|
<Text className={classes.entryLabel} onClick={changeOpen}>{ state.strings.changeLogin }</Text>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.divider} />
|
<div className={classes.divider} />
|
||||||
|
|
||||||
<div className={classes.entry}>
|
|
||||||
<div className={classes.entryIcon}>
|
|
||||||
<IconClock />
|
|
||||||
</div>
|
|
||||||
<Text className={classes.controlLabel}>{ state.strings.timeFormat }</Text>
|
|
||||||
<Radio.Group
|
|
||||||
name="timeFormat"
|
|
||||||
className={classes.radio}
|
|
||||||
value={state.timeFormat}
|
|
||||||
onChange={actions.setTimeFormat}
|
|
||||||
>
|
|
||||||
<Group mt="xs">
|
|
||||||
<Radio value="12h" label={ state.strings.timeUs } />
|
|
||||||
<Radio value="24h" label={ state.strings.timeEu } />
|
|
||||||
</Group>
|
|
||||||
</Radio.Group>
|
|
||||||
</div>
|
|
||||||
<div className={classes.entry}>
|
|
||||||
<div className={classes.entryIcon}>
|
|
||||||
<IconCalendar />
|
|
||||||
</div>
|
|
||||||
<Text className={classes.controlLabel}>{ state.strings.dateFormat }</Text>
|
|
||||||
<Radio.Group
|
|
||||||
name="dateFormat"
|
|
||||||
className={classes.radio}
|
|
||||||
value={state.dateFormat}
|
|
||||||
onChange={actions.setDateFormat}
|
|
||||||
>
|
|
||||||
<Group mt="xs">
|
|
||||||
<Radio value="mm/dd" label={ state.strings.dateUs } />
|
|
||||||
<Radio value="dd/mm" label={ state.strings.dateEu } />
|
|
||||||
</Group>
|
|
||||||
</Radio.Group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={classes.selects}>
|
<div className={classes.selects}>
|
||||||
|
<div className={classes.entry}>
|
||||||
|
<div className={classes.entryIcon}>
|
||||||
|
<IconClock />
|
||||||
|
</div>
|
||||||
|
<Text className={classes.controlLabel}>{ state.strings.timeFormat }</Text>
|
||||||
|
<Radio.Group
|
||||||
|
name="timeFormat"
|
||||||
|
className={classes.radio}
|
||||||
|
value={state.timeFormat}
|
||||||
|
onChange={actions.setTimeFormat}
|
||||||
|
>
|
||||||
|
<Group mt="xs">
|
||||||
|
<Radio value="12h" label={ state.strings.timeUs } />
|
||||||
|
<Radio value="24h" label={ state.strings.timeEu } />
|
||||||
|
</Group>
|
||||||
|
</Radio.Group>
|
||||||
|
</div>
|
||||||
|
<div className={classes.entry}>
|
||||||
|
<div className={classes.entryIcon}>
|
||||||
|
<IconCalendar />
|
||||||
|
</div>
|
||||||
|
<Text className={classes.controlLabel}>{ state.strings.dateFormat }</Text>
|
||||||
|
<Radio.Group
|
||||||
|
name="dateFormat"
|
||||||
|
className={classes.radio}
|
||||||
|
value={state.dateFormat}
|
||||||
|
onChange={actions.setDateFormat}
|
||||||
|
>
|
||||||
|
<Group mt="xs">
|
||||||
|
<Radio value="mm/dd" label={ state.strings.dateUs } />
|
||||||
|
<Radio value="dd/mm" label={ state.strings.dateEu } />
|
||||||
|
</Group>
|
||||||
|
</Radio.Group>
|
||||||
|
</div>
|
||||||
<div className={classes.entry}>
|
<div className={classes.entry}>
|
||||||
<div className={classes.entryIcon}>
|
<div className={classes.entryIcon}>
|
||||||
<IconBrightness />
|
<IconBrightness />
|
||||||
@ -249,13 +280,13 @@ throw new Error("NO");
|
|||||||
<TextInput
|
<TextInput
|
||||||
className={classes.input}
|
className={classes.input}
|
||||||
size="md"
|
size="md"
|
||||||
value={state.username}
|
value={state.handle}
|
||||||
leftSectionPointerEvents="none"
|
leftSectionPointerEvents="none"
|
||||||
leftSection={<IconUser />}
|
leftSection={<IconUser />}
|
||||||
rightSection={state.taken ? <IconUsers /> : null}
|
rightSection={state.taken ? <IconUsers /> : null}
|
||||||
placeholder={state.strings.username}
|
placeholder={state.strings.username}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
actions.setUsername(event.currentTarget.value)
|
actions.setHandle(event.currentTarget.value)
|
||||||
}
|
}
|
||||||
error={state.taken}
|
error={state.taken}
|
||||||
/>
|
/>
|
||||||
@ -286,8 +317,66 @@ throw new Error("NO");
|
|||||||
<Button
|
<Button
|
||||||
variant="filled"
|
variant="filled"
|
||||||
onClick={setLogin}
|
onClick={setLogin}
|
||||||
loading={state.loading}
|
loading={savingLogin}
|
||||||
disabled={state.taken || !state.checked || !state.username || !state.password || state.confirm !== state.password}
|
disabled={state.taken || !state.checked || !state.handle || !state.password || state.confirm !== state.password}
|
||||||
|
>
|
||||||
|
{state.strings.save}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
title={state.strings.profileDetails}
|
||||||
|
opened={detailsOpened}
|
||||||
|
onClose={detailsClose}
|
||||||
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
|
centered
|
||||||
|
>
|
||||||
|
<div className={classes.change}>
|
||||||
|
<TextInput
|
||||||
|
className={classes.input}
|
||||||
|
size="md"
|
||||||
|
value={state.name}
|
||||||
|
leftSectionPointerEvents="none"
|
||||||
|
leftSection={<IconIdBadge />}
|
||||||
|
placeholder={state.strings.name}
|
||||||
|
onChange={(event) =>
|
||||||
|
actions.setName(event.currentTarget.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
className={classes.input}
|
||||||
|
size="md"
|
||||||
|
value={state.location}
|
||||||
|
leftSectionPointerEvents="none"
|
||||||
|
leftSection={<IconMapPin />}
|
||||||
|
placeholder={state.strings.location}
|
||||||
|
onChange={(event) =>
|
||||||
|
actions.setLocation(event.currentTarget.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Textarea
|
||||||
|
className={classes.input}
|
||||||
|
size="md"
|
||||||
|
minRows={1}
|
||||||
|
maxRows={4}
|
||||||
|
autosize={true}
|
||||||
|
value={state.description}
|
||||||
|
leftSectionPointerEvents="none"
|
||||||
|
leftSection={<IconBook />}
|
||||||
|
placeholder={state.strings.description}
|
||||||
|
onChange={(event) =>
|
||||||
|
actions.setDescription(event.currentTarget.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className={classes.control}>
|
||||||
|
<Button variant="default" onClick={detailsClose}>
|
||||||
|
{state.strings.cancel}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="filled"
|
||||||
|
onClick={setDetails}
|
||||||
|
loading={savingDetails}
|
||||||
>
|
>
|
||||||
{state.strings.save}
|
{state.strings.save}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -34,6 +34,10 @@ export function useSettings() {
|
|||||||
username: '',
|
username: '',
|
||||||
taken: false,
|
taken: false,
|
||||||
checked: true,
|
checked: true,
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
location: '',
|
||||||
|
handle: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ -58,7 +62,8 @@ export function useSettings() {
|
|||||||
}
|
}
|
||||||
settings.addConfigListener(setConfig);
|
settings.addConfigListener(setConfig);
|
||||||
const setProfile = (profile: Profile) => {
|
const setProfile = (profile: Profile) => {
|
||||||
updateState({ profile, username: profile.handle, profileSet: true, imageUrl: identity.getProfileImageUrl() })
|
const { handle, name, location, description } = profile;
|
||||||
|
updateState({ profile, handle, name, location, description, profileSet: true, imageUrl: identity.getProfileImageUrl() })
|
||||||
}
|
}
|
||||||
identity.addProfileListener(setProfile)
|
identity.addProfileListener(setProfile)
|
||||||
return () => {
|
return () => {
|
||||||
@ -176,16 +181,16 @@ console.log(audioInputs, videoInputs);
|
|||||||
logout: async () => {
|
logout: async () => {
|
||||||
await app.actions.accountLogout(state.all);
|
await app.actions.accountLogout(state.all);
|
||||||
},
|
},
|
||||||
setUsername: (username) => {
|
setHandle: (handle) => {
|
||||||
updateState({ username, taken: false, checked: false });
|
updateState({ handle, taken: false, checked: false });
|
||||||
clearTimeout(debounce.current);
|
clearTimeout(debounce.current);
|
||||||
if (!username || username === state.profile.handle) {
|
if (!handle || handle === state.profile.handle) {
|
||||||
updateState({ available: true, checked: true});
|
updateState({ available: true, checked: true});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
debounce.current = setTimeout(async () => {
|
debounce.current = setTimeout(async () => {
|
||||||
const { settings } = getSession();
|
const { settings } = getSession();
|
||||||
const available = await settings.getUsernameStatus(username);
|
const available = await settings.getUsernameStatus(handle);
|
||||||
updateState({ taken: !available, checked: true });
|
updateState({ taken: !available, checked: true });
|
||||||
}, DEBOUNCE_MS);
|
}, DEBOUNCE_MS);
|
||||||
}
|
}
|
||||||
@ -199,6 +204,20 @@ console.log(audioInputs, videoInputs);
|
|||||||
setLogin: async () => {
|
setLogin: async () => {
|
||||||
const { settings } = getSession();
|
const { settings } = getSession();
|
||||||
await settings.setLogin(state.username, state.password);
|
await settings.setLogin(state.username, state.password);
|
||||||
|
},
|
||||||
|
setName: (name) => {
|
||||||
|
updateState({ name });
|
||||||
|
},
|
||||||
|
setLocation: (location) => {
|
||||||
|
updateState({ location });
|
||||||
|
},
|
||||||
|
setDescription: (description) => {
|
||||||
|
updateState({ description });
|
||||||
|
},
|
||||||
|
setDetails: async () => {
|
||||||
|
const { identity } = getSession();
|
||||||
|
const { name, location, description } = state;
|
||||||
|
await identity.setProfileData(name, location, description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user