Merge pull request #22 from jedmund/account-settings

Add account settings and a modal to control them in
This commit is contained in:
Justin Edmund 2022-03-04 05:33:29 -08:00 committed by GitHub
commit b412a94b66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 922 additions and 84 deletions

View file

@ -0,0 +1,160 @@
.Account.Dialog {
display: flex;
flex-direction: column;
gap: $unit * 2;
width: $unit * 60;
form {
display: flex;
flex-direction: column;
gap: $unit * 2;
.Switch {
$height: 34px;
background: $grey-70;
border-radius: $height / 2;
border: none;
position: relative;
width: 58px;
height: $height;
&:focus {
box-shadow: 0 0 0 2px $grey-00;
}
&[data-state="checked"] {
background: $grey-00;
}
}
.Thumb {
background: white;
border-radius: 13px;
display: block;
height: 26px;
width: 26px;
transition: transform 100ms;
transform: translateX(-1px);
&:hover {
cursor: pointer;
}
&[data-state="checked"] {
background: white;
transform: translateX(21px);
}
}
.Button {
font-size: $font-regular;
padding: ($unit * 1.5) ($unit * 2);
margin-top: $unit * 2;
width: 100%;
&.btn-disabled {
background: $grey-90;
color: $grey-70;
cursor: not-allowed;
}
&:not(.btn-disabled) {
background: $grey-90;
color: $grey-40;
&:hover {
background: $grey-80;
}
}
}
.field {
align-items: center;
display: flex;
flex-direction: row;
gap: $unit * 2;
select {
background: no-repeat url('/icons/ArrowDark.svg'), $grey-90;
background-position-y: center;
background-position-x: 95%;
margin: 0;
width: 240px;
}
.left {
display: flex;
flex-direction: column;
flex-grow: 1;
gap: $unit / 2;
label {
color: $grey-00;
font-size: $font-regular;
}
p {
color: $grey-60;
font-size: $font-small;
line-height: 1.1;
max-width: 300px;
}
}
.preview {
$diameter: 48px;
background-color: $grey-90;
border-radius: 999px;
height: $diameter;
width: $diameter;
img {
height: $diameter;
width: $diameter;
}
&.fire {
background: $fire-bg-light;
}
&.water {
background: $water-bg-light;
}
&.wind {
background: $wind-bg-light;
}
&.earth {
background: $earth-bg-light;
}
&.dark {
background: $dark-bg-light;
}
&.light {
background: $light-bg-light;
}
}
}
section {
margin-bottom: $unit;
h2 {
margin-bottom: $unit * 3;
}
}
}
.DialogDescription {
font-size: $font-regular;
line-height: 1.24;
margin-bottom: $unit;
&:last-of-type {
margin-bottom: 0;
}
}
}

View file

