diff options
-rw-r--r-- | pages/_app.tsx | 1 | ||||
-rw-r--r-- | pages/user.tsx | 456 | ||||
-rw-r--r-- | styles/global.css | 16 | ||||
-rw-r--r-- | styles/user.css | 46 | ||||
-rw-r--r-- | styles/utility.css | 4 |
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; } |