aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pages/_app.tsx1
-rw-r--r--pages/user.tsx456
-rw-r--r--styles/global.css16
-rw-r--r--styles/user.css46
-rw-r--r--styles/utility.css4
5 files changed, 256 insertions, 267 deletions
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 7cdb8e3..db62fa8 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -16,6 +16,7 @@ import '../styles/index.css';
import '../styles/loginregister.css';
import '../styles/search.css';
import '../styles/settings.css';
+import '../styles/user.css';
export default function VierOpEenRijWebsite({ Component, pageProps }) {
return <div>
diff --git a/pages/user.tsx b/pages/user.tsx
index 4f7331c..e1c3674 100644
--- a/pages/user.tsx
+++ b/pages/user.tsx
@@ -1,6 +1,6 @@
import Icon from '@mdi/react';
import axios from 'axios';
-import { Children, ReactNode, useContext, useEffect, useState } from 'react';
+import { ReactNode, useContext, useEffect, useState } from 'react';
import { userGames, userInfo } from '../api/api';
import { AccountAvatar } from '../components/account';
@@ -35,60 +35,18 @@ function InfoModule(props: {
label: string;
icon: ReactNode;
}) {
- return <div
- style={{
- position: 'relative',
- height: '100%',
- }}
- >
- <div
- style={{
- position: 'absolute',
- left: '50%',
- transform: 'translateX(-50%)',
- }}
- >
+ return <div className='infoModule posrel'>
+ <div className='iconWrapper posabs'>
{props.icon}
</div>
- <div
- style={{
- position: 'absolute',
- top: 24 + 6,
- left: 0,
- right: 0,
- bottom: 0,
- }}
- >
- <span
- style={{
- position: 'absolute',
- top: '50%',
- transform: 'translateY(-50%)',
- width: '100%',
- textAlign: 'center',
- }}
- >
+ <div className='labelWrapper posabs h0 b0'>
+ <span className='label posabs center fullwidth'>
{props.label}
</span>
</div>
</div>;
}
-function InfoSection(props: { children: ReactNode; }) {
- return <Vierkant fullwidth>
- <div
- style={{
- display: 'grid',
- gridTemplateColumns: `repeat(${Children.count(props.children)}, 1fr)`,
- gridGap: 12,
- height: 64,
- }}
- >
- {props.children}
- </div>
- </Vierkant>;
-}
-
export default function AccountPage() {
var server = typeof window === 'undefined';
var loggedIn = !server && document.cookie.includes('token');
@@ -167,226 +125,222 @@ export default function AccountPage() {
<NavBar />
<CenteredPage width={802}>
<PageTitle>Profiel</PageTitle>
- <Vierkant fullwidth>
- <AccountAvatar size={128} id={user?.id || ''} />
- <div
- style={{
- display: 'inline-block',
- verticalAlign: 'top',
- marginLeft: 12,
- width: 'calc(100% - 128px - 12px)',
- }}
- >
- <h2 style={{ fontSize: 32 }}>{user?.username}</h2>
- <p
- id='status'
- contentEditable={editingStatus ? 'true' : 'false'}
- style={{
- marginTop: 6,
- transitionDuration: '.3s',
- }}
- suppressContentEditableWarning={true}
- >
- {user?.status}
- </p>
- </div>
- <div
- style={{
- position: 'absolute',
- backgroundColor: 'var(--background)',
- height: '40px',
- bottom: 24,
- left: 24 + 12 + 128,
- right: 24,
- }}
- >
- {loggedIn && <div>
- {ownPage
- ? <div>
- <IconLabelButton icon={<SettingsOutlinedIcon />} href='/settings' text='Instellingen' />
- {!editingStatus
- ? <IconLabelButton
- icon={<EditOutlinedIcon />}
- text='Status bewerken'
- onclick={() => setEditingStatus(true)}
+ <Vierkant className='accountHeader w100m2m pad-l'>
+ <div className='inner posrel'>
+ <AccountAvatar size={128} id={user?.id || ''} />
+ <div className='userInfo dispinbl valigntop'>
+ <h2 className='username'>{user?.username}</h2>
+ <p
+ id='status'
+ className='status round-t'
+ contentEditable={editingStatus ? 'true' : 'false'}
+ suppressContentEditableWarning={true}
+ >
+ {user?.status}
+ </p>
+ </div>
+ <div className='posabs b0 r0'>
+ {loggedIn && <div>
+ {ownPage
+ ? <div>
+ <IconLabelButton
+ icon={<SettingsOutlinedIcon />}
+ href='/settings'
+ text='Instellingen'
/>
- : <IconLabelButton
- icon={<DoneOutlinedIcon />}
- text='Status opslaan'
- onclick={() => {
- setEditingStatus(false);
- axios.request({
- method: 'post',
- url: `/api/user/status`,
- headers: { 'content-type': 'application/json' },
- data: { 'status': document.getElementById('status').innerText },
- });
- }}
- />}
- </div>
- : <div>
- {(() => {
- var icon = {
- 'blocked': <Icon size={1} path={mdiAccountCancelOutline} />,
- }[relation] || <Icon size={1} path={mdiAccountCancelOutline} />;
+ {!editingStatus
+ ? <IconLabelButton
+ icon={<EditOutlinedIcon />}
+ text='Status bewerken'
+ onclick={() => setEditingStatus(true)}
+ />
+ : <IconLabelButton
+ icon={<DoneOutlinedIcon />}
+ text='Status opslaan'
+ onclick={() => {
+ setEditingStatus(false);
+ axios.request({
+ method: 'post',
+ url: `/api/user/status`,
+ headers: { 'content-type': 'application/json' },
+ data: { 'status': document.getElementById('status').innerText },
+ });
+ }}
+ />}
+ </div>
+ : <div>
+ {(() => {
+ var icon = {
+ 'blocked': <Icon size={1} path={mdiAccountCancelOutline} />,
+ }[relation] || <Icon size={1} path={mdiAccountCancelOutline} />;
- var text = {
- 'blocked': 'Deblokkeren',
- }[relation] || 'Blokkeren';
+ var text = {
+ 'blocked': 'Deblokkeren',
+ }[relation] || 'Blokkeren';
- return <IconLabelButton
- icon={icon}
- text={text}
- onclick={() => {
- var nextRelation = {
- 'blocked': {
- 'endpoint': '/api/social/unblock',
- 'action': `${user.username} gedeblokkeerd`,
- 'relation': 'none',
+ return <IconLabelButton
+ icon={icon}
+ text={text}
+ onclick={() => {
+ var nextRelation = {
+ 'blocked': {
+ 'endpoint': '/api/social/unblock',
+ 'action': `${user.username} gedeblokkeerd`,
+ 'relation': 'none',
+ 'icon': <Icon size={1} path={mdiAccountCancelOutline} />,
+ },
+ }[relation] || {
+ 'endpoint': '/api/social/block',
+ 'action': `${user.username} geblokkeerd`,
+ 'relation': 'blocked',
'icon': <Icon size={1} path={mdiAccountCancelOutline} />,
- },
- }[relation] || {
- 'endpoint': '/api/social/block',
- 'action': `${user.username} geblokkeerd`,
- 'relation': 'blocked',
- 'icon': <Icon size={1} path={mdiAccountCancelOutline} />,
- };
+ };
- axios.request({
- method: 'post',
- url: nextRelation.endpoint,
- headers: { 'content-type': 'application/json' },
- data: { 'id': user?.id },
- })
- .then(() => {
- toast({
- message: nextRelation.action,
- type: 'confirmation',
- icon: nextRelation.icon,
+ axios.request({
+ method: 'post',
+ url: nextRelation.endpoint,
+ headers: { 'content-type': 'application/json' },
+ data: { 'id': user?.id },
+ })
+ .then(() => {
+ toast({
+ message: nextRelation.action,
+ type: 'confirmation',
+ icon: nextRelation.icon,
+ });
+ setRelation(nextRelation.relation);
});
- setRelation(nextRelation.relation);
- });
- }}
- />;
- })()}
- {(() => {
- var icon = {
- 'friends': <Icon size={1} path={mdiAccountMinusOutline} />,
- 'outgoing': <Icon size={1} path={mdiAccountRemoveOutline} />,
- 'incoming': <PersonAddOutlinedIcon />,
- }[relation] || <PersonAddOutlinedIcon />;
+ }}
+ />;
+ })()}
+ {(() => {
+ var icon = {
+ 'friends': <Icon size={1} path={mdiAccountMinusOutline} />,
+ 'outgoing': <Icon size={1} path={mdiAccountRemoveOutline} />,
+ 'incoming': <PersonAddOutlinedIcon />,
+ }[relation] || <PersonAddOutlinedIcon />;
- var text = {
- 'friends': 'Vriend verwijderen',
- 'outgoing': 'Vriendschapsverzoek annuleren',
- 'incoming': 'Vriendschapsverzoek accepteren',
- }[relation] || 'Vriendschapsverzoek sturen';
+ var text = {
+ 'friends': 'Vriend verwijderen',
+ 'outgoing': 'Vriendschapsverzoek annuleren',
+ 'incoming': 'Vriendschapsverzoek accepteren',
+ }[relation] || 'Vriendschapsverzoek sturen';
- return <IconLabelButton
- icon={icon}
- text={text}
- onclick={() => {
- var nextRelation = {
- 'friends': {
- 'endpoint': '/api/social/remove',
- 'action': `${user.username} succesvol verwijderd als vriend`,
- 'relation': 'none',
- 'icon': <Icon size={1} path={mdiAccountMinusOutline} />,
- },
- 'outgoing': {
- 'endpoint': '/api/social/remove',
- 'action': `Vriendschapsverzoek naar ${user.username} geannuleerd`,
- 'relation': 'none',
- 'icon': <Icon size={1} path={mdiAccountMinusOutline} />,
- },
- 'incoming': {
- 'endpoint': '/api/social/accept',
- 'action': `Vriendschapsverzoek van ${user.username} geaccepteerd`,
- 'relation': 'friends',
+ return <IconLabelButton
+ icon={icon}
+ text={text}
+ onclick={() => {
+ var nextRelation = {
+ 'friends': {
+ 'endpoint': '/api/social/remove',
+ 'action': `${user.username} succesvol verwijderd als vriend`,
+ 'relation': 'none',
+ 'icon': <Icon size={1} path={mdiAccountMinusOutline} />,
+ },
+ 'outgoing': {
+ 'endpoint': '/api/social/remove',
+ 'action':
+ `Vriendschapsverzoek naar ${user.username} geannuleerd`,
+ 'relation': 'none',
+ 'icon': <Icon size={1} path={mdiAccountMinusOutline} />,
+ },
+ 'incoming': {
+ 'endpoint': '/api/social/accept',
+ 'action':
+ `Vriendschapsverzoek van ${user.username} geaccepteerd`,
+ 'relation': 'friends',
+ 'icon': <PersonAddOutlinedIcon />,
+ },
+ }[relation] || {
+ 'endpoint': '/api/social/request',
+ 'action': `Vriendschapsverzoek gestuurd naar ${user.username}`,
+ 'relation': 'outgoing',
'icon': <PersonAddOutlinedIcon />,
- },
- }[relation] || {
- 'endpoint': '/api/social/request',
- 'action': `Vriendschapsverzoek gestuurd naar ${user.username}`,
- 'relation': 'outgoing',
- 'icon': <PersonAddOutlinedIcon />,
- };
+ };
- axios.request({
- method: 'post',
- url: nextRelation.endpoint,
- headers: { 'content-type': 'application/json' },
- data: { 'id': user?.id },
- })
- .then(() => {
- toast({
- message: nextRelation.action,
- type: 'confirmation',
- icon: nextRelation.icon,
+ axios.request({
+ method: 'post',
+ url: nextRelation.endpoint,
+ headers: { 'content-type': 'application/json' },
+ data: { 'id': user?.id },
+ })
+ .then(() => {
+ toast({
+ message: nextRelation.action,
+ type: 'confirmation',
+ icon: nextRelation.icon,
+ });
+ setRelation(nextRelation.relation);
});
- setRelation(nextRelation.relation);
- });
- }}
- />;
- })()}
- </div>}
- </div>}
+ }}
+ />;
+ })()}
+ </div>}
+ </div>}
+ </div>
</div>
</Vierkant>
- <InfoSection>
- <InfoModule
- icon={<Icon size={1} path={mdiCheckboxBlankCircle} color='var(--disk-b-text)' />}
- label='Online'
- />
- <InfoModule
- icon={<AssignmentIndOutlinedIcon />}
- label={(() => {
- var memberSince = 'Lid sinds';
+ <Vierkant className='infosection pad-l w100m2m'>
+ <div className='inner sidebyside'>
+ <InfoModule
+ icon={<Icon size={1} path={mdiCheckboxBlankCircle} className='outcome win' />}
+ label='Online'
+ />
+ <InfoModule
+ icon={<AssignmentIndOutlinedIcon />}
+ label={(() => {
+ var memberSince = 'Lid sinds';
- var registered = new Date(user?.registered);
- memberSince += ' ' + registered.toLocaleString('nl-nl', { month: 'long', day: 'numeric' });
+ var registered = new Date(user?.registered);
+ memberSince += ' ' + registered.toLocaleString('nl-nl', { month: 'long', day: 'numeric' });
- var currentYear = new Date().getFullYear();
- var memberYear = registered.getFullYear();
- if (currentYear != memberYear) memberSince += ' ' + memberYear;
+ var currentYear = new Date().getFullYear();
+ var memberYear = registered.getFullYear();
+ if (currentYear != memberYear) memberSince += ' ' + memberYear;
- return memberSince;
- })()}
- />
- <InfoModule
- icon={<PeopleOutlineOutlinedIcon />}
- label={(() => {
- var label = user?.friends.toString() + ' ';
- label += user?.friends == 1 ? 'vriend' : 'vrienden';
- return label;
- })()}
- />
- <InfoModule icon={<Icon size={1} path={mdiEarth} />} label='Nederland' />
- </InfoSection>
- <InfoSection>
- <InfoModule
- icon={<ArrowUpwardOutlinedIcon style={{ color: 'var(--disk-b-text)' }} />}
- label={gameInfo?.totals.win + ' keer gewonnen'}
- />
- <InfoModule
- icon={<Icon size={1} path={mdiEqual} />}
- label={gameInfo?.totals.draw + ' keer gelijkspel'}
- />
- <InfoModule
- icon={<ArrowDownwardOutlinedIcon style={{ color: 'var(--disk-a-text)' }} />}
- label={gameInfo?.totals.lose + ' keer verloren'}
- />
- <InfoModule icon={<Icon size={1} path={mdiClipboardTextOutline} />} label={'Score: ' + user?.rating} />
- <InfoModule
- icon={<Icon size={1} path={mdiGamepadSquareOutline} />}
- label={(() => {
- var label = gameInfo?.totals.games.toString() + ' ';
- label += gameInfo?.totals.games == 1 ? 'potje' : 'potjes';
- return label;
- })()}
- />
- </InfoSection>
- <Vierkant>
+ return memberSince;
+ })()}
+ />
+ <InfoModule
+ icon={<PeopleOutlineOutlinedIcon />}
+ label={(() => {
+ var label = user?.friends.toString() + ' ';
+ label += user?.friends == 1 ? 'vriend' : 'vrienden';
+ return label;
+ })()}
+ />
+ <InfoModule icon={<Icon size={1} path={mdiEarth} />} label='Nederland' />
+ </div>
+ </Vierkant>
+ <Vierkant className='infosection pad-l w100m2m sidebyside'>
+ <div className='inner sidebyside'>
+ <InfoModule
+ icon={<ArrowUpwardOutlinedIcon className='outcome win' />}
+ label={gameInfo?.totals.win + ' keer gewonnen'}
+ />
+ <InfoModule
+ icon={<Icon size={1} path={mdiEqual} className='subtile' />}
+ label={gameInfo?.totals.draw + ' keer gelijkspel'}
+ />
+ <InfoModule
+ icon={<ArrowDownwardOutlinedIcon className='outcome lose' />}
+ label={gameInfo?.totals.lose + ' keer verloren'}
+ />
+ <InfoModule
+ icon={<Icon size={1} path={mdiClipboardTextOutline} />}
+ label={'Score: ' + user?.rating}
+ />
+ <InfoModule
+ icon={<Icon size={1} path={mdiGamepadSquareOutline} />}
+ label={(() => {
+ var label = gameInfo?.totals.games.toString() + ' ';
+ label += gameInfo?.totals.games == 1 ? 'potje' : 'potjes';
+ return label;
+ })()}
+ />
+ </div>
+ </Vierkant>
+ <Vierkant className='pad-l'>
<RecentGames games={gameInfo?.games} />
</Vierkant>
</CenteredPage>
diff --git a/styles/global.css b/styles/global.css
index b6ed028..f70b6bd 100644
--- a/styles/global.css
+++ b/styles/global.css
@@ -141,19 +141,3 @@ input::placeholder {
/* material-ui default state */
svg.MuiSvgIcon-root { transition: none !important; }
-/* editable field status */
-*[contenteditable] { border-color: var(--background-alt); }
-*[contenteditable="true"]:focus { border-color: var(--disk-a); }
-*[contenteditable="true"] {
- background-color: var(--page-background);
- color: var(--text-alt);
- padding: 6px;
- border-radius: 6px;
- border-style: solid;
- border-width: 2px;
-}
-
-.outcome.win { color: var(--disk-a-alt); }
-.outcome.lose { color: var(--disk-b-alt); }
-.outcome.draw { color: var(--gray-600); }
-
diff --git a/styles/user.css b/styles/user.css
new file mode 100644
index 0000000..d66fa94
--- /dev/null
+++ b/styles/user.css
@@ -0,0 +1,46 @@
+.infosection .inner {
+ height: 64px;
+}
+
+/* editable field status */
+*[contenteditable] {
+ box-shadow: inset 0 0 0 0px var(--foreground);
+}
+*[contenteditable="true"]:focus {
+ box-shadow: inset 0 0 0 2px var(--accent);
+}
+*[contenteditable="true"] {
+ background-color: var(--background);
+ color: var(--text-alt);
+ padding: 8px;
+ box-shadow: inset 0 0 0 2px var(--foreground);
+}
+
+.accountHeader .userInfo {
+ margin-left: var(--spacing-medium);
+ width: calc(100% - 128px - var(--spacing-medium));
+}
+
+.accountHeader .userInfo .username {
+ font-size: 2rem;
+}
+
+.accountHeader .userInfo .status {
+ transition-duration: .3s;
+ margin-top: var(--spacing-small);
+}
+
+.infoModule .iconWrapper {
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.infoModule .labelWrapper {
+ top: calc(24px + var(--spacing-small));
+}
+
+.infoModule .labelWrapper .label {
+ top: 50%;
+ transform: translateY(-50%);
+}
+
diff --git a/styles/utility.css b/styles/utility.css
index 7f5ebbd..be982f8 100644
--- a/styles/utility.css
+++ b/styles/utility.css
@@ -18,6 +18,10 @@
.fg-100 { color: var(--gray-100); }
.fg-900 { color: var(--gray-900); }
+.outcome.win { color: var(--disk-a-alt); }
+.outcome.lose { color: var(--disk-b-alt); }
+.outcome.draw { color: var(--gray-600); }
+
.posabs { position: absolute; }
.posrel { position: relative; }
.posfix { position: fixed; }