@ -0,0 +1,158 @@
import React, { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import { useSnapshot } from 'valtio'
import * as Dialog from '@radix-ui/react-dialog'
import * as Switch from '@radix-ui/react-switch'
import api from '~utils/api'
import { accountState } from '~utils/accountState'
import { pictureData } from '~utils/pictureData'
import Button from '~components/Button'
import CrossIcon from '~public/icons/Cross.svg'
import './index.scss'
const AccountModal = () => {
const { account } = useSnapshot(accountState)
// Cookies
const [cookies] = useCookies(['user'])
const headers = (cookies.user != null) ? {
headers: {
'Authorization': `Bearer ${cookies.user.access_token}`
}
} : {}
// State
const [open, setOpen] = useState(false)
const [picture, setPicture] = useState('')
const [language, setLanguage] = useState('')
const [privateProfile, setPrivateProfile] = useState(false)
// Refs
const pictureSelect = React.createRef<HTMLSelectElement>()
const languageSelect = React.createRef<HTMLSelectElement>()
const privateSelect = React.createRef<HTMLInputElement>()
useEffect(() => {
if (cookies.user) setPicture(cookies.user.picture)
if (cookies.user) setLanguage(cookies.user.language)
}, [cookies])
const pictureOptions = (
pictureData.sort((a, b) => (a.name.en > b.name.en) ? 1 : -1).map((item, i) => {
return (
<option key={`picture-${i}`} value={item.filename}>{item.name.en}</option>
)
})
)
function handlePictureChange(event: React.ChangeEvent<HTMLSelectElement>) {
if (pictureSelect.current)
setPicture(pictureSelect.current.value)
}
function handleLanguageChange(event: React.ChangeEvent<HTMLSelectElement>) {
if (languageSelect.current)
setLanguage(languageSelect.current.value)
}
function handlePrivateChange(checked: boolean) {
setPrivateProfile(checked)
}
function update(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
const object = {
user: {
picture: picture,
element: pictureData.find(i => i.filename === picture)?.element,
language: language,
private: privateProfile
}
}
api.endpoints.users.update(cookies.user.user_id, object, headers)
.then(response => {
setOpen(false)
})
}
function openChange(open: boolean) {
setOpen(open)
}
return (
<Dialog.Root open={open} onOpenChange={openChange}>
<Dialog.Trigger asChild>
<li className="MenuItem">
<span>Settings</span>
</li>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Content className="Account Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
<div className="DialogHeader">
<div className="DialogTop">
<Dialog.Title className="SubTitle">Account Settings</Dialog.Title>
<Dialog.Title className="DialogTitle">@{account.user?.username}</Dialog.Title>
</div>
<Dialog.Close className="DialogClose" asChild>
<span>
<CrossIcon />
</span>
</Dialog.Close>
</div>
<form onSubmit={update}>
<div className="field">
<div className="left">
<label>Picture</label>
</div>
<div className={`preview ${pictureData.find(i => i.filename === picture)?.element}`}>
<img
alt="Profile preview"
srcSet={`/profile/${picture}.png,
/profile/${picture}@2x.png 2x`}
src={`/profile/${picture}.png`}
/>
</div>
<select name="picture" onChange={handlePictureChange} value={picture} ref={pictureSelect}>
{pictureOptions}
</select>
</div>
<div className="field">
<div className="left">
<label>Language</label>
</div>
<select name="language" onChange={handleLanguageChange} value={language} ref={languageSelect}>
<option key="en" value="en">English</option>
<option key="jp" value="jp">Japanese</option>
</select>
</div>
<div className="field">
<div className="left">
<label>Private</label>
<p>Hide your profile and prevent your grids from showing up in collections</p>
</div>
<Switch.Root className="Switch" onCheckedChange={handlePrivateChange} checked={privateProfile}>
<Switch.Thumb className="Thumb" />
</Switch.Root>
</div>
<Button>Save settings</Button>
</form>
</Dialog.Content>
<Dialog.Overlay className="Overlay" />
</Dialog.Portal>
</Dialog.Root>
)
}
export default AccountModal

View file

@ -83,11 +83,12 @@ const GridRep = (props: Props) => {
if (props.user)
return (
<img
alt="Gran"
className="gran"
srcSet="/profile/gran.png,
/profile/gran@2x.png 2x"
src="/profile/gran.png" />
alt={props.user.picture.picture}
className={`profile ${props.user.picture.element}`}
srcSet={`/profile/${props.user.picture.picture}.png,
/profile/${props.user.picture.picture}@2x.png 2x`}
src={`/profile/${props.user.picture.picture}.png`}
/>
)
else
return (<div className="no-user" />)

View file

@ -12,7 +12,7 @@
color: $grey-40;
font-weight: $normal;
&:hover {
&:hover:not(.disabled) {
background: $grey-100;
color: $grey-00;
cursor: pointer;
@ -22,13 +22,42 @@
}
}
&.profile > div {
padding: 6px 12px;
}
a {
color: $grey-40;
}
a, span {
& > a, & > span {
display: block;
padding: 12px;
padding: 12px 12px;
}
& > div {
align-items: center;
display: flex;
flex-direction: row;
padding: 10px 12px;
&:hover {
i.tag {
background: $grey-60;
color: white;
}
}
span {
flex-grow: 1;
}
img {
$diameter: 32px;
border-radius: $diameter / 2;
height: $diameter;
width: $diameter;
}
}
}

View file

@ -1,14 +1,11 @@
import React from 'react'
import Link from 'next/link'
import LoginModal from '~components/LoginModal'
import SignupModal from '~components/SignupModal'
import { useModal as useSignupModal } from '~utils/useModal'
import { useModal as useLoginModal } from '~utils/useModal'
import { useModal as useAboutModal } from '~utils/useModal'
import { useCookies } from 'react-cookie'
import AboutModal from '~components/AboutModal'
import AccountModal from '~components/AccountModal'
import LoginModal from '~components/LoginModal'
import SignupModal from '~components/SignupModal'
import './index.scss'
@ -19,13 +16,27 @@ interface Props {
}
const HeaderMenu = (props: Props) => {
const [cookies] = useCookies()
function authItems() {
return (
<nav>
<ul className="Menu auth">
<div className="MenuGroup">
<li className="MenuItem">
<Link href={`/${props.username}` || ''}>{props.username}</Link>
<li className="MenuItem profile">
<Link href={`/${cookies.user?.username}` || ''}>
<div>
<span>{cookies.user?.username}</span>
<img
alt={cookies.user?.picture}
className={`profile ${cookies.user?.element}`}
srcSet={`/profile/${cookies.user?.picture}.png,
/profile/${cookies.user?.picture}@2x.png 2x`}
src={`/profile/${cookies.user?.picture}.png`}
/>
</div
></Link>
</li>
<li className="MenuItem">
<Link href={`/saved` || ''}>Saved</Link>
@ -36,15 +47,16 @@ const HeaderMenu = (props: Props) => {
<Link href='/teams'>Teams</Link>
</li>
<li className="MenuItem">
<Link href='/guides'>Guides</Link>
<li className="MenuItem disabled">
<div>
<span>Guides</span>
<i className="tag">Coming Soon</i>
</div>
</li>
</div>
<div className="MenuGroup">
<AboutModal />
<li className="MenuItem">
<span>Settings</span>
</li>
<AccountModal />
<li className="MenuItem" onClick={props.logout}>
<span>Logout</span>
</li>
@ -65,8 +77,11 @@ const HeaderMenu = (props: Props) => {
<Link href='/teams'>Teams</Link>
</li>
<li className="MenuItem">
<Link href='/guides'>Guides</Link>
<li className="MenuItem disabled">
<div>
<span>Guides</span>
<i className="tag">Coming Soon</i>
</div>
</li>
</div>
<div className="MenuGroup">

View file

@ -1,5 +1,6 @@
import React, { useState } from 'react'
import { useCookies } from 'react-cookie'
import { AxiosResponse } from 'axios'
import * as Dialog from '@radix-ui/react-dialog'
@ -94,27 +95,42 @@ const LoginModal = (props: Props) => {
if (formValid) {
api.login(body)
.then((response) => {
const cookieObj = {
user_id: response.data.user.id,
username: response.data.user.username,
access_token: response.data.access_token
}
setCookies('user', cookieObj, { path: '/'})
accountState.account.authorized = true
accountState.account.user = {
id: cookieObj.user_id,
username: cookieObj.username
}
setOpen(false)
}, (error) => {
console.error(error)
})
.then(response => response.data.user.id)
.then(id => fetchUserInfo(id))
.then(infoResponse => storeUserInfo(infoResponse))
}
}
function fetchUserInfo(id: string) {
return api.userInfo(id)
}
function storeUserInfo(response: AxiosResponse) {
const user = response.data.user
const cookieObj = {
user_id: user.id,
username: user.username,
picture: user.picture.picture,
element: user.picture.element,
language: user.language,
access_token: response.data.access_token
}
setCookies('user', cookieObj, { path: '/'})
accountState.account.language = user.language
accountState.account.user = {
id: user.id,
username: user.username,
picture: user.picture.picture,
element: user.picture.element
}
accountState.account.authorized = true
setOpen(false)
}
function openChange(open: boolean) {
setOpen(open)
setErrors({
@ -158,7 +174,7 @@ const LoginModal = (props: Props) => {
ref={passwordInput}
/>
<Button disabled={!formValid}>Log in</Button>
<Button disabled={false}>Log in</Button>
</form>
</Dialog.Content>
<Dialog.Overlay className="Overlay" />

View file

@ -58,7 +58,8 @@ const TopHeader = () => {
accountState[key] = resetState[key]
})
appState.party.editable = false
if (router.route != '/new')
appState.party.editable = false
router.push('/')
return false

148
package-lock.json generated
View file

@ -11,7 +11,7 @@
"@radix-ui/react-dropdown-menu": "^0.1.4",
"@radix-ui/react-hover-card": "^0.1.5",
"@radix-ui/react-label": "^0.1.4",
"@radix-ui/react-switch": "^0.1.4",
"@radix-ui/react-switch": "^0.1.5",
"@radix-ui/react-toggle-group": "^0.1.5",
"@svgr/webpack": "^6.2.0",
"@types/axios": "^0.14.0",
@ -2590,15 +2590,39 @@
}
},
"node_modules/@radix-ui/react-label": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-0.1.4.tgz",
"integrity": "sha512-I59IMdUhHixk6cG4D00UN+oFxTpur9cJQSOl+4EfSTJZs+x4PqDpM7p402/gb9sXJqylsUkDMHDdaPzLwPNf7g==",
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-0.1.5.tgz",
"integrity": "sha512-Au9+n4/DhvjR0IHhvZ1LPdx/OW+3CGDie30ZyCkbSHIuLp4/CV4oPPGBwJ1vY99Jog3zyQhsGww9MXj8O9Aj/A==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "0.1.0",
"@radix-ui/react-context": "0.1.1",
"@radix-ui/react-id": "0.1.4",
"@radix-ui/react-primitive": "0.1.3"
"@radix-ui/react-id": "0.1.5",
"@radix-ui/react-primitive": "0.1.4"
},
"peerDependencies": {
"react": "^16.8 || ^17.0"
}
},
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-id": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-0.1.5.tgz",
"integrity": "sha512-IPc4H/63bes0IZ1GJJozSEkSWcDyhNGtKFWUpJ+XtaLyQ1X3x7Mf6fWwWhDcpqlYEP+5WtAvfqcyEsyjP+ZhBQ==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-layout-effect": "0.1.0"
},
"peerDependencies": {
"react": "^16.8 || ^17.0"
}
},
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz",
"integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "0.1.2"
},
"peerDependencies": {
"react": "^16.8 || ^17.0"
@ -2723,19 +2747,42 @@
}
},
"node_modules/@radix-ui/react-switch": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-0.1.4.tgz",
"integrity": "sha512-GUK0hw/FQX/MJ5Zp5W6e69fqk/KO6bRP0t0Sx89Sl//WSdcgpwk3CimV5seqk8LxL1wwBkDR5zE185U+LdBCOQ==",
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-0.1.5.tgz",
"integrity": "sha512-ITtslJPK+Yi34iNf7K9LtsPaLD76oRIVzn0E8JpEO5HW8gpRBGb2NNI9mxKtEB30TVqIcdjdL10AmuIfOMwjtg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "0.1.0",
"@radix-ui/react-compose-refs": "0.1.0",
"@radix-ui/react-context": "0.1.1",
"@radix-ui/react-label": "0.1.4",
"@radix-ui/react-primitive": "0.1.3",
"@radix-ui/react-label": "0.1.5",
"@radix-ui/react-primitive": "0.1.4",
"@radix-ui/react-use-controllable-state": "0.1.0",
"@radix-ui/react-use-previous": "0.1.0",
"@radix-ui/react-use-size": "0.1.0"
"@radix-ui/react-use-previous": "0.1.1",
"@radix-ui/react-use-size": "0.1.1"
},
"peerDependencies": {
"react": "^16.8 || ^17.0"
}
},
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-primitive": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz",
"integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "0.1.2"
},
"peerDependencies": {
"react": "^16.8 || ^17.0"
}
},
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-use-size": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-0.1.1.tgz",
"integrity": "sha512-pTgWM5qKBu6C7kfKxrKPoBI2zZYZmp2cSXzpUiGM3qEBQlMLtYhaY2JXdXUCxz+XmD1YEjc8oRwvyfsD4AG4WA==",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"react": "^16.8 || ^17.0"
@ -2912,9 +2959,9 @@
}
},
"node_modules/@radix-ui/react-use-previous": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-0.1.0.tgz",
"integrity": "sha512-0fxNc33rYnCzDMPSiSnfS8YklnxQo8WqbAQXPAgIaaA1jRu2qFB916PL4qCIW+avcAAqFD38vWhqDqcVmBharA==",
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-0.1.1.tgz",
"integrity": "sha512-O/ZgrDBr11dR8rhO59ED8s5zIXBRFi8MiS+CmFGfi7MJYdLbfqVOmQU90Ghf87aifEgWe6380LA69KBneaShAg==",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
@ -8837,15 +8884,35 @@
}
},
"@radix-ui/react-label": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-0.1.4.tgz",
"integrity": "sha512-I59IMdUhHixk6cG4D00UN+oFxTpur9cJQSOl+4EfSTJZs+x4PqDpM7p402/gb9sXJqylsUkDMHDdaPzLwPNf7g==",
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-0.1.5.tgz",
"integrity": "sha512-Au9+n4/DhvjR0IHhvZ1LPdx/OW+3CGDie30ZyCkbSHIuLp4/CV4oPPGBwJ1vY99Jog3zyQhsGww9MXj8O9Aj/A==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "0.1.0",
"@radix-ui/react-context": "0.1.1",
"@radix-ui/react-id": "0.1.4",
"@radix-ui/react-primitive": "0.1.3"
"@radix-ui/react-id": "0.1.5",
"@radix-ui/react-primitive": "0.1.4"
},
"dependencies": {
"@radix-ui/react-id": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-0.1.5.tgz",
"integrity": "sha512-IPc4H/63bes0IZ1GJJozSEkSWcDyhNGtKFWUpJ+XtaLyQ1X3x7Mf6fWwWhDcpqlYEP+5WtAvfqcyEsyjP+ZhBQ==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-layout-effect": "0.1.0"
}
},
"@radix-ui/react-primitive": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz",
"integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "0.1.2"
}
}
}
},
"@radix-ui/react-menu": {
@ -8944,19 +9011,38 @@
}
},
"@radix-ui/react-switch": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-0.1.4.tgz",
"integrity": "sha512-GUK0hw/FQX/MJ5Zp5W6e69fqk/KO6bRP0t0Sx89Sl//WSdcgpwk3CimV5seqk8LxL1wwBkDR5zE185U+LdBCOQ==",
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-0.1.5.tgz",
"integrity": "sha512-ITtslJPK+Yi34iNf7K9LtsPaLD76oRIVzn0E8JpEO5HW8gpRBGb2NNI9mxKtEB30TVqIcdjdL10AmuIfOMwjtg==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "0.1.0",
"@radix-ui/react-compose-refs": "0.1.0",
"@radix-ui/react-context": "0.1.1",
"@radix-ui/react-label": "0.1.4",
"@radix-ui/react-primitive": "0.1.3",
"@radix-ui/react-label": "0.1.5",
"@radix-ui/react-primitive": "0.1.4",
"@radix-ui/react-use-controllable-state": "0.1.0",
"@radix-ui/react-use-previous": "0.1.0",
"@radix-ui/react-use-size": "0.1.0"
"@radix-ui/react-use-previous": "0.1.1",
"@radix-ui/react-use-size": "0.1.1"
},
"dependencies": {
"@radix-ui/react-primitive": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz",
"integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "0.1.2"
}
},
"@radix-ui/react-use-size": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-0.1.1.tgz",
"integrity": "sha512-pTgWM5qKBu6C7kfKxrKPoBI2zZYZmp2cSXzpUiGM3qEBQlMLtYhaY2JXdXUCxz+XmD1YEjc8oRwvyfsD4AG4WA==",
"requires": {
"@babel/runtime": "^7.13.10"
}
}
}
},
"@radix-ui/react-toggle": {
@ -9095,9 +9181,9 @@
}
},
"@radix-ui/react-use-previous": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-0.1.0.tgz",
"integrity": "sha512-0fxNc33rYnCzDMPSiSnfS8YklnxQo8WqbAQXPAgIaaA1jRu2qFB916PL4qCIW+avcAAqFD38vWhqDqcVmBharA==",
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-0.1.1.tgz",
"integrity": "sha512-O/ZgrDBr11dR8rhO59ED8s5zIXBRFi8MiS+CmFGfi7MJYdLbfqVOmQU90Ghf87aifEgWe6380LA69KBneaShAg==",
"requires": {
"@babel/runtime": "^7.13.10"
}

