Fixes for reactivity and performance (#314)

* Remove editable styles

* Use snapshot for segment reps

Using snapshots lets that data be reactive.

Also removed extra dependencies and fixed a bug in how SummonRep displayed sub-summons

* Don't display QuickSummon on friends, subaura

* Hotfix refreshing when switching tabs
This commit is contained in:
Justin Edmund 2023-06-18 16:38:36 -07:00 committed by GitHub
parent 45645aa352
commit 36804779e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 123 additions and 268 deletions

View file

@ -351,7 +351,8 @@ const Party = (props: Props) => {
// Methods: Navigating with segmented control
function segmentClicked(event: React.ChangeEvent<HTMLInputElement>) {
const path = [
router.asPath.split('/').filter((el) => el != '')[1],
// Enable when using Next.js Router
// router.asPath.split('/').filter((el) => el != '')[1],
event.target.value,
].join('/')
@ -369,7 +370,10 @@ const Party = (props: Props) => {
break
}
router.replace(path, undefined, { shallow: true })
// Ideally, we would use the Next.js Router to replace the URL,
// but something about shallow routing isn't working so the page is refreshing
// router.replace(path, undefined, { shallow: true })
history.pushState({}, '', path)
}
// Render: JSX components

View file

@ -18,7 +18,9 @@
.PartyDetails {
box-sizing: border-box;
display: none;
display: block;
line-height: 1.4;
white-space: pre-wrap;
margin: 0 auto $unit-2x;
max-width: $unit * 94;
overflow: hidden;
@ -28,261 +30,105 @@
padding: 0 $unit;
}
&.Visible {
// margin-bottom: $unit-12x;
a:hover {
text-decoration: underline;
}
&.Editable {
p {
font-size: $font-regular;
line-height: $font-regular * 1.2;
white-space: pre-line;
}
.Tokens {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: $unit;
&.Visible {
display: grid;
}
fieldset {
display: block;
width: 100%;
textarea {
min-height: $unit * 22;
width: 100%;
}
}
.SelectTrigger {
padding: $unit-2x;
width: 100%;
}
.DetailToggleGroup {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: $unit;
@include breakpoint(phone) {
grid-template-columns: 1fr;
}
.ToggleSection,
.InputSection {
align-items: center;
display: flex;
background: var(--card-bg);
border-radius: $input-corner;
& > label {
align-items: center;
display: flex;
font-size: $font-regular;
gap: $unit;
grid-template-columns: 2fr 1fr;
justify-content: space-between;
width: 100%;
& > span {
flex-grow: 1;
}
}
}
.ToggleSection {
padding: ($unit * 1.5) $unit-2x;
}
.InputSection {
padding: $unit-half $unit-2x;
padding-right: $unit-half;
.Input {
border-radius: 7px;
}
div.Input {
align-items: center;
border: 2px solid transparent;
box-sizing: border-box;
display: flex;
padding: $unit;
&:has(> input:focus) {
border: 2px solid $blue;
outline: none;
}
& > input {
background: transparent;
border: none;
padding: $unit 0;
text-align: right;
width: 2rem;
&:focus {
outline: none;
border: none;
}
}
}
label {
display: flex;
justify-content: space-between;
span {
flex-grow: 1;
}
.Input {
border-radius: 7px;
max-width: 10rem;
}
div {
display: flex;
flex-direction: row;
gap: $unit-half;
justify-content: right;
}
}
}
}
.bottom {
display: flex;
flex-direction: row;
gap: $unit;
@include breakpoint(phone) {
flex-direction: column;
width: 100%;
}
.left {
flex-grow: 1;
}
.right {
display: flex;
flex-direction: row;
gap: $unit;
@include breakpoint(phone) {
.Button {
flex-grow: 1;
}
}
}
}
margin-bottom: $unit-2x;
}
&.ReadOnly {
box-sizing: border-box;
line-height: 1.4;
white-space: pre-wrap;
.YoutubeWrapper {
background-color: var(--card-bg);
border-radius: $card-corner;
margin: $unit 0;
position: relative;
display: block;
contain: content;
background-position: center center;
background-size: cover;
cursor: pointer;
width: 60%;
height: 60%;
&.Visible {
@include breakpoint(tablet) {
width: 100%;
height: 100%;
}
/* gradient */
&::before {
content: '';
display: block;
position: absolute;
top: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
background-position: top;
background-repeat: repeat-x;
height: 60px;
padding-bottom: 50px;
width: 100%;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}
a:hover {
text-decoration: underline;
}
p {
font-size: $font-regular;
line-height: $font-regular * 1.2;
white-space: pre-line;
}
.Tokens {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: $unit;
margin-bottom: $unit-2x;
}
.YoutubeWrapper {
background-color: var(--card-bg);
border-radius: $card-corner;
margin: $unit 0;
position: relative;
display: block;
contain: content;
background-position: center center;
background-size: cover;
cursor: pointer;
width: 60%;
height: 60%;
@include breakpoint(tablet) {
width: 100%;
height: 100%;
}
/* gradient */
&::before {
content: '';
display: block;
position: absolute;
top: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
background-position: top;
background-repeat: repeat-x;
height: 60px;
padding-bottom: 50px;
width: 100%;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}
/* responsive iframe with a 16:9 aspect ratio
/* responsive iframe with a 16:9 aspect ratio
thanks https://css-tricks.com/responsive-iframes/
*/
&::after {
content: '';
display: block;
padding-bottom: calc(100% / (16 / 9));
}
&::after {
content: '';
display: block;
padding-bottom: calc(100% / (16 / 9));
}
&:hover > .PlayerButton {
opacity: 1;
}
&:hover > .PlayerButton {
opacity: 1;
}
& > iframe {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
& > iframe {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
/* Play button */
& > .PlayerButton {
background: none;
border: none;
background-image: url('/icons/youtube.svg');
width: 68px;
height: 68px;
opacity: 0.8;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}
/* Play button */
& > .PlayerButton {
background: none;
border: none;
background-image: url('/icons/youtube.svg');
width: 68px;
height: 68px;
opacity: 0.8;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
}
& > .PlayerButton,
& > .PlayerButton:before {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
& > .PlayerButton,
& > .PlayerButton:before {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
/* Post-click styles */
&.lyt-activated {
cursor: unset;
}
&.lyt-activated::before,
&.lyt-activated > .PlayerButton {
opacity: 0;
pointer-events: none;
}
/* Post-click styles */
&.lyt-activated {
cursor: unset;
}
&.lyt-activated::before,
&.lyt-activated > .PlayerButton {
opacity: 0;
pointer-events: none;
}
}
}

View file

@ -57,7 +57,6 @@ const PartyHeader = (props: Props) => {
const classes = classNames({
PartyDetails: true,
ReadOnly: true,
})
const userClass = classNames({

View file

@ -1,20 +1,25 @@
import React from 'react'
import { useSnapshot } from 'valtio'
import { useTranslation } from 'next-i18next'
import classNames from 'classnames'
import { appState } from '~utils/appState'
import { accountState } from '~utils/accountState'
import SegmentedControl from '~components/common/SegmentedControl'
import RepSegment from '~components/reps/RepSegment'
import CharacterRep from '~components/reps/CharacterRep'
import WeaponRep from '~components/reps/WeaponRep'
import SummonRep from '~components/reps/SummonRep'
import { GridType } from '~utils/enums'
import './index.scss'
import classNames from 'classnames'
import RepSegment from '~components/reps/RepSegment'
import CharacterRep from '~components/reps/CharacterRep'
import { accountState } from '~utils/accountState'
import WeaponRep from '~components/reps/WeaponRep'
import SummonRep from '~components/reps/SummonRep'
// Fix for valtio readonly array
declare module 'valtio' {
function useSnapshot<T extends object>(p: T): T
}
interface Props {
selectedTab: GridType
@ -27,7 +32,7 @@ const PartySegmentedControl = (props: Props) => {
const { party, grid } = useSnapshot(appState)
function getElement() {
const getElement = () => {
let element: number = 0
if (party.element == 0 && grid.weapons.mainWeapon)
element = grid.weapons.mainWeapon.element
@ -55,16 +60,16 @@ const PartySegmentedControl = (props: Props) => {
controlGroup="grid"
inputName="characters"
name={t('party.segmented_control.characters')}
selected={props.selectedTab == GridType.Character}
selected={props.selectedTab === GridType.Character}
onClick={props.onClick}
>
<CharacterRep
job={appState.party?.job}
element={appState.party?.element}
job={party.job}
element={party.element}
gender={
accountState.account.user ? accountState.account.user.gender : 0
}
grid={appState.grid.characters}
grid={grid.characters}
/>
</RepSegment>
)
@ -77,10 +82,10 @@ const PartySegmentedControl = (props: Props) => {
controlGroup="grid"
inputName="weapons"
name="Weapons"
selected={props.selectedTab == GridType.Weapon}
selected={props.selectedTab === GridType.Weapon}
onClick={props.onClick}
>
<WeaponRep grid={appState.grid.weapons} />
<WeaponRep grid={grid.weapons} />
</RepSegment>
)
}
@ -92,10 +97,10 @@ const PartySegmentedControl = (props: Props) => {
controlGroup="grid"
inputName="summons"
name="Summons"
selected={props.selectedTab == GridType.Summon}
selected={props.selectedTab === GridType.Summon}
onClick={props.onClick}
>
<SummonRep grid={appState.grid.summons} />
<SummonRep grid={grid.summons} />
</RepSegment>
)
}

View file

@ -1,7 +1,5 @@
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import 'fix-date'
import './index.scss'
@ -18,7 +16,6 @@ const SUMMONS_COUNT = 4
const SummonRep = (props: Props) => {
// Localization for alt tags
const router = useRouter()
const { t } = useTranslation('common')
const locale =
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
@ -159,8 +156,8 @@ const SummonRep = (props: Props) => {
<ul className="GridSummons">
{Array.from(Array(SUMMONS_COUNT)).map((x, i) => {
return (
<li key={`summons-${i + 1}`} className="Grid Summon">
{generateGridImage(i + 1)}
<li key={`summons-${i}`} className="Grid Summon">
{generateGridImage(i)}
</li>
)
})}

View file

@ -1,7 +1,5 @@
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import 'fix-date'
import './index.scss'
@ -17,7 +15,6 @@ const WEAPONS_COUNT = 9
const WeaponRep = (props: Props) => {
// Localization for alt tags
const router = useRouter()
const { t } = useTranslation('common')
const locale =
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'

View file

@ -70,4 +70,8 @@
.SummonUnit .SummonImage .icon svg {
fill: var(--subaura-orange-secondary);
}
.SummonUnit .QuickSummon {
display: none;
}
}

View file

@ -117,8 +117,7 @@
opacity: 1;
}
&.main .QuickSummon,
&.friend .QuickSummon {
&.main .QuickSummon {
$diameter: $unit-6x;
background-size: $diameter $diameter;
top: -2%;
@ -127,6 +126,10 @@
height: $diameter;
}
&.friend .QuickSummon {
display: none;
}
&.grid .QuickSummon {
$diameter: $unit-5x;
background-size: $diameter $diameter;