View file

@ -16,7 +16,7 @@
"@radix-ui/react-dropdown-menu": "^0.1.4",
"@radix-ui/react-hover-card": "^0.1.5",
"@radix-ui/react-label": "^0.1.4",
"@radix-ui/react-switch": "^0.1.4",
"@radix-ui/react-switch": "^0.1.5",
"@radix-ui/react-toggle-group": "^0.1.5",
"@svgr/webpack": "^6.2.0",
"@types/axios": "^0.14.0",

View file

@ -22,7 +22,12 @@ const ProfileRoute: React.FC = () => {
const [user, setUser] = useState<User>({
id: '',
username: '',
granblueId: 0
granblueId: 0,
picture: {
picture: '',
element: ''
},
private: false
})
// Filter states
@ -63,7 +68,9 @@ const ProfileRoute: React.FC = () => {
setUser({
id: response.data.user.id,
username: response.data.user.username,
granblueId: response.data.user.granblue_id
granblueId: response.data.user.granblue_id,
picture: response.data.user.picture,
private: response.data.user.private
})
const parties: Party[] = response.data.user.parties
@ -113,11 +120,12 @@ const ProfileRoute: React.FC = () => {
<FilterBar onFilter={receiveFilters} scrolled={scrolled}>
<div className="UserInfo">
<img
alt="Gran"
className="gran"
srcSet="/profile/gran.png,
/profile/gran@2x.png 2x"
src="/profile/gran.png" />
alt={user.picture.picture}
className={`profile ${user.picture.element}`}
srcSet={`/profile/${user.picture.picture}.png,
/profile/${user.picture.picture}@2x.png 2x`}
src={`/profile/${user.picture.picture}.png`}
/>
<h1>{user.username}</h1>
</div>
</FilterBar>

BIN
public/profile/alexiel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
public/profile/aoidos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
public/profile/beatrix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

BIN
public/profile/belial.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
public/profile/cassius.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
public/profile/catura.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

BIN
public/profile/europa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 88 KiB

BIN
public/profile/gran_19.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
public/profile/gran_20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
public/profile/grimnir.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
public/profile/heles.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
public/profile/heles@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

BIN
public/profile/kumbhira.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

BIN
public/profile/lunalu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

BIN
public/profile/meg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
public/profile/meg@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

BIN
public/profile/naoise.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

BIN
public/profile/narmaya.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

BIN
public/profile/percival.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
public/profile/quatre.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

BIN
public/profile/seofon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
public/profile/seox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
public/profile/seox@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
public/profile/seruel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

BIN
public/profile/shiva.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
public/profile/shiva@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
public/profile/siero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
public/profile/siero@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

BIN
public/profile/societte.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

BIN
public/profile/tien.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
public/profile/tien@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
public/profile/vajra.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
public/profile/vajra@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

BIN
public/profile/vane.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
public/profile/vane@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
public/profile/vikala.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

BIN
public/profile/yuel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

BIN
public/profile/yuel@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

BIN
public/profile/yuisis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
public/profile/zeta.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
public/profile/zeta@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

View file

@ -290,6 +290,43 @@ select {
}
}
img.profile {
background: $grey-90;
&.fire {
background: $fire-bg-light;
}
&.water {
background: $water-bg-light;
}
&.wind {
background: $wind-bg-light;
}
&.earth {
background: $earth-bg-light;
}
&.dark {
background: $dark-bg-light;
}
&.light {
background: $light-bg-light;
}
}
i.tag {
background: $grey-90;
border-radius: $unit / 2;
font-size: 10px;
font-weight: $bold;
padding: 4px 6px;
text-transform: uppercase;
}
@keyframes openModal {
0% {
opacity: 0;

5
types/User.d.ts vendored
View file

@ -2,4 +2,9 @@ interface User {
id: string
username: string
granblueId: number
picture: {
picture: string
element: string
}
private: boolean
}

View file

@ -8,7 +8,9 @@ interface AccountState {
language: 'en' | 'jp',
user: {
id: string,
username: string
username: string,
picture: string,
element: string,
} | undefined
}
}

View file

@ -98,6 +98,11 @@ class Api {
}
})
}
userInfo(id: string) {
const resourceUrl = `${this.url}/users/info/${id}`
return axios.get(resourceUrl)
}
}
const api: Api = new Api({ url: process.env.NEXT_PUBLIC_SIERO_API_URL || 'https://localhost:3000/api/v1'})

315
utils/pictureData.tsx Normal file
View file

@ -0,0 +1,315 @@
interface Picture {
name: {
en: string
jp: string
}
filename: string
element: string
}
export const pictureData: Picture[] = [
{
name: {
en: "Gran 2019",
jp: "グラン"
},
filename: "gran_19",
element: "water"
},
{
name: {
en: "Djeeta 2019",
jp: "ジータ"
},
filename: "djeeta_19",
element: "fire"
},
{
name: {
en: "Gran 2020",
jp: "グラン"
},
filename: "gran_20",
element: "water"
},
{
name: {
en: "Djeeta 2020",
jp: "ジータ"
},
filename: "djeeta_20",
element: "fire"
},
{
name: {
en: "Gran - Farer of the Skies",
jp: "空駆ける新鋭 グランver"
},
filename: "gran",
element: "water"
},
{
name: {
en: "Djeeta - Farer of the Skies",
jp: "空駆ける新鋭 ジータver"
},
filename: "djeeta",
element: "fire"
},
{
name: {
en: "Cassius",
jp: "カシウス"
},
filename: "Cassius",
element: "dark"
},
{
name: {
en: "Percival",
jp: "パーシヴァル"
},
filename: "percival",
element: "fire"
},
{
name: {
en: "Vane",
jp: "ヴェイン"
},
filename: "vane",
element: "water"
},
{
name: {
en: "Heles",
jp: "ヘルエス"
},
filename: "heles",
element: "fire"
},
{
name: {
en: "Lunalu",
jp: "ルナール"
},
filename: "lunalu",
element: "dark"
},
{
name: {
en: "Catura",
jp: "シャトラ"
},
filename: "catura",
element: "wind"
},
{
name: {
en: "Yuisis",
jp: "ユイシス"
},
filename: "yuisis",
element: "wind"
},
{
name: {
en: "Meg",
jp: "メグ"
},
filename: "meg",
element: "dark"
},
{
name: {
en: "Seofon",
jp: "シエテ"
},
filename: "seofon",
element: "wind"
},
{
name: {
en: "Quatre",
jp: "カトル"
},
filename: "quatre",
element: "water"
},
{
name: {
en: "Tien",
jp: "エッセル"
},
filename: "Tien",
element: "fire"
},
{
name: {
en: "Seox",
jp: "シス"
},
filename: "seox",
element: "dark"
},
{
name: {
en: "Aoidos",
jp: "アオイドス"
},
filename: "aoidos",
element: "fire"
},
{
name: {
en: "Sandalphon",
jp: "サンダルフォン"
},
filename: "sandalphon",
element: "light"
},
{
name: {
en: "Vikala",
jp: "ビカラ"
},
filename: "vikala",
element: "dark"
},
{
name: {
en: "Belial",
jp: "ベリアル"
},
filename: "belial",
element: "dark"
},
{
name: {
en: "Zeta",
jp: "ゼタ"
},
filename: "zeta",
element: "fire"
},
{
name: {
en: "Beatrix",
jp: "ベアトリックス"
},
filename: "beatrix",
element: "earth"
},
{
name: {
en: "Yuel",
jp: "ユエル"
},
filename: "yuel",
element: "fire"
},
{
name: {
en: "Societte",
jp: "ソシエ"
},
filename: "societte",
element: "water"
},
{
name: {
en: "Kumbhira",
jp: "クビラ"
},
filename: "kumbhira",
element: "light"
},
{
name: {
en: "Narmaya",
jp: "ナルメア"
},
filename: "narmaya",
element: "dark"
},
{
name: {
en: "Siegfried",
jp: "ジークフリード"
},
filename: "siegfried",
element: "earth"
},
{
name: {
en: "Naoise",
jp: "ノイシュ"
},
filename: "naoise",
element: "light"
},
{
name: {
en: "Scathacha",
jp: "スカーサハ"
},
filename: "scathacha",
element: "wind"
},
{
name: {
en: "Seruel",
jp: "セルエル"
},
filename: "seruel",
element: "light"
},
{
name: {
en: "Shiva",
jp: "シヴァ"
},
filename: "shiva",
element: "fire"
},
{
name: {
en: "Europa",
jp: "エウロペ"
},
filename: "europa",
element: "water"
},
{
name: {
en: "Grimnir",
jp: "グリームニル"
},
filename: "grimnir",
element: "wind"
},
{
name: {
en: "Alexiel",
jp: "ブローディア"
},
filename: "alexiel",
element: "earth"
},
{
name: {
en: "Sierokarte",
jp: "シェロカルテ"
},
filename: "siero",
element: "wind"
},
{
name: {
en: "Vajra",
jp: "ヴァジラ"
},
filename: "vajra",
element: "water"
}
]