diff --git a/components/character/CharacterModal/index.tsx b/components/character/CharacterModal/index.tsx index 6a170d6d..09e3622e 100644 --- a/components/character/CharacterModal/index.tsx +++ b/components/character/CharacterModal/index.tsx @@ -270,7 +270,7 @@ const CharacterModal = ({ - + You will lose all changes to{' '} {{ objectName: gridCharacter.object.name[locale] }}{' '} if you continue. @@ -281,9 +281,9 @@ const CharacterModal = ({ } open={alertOpen} - primaryActionText="Close" + primaryActionText={t('alert.unsaved_changes.buttons.confirm')} primaryAction={close} - cancelActionText="Nevermind" + cancelActionText={t('alert.unsaved_changes.buttons.cancel')} cancelAction={() => setAlertOpen(false)} /> ) diff --git a/components/common/Alert/index.module.scss b/components/common/Alert/index.module.scss index 7a4b55a9..30a755e9 100644 --- a/components/common/Alert/index.module.scss +++ b/components/common/Alert/index.module.scss @@ -29,7 +29,7 @@ flex-direction: column; gap: $unit-2x; min-width: 20vw; - max-width: 30vw; + max-width: 32vw; padding: $unit * 4; @include breakpoint(tablet) { diff --git a/components/common/Button/index.module.scss b/components/common/Button/index.module.scss index b61e394d..407af421 100644 --- a/components/common/Button/index.module.scss +++ b/components/common/Button/index.module.scss @@ -46,6 +46,10 @@ flex-grow: 1; } + &.no-shrink { + flex-shrink: 0; + } + &.blended { background: transparent; } @@ -304,6 +308,15 @@ } } + &.notice { + background-color: var(--notice-button-bg); + color: var(--notice-button-text); + + &:hover { + background-color: var(--notice-button-bg-hover); + } + } + &.destructive { background: $error; color: white; diff --git a/components/party/EditPartyModal/index.tsx b/components/party/EditPartyModal/index.tsx index 26103c51..1fed3e8a 100644 --- a/components/party/EditPartyModal/index.tsx +++ b/components/party/EditPartyModal/index.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useRef, useState } from 'react' -import { useRouter } from 'next/router' import { useSnapshot } from 'valtio' import { Trans, useTranslation } from 'react-i18next' import classNames from 'classnames' @@ -20,7 +19,6 @@ import SegmentedControl from '~components/common/SegmentedControl' import Segment from '~components/common/Segment' import SwitchTableField from '~components/common/SwitchTableField' import TableField from '~components/common/TableField' -import Textarea from '~components/common/Textarea' import capitalizeFirstLetter from '~utils/capitalizeFirstLetter' import type { DetailsObject } from 'types' @@ -384,7 +382,7 @@ const EditPartyModal = ({ - + You will lose all changes to your party{' '} {{ @@ -399,9 +397,9 @@ const EditPartyModal = ({ } open={alertOpen} - primaryActionText="Close" + primaryActionText={t('alert.unsaved_changes.buttons.confirm')} primaryAction={close} - cancelActionText="Nevermind" + cancelActionText={t('alert.unsaved_changes.buttons.cancel')} cancelAction={() => setAlertOpen(false)} /> ) diff --git a/components/party/Party/index.tsx b/components/party/Party/index.tsx index 2333c345..58998e08 100644 --- a/components/party/Party/index.tsx +++ b/components/party/Party/index.tsx @@ -169,6 +169,7 @@ const Party = (props: Props) => { if (details.guidebook1_id) payload.guidebook1_id = details.guidebook1_id if (details.guidebook2_id) payload.guidebook2_id = details.guidebook2_id if (details.guidebook3_id) payload.guidebook3_id = details.guidebook3_id + if (details.visibility) payload.visibility = details.visibility if (getLocalId()) payload.local_id = getLocalId() if (Object.keys(payload).length >= 1) return { party: payload } @@ -292,6 +293,7 @@ const Party = (props: Props) => { appState.party.favorited = team.favorited appState.party.remix = team.remix appState.party.remixes = team.remixes + appState.party.visibility = team.visibility appState.party.sourceParty = team.source_party appState.party.created_at = team.created_at appState.party.updated_at = team.updated_at diff --git a/components/party/PartyDropdown/index.tsx b/components/party/PartyDropdown/index.tsx index 52a2a8ca..eadea44e 100644 --- a/components/party/PartyDropdown/index.tsx +++ b/components/party/PartyDropdown/index.tsx @@ -1,5 +1,5 @@ // Libraries -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { useRouter } from 'next/router' import { useSnapshot } from 'valtio' import { useTranslation } from 'next-i18next' @@ -33,12 +33,14 @@ interface Props { editable: boolean deleteTeamCallback: () => void remixTeamCallback: () => void + teamVisibilityCallback: () => void } const PartyDropdown = ({ editable, deleteTeamCallback, remixTeamCallback, + teamVisibilityCallback, }: Props) => { // Localization const { t } = useTranslation('common') @@ -81,6 +83,11 @@ const PartyDropdown = ({ // Methods: Event handlers + // Dialogs / Visibility + function visibilityCallback() { + teamVisibilityCallback() + } + // Alerts / Delete team function openDeleteTeamAlert() { setDeleteAlertOpen(true) @@ -125,6 +132,9 @@ const PartyDropdown = ({ const items = ( <> + + {t('dropdown.party.visibility')} + {t('dropdown.party.copy')} diff --git a/components/party/PartyFooter/index.tsx b/components/party/PartyFooter/index.tsx index 9ae4e432..a028a0d1 100644 --- a/components/party/PartyFooter/index.tsx +++ b/components/party/PartyFooter/index.tsx @@ -260,16 +260,7 @@ const PartyFooter = (props: Props) => { return partySnapshot?.remixes.map((party, i) => { return ( { // State: Component const [detailsOpen, setDetailsOpen] = useState(false) + const [copyToastOpen, setCopyToastOpen] = useState(false) const [remixAlertOpen, setRemixAlertOpen] = useState(false) const [remixToastOpen, setRemixToastOpen] = useState(false) + const [visibilityDialogOpen, setVisibilityDialogOpen] = useState(false) const userClass = classNames({ [styles.user]: true, @@ -122,12 +128,29 @@ const PartyHeader = (props: Props) => { setDetailsOpen(open) } + // Dialogs: Visibility + function visibilityDialogCallback() { + setVisibilityDialogOpen(true) + } + + function handleVisibilityDialogChange(open: boolean) { + setVisibilityDialogOpen(open) + } + // Actions: Remix team function remixTeamCallback() { setRemixToastOpen(true) props.remixCallback() } + // Actions: Copy URL + function copyToClipboard() { + if (router.asPath.split('/')[1] === 'p') { + navigator.clipboard.writeText(window.location.href) + setCopyToastOpen(true) + } + } + // Alerts: Remix team function openRemixTeamAlert() { setRemixAlertOpen(true) @@ -146,6 +169,15 @@ const PartyHeader = (props: Props) => { setRemixToastOpen(false) } + // Toasts / Copy URL + function handleCopyToastOpenChanged(open: boolean) { + setCopyToastOpen(!open) + } + + function handleCopyToastCloseClicked() { + setCopyToastOpen(false) + } + // Rendering const userBlock = (username?: string, picture?: string, element?: string) => { @@ -298,6 +330,50 @@ const PartyHeader = (props: Props) => { ) } + // Render: Notice + const unlistedNotice = ( +
+
+ +
+

{t('party.notices.unlisted')}

+
+
+
+ ) + + const privateNotice = ( +
+
+ +
+

{t('party.notices.private')}

+
+
+
+ ) + // Render: Buttons const saveButton = () => { return ( @@ -358,6 +434,8 @@ const PartyHeader = (props: Props) => { return ( <>
+ {party.visibility == 2 && unlistedNotice} + {party.visibility == 3 && privateNotice}
@@ -399,6 +477,7 @@ const PartyHeader = (props: Props) => { editable={props.editable} deleteTeamCallback={props.deleteCallback} remixTeamCallback={props.remixCallback} + teamVisibilityCallback={visibilityDialogCallback} /> )}
@@ -412,6 +491,13 @@ const PartyHeader = (props: Props) => {
{renderTokens()}
+ + { onOpenChange={handleRemixToastOpenChanged} onCloseClick={handleRemixToastCloseClicked} /> + + ) } diff --git a/components/party/PartyVisibilityDialog/index.module.scss b/components/party/PartyVisibilityDialog/index.module.scss new file mode 100644 index 00000000..4d0c9ed1 --- /dev/null +++ b/components/party/PartyVisibilityDialog/index.module.scss @@ -0,0 +1,83 @@ +.content { + display: flex; + flex-direction: column; + gap: $unit-4x; + padding: 0 $unit-4x $unit-2x; + + .description { + color: var(--text-primary); + font-size: $font-regular; + } + + .radioGroup { + display: flex; + flex-direction: column; + gap: $unit-2x; + + .radioSet { + display: flex; + gap: $unit; + + .radioItem { + align-items: center; + background: var(--radio-button-bg); + border-radius: $full-corner; + border: none; + display: flex; + border: 2px solid transparent; + box-sizing: border-box; + justify-content: center; + height: $unit-4x; + width: $unit-4x; + min-height: $unit-4x; + min-width: $unit-4x; + + &:focus { + outline: 2px solid var(--radio-active-bg); + + &:hover { + outline-color: var(--radio-active-bg-hover); + } + } + + [data-state='checked'] { + background-color: var(--radio-active-bg); + border-radius: $full-corner; + display: block; + height: $unit-2x; + width: $unit-2x; + } + + &[data-state='checked']:hover [data-state='checked'] { + background-color: var(--radio-active-bg-hover); + } + + &:hover { + background: var(--radio-button-bg-hover); + cursor: pointer; + } + } + + label { + display: flex; + flex-direction: column; + gap: $unit-half; + + &:hover { + cursor: pointer; + } + + h4 { + color: var(--text-primary); + font-size: $font-regular; + font-weight: $bold; + } + + p { + color: var(--text-tertiary); + font-size: $font-small; + } + } + } + } +} diff --git a/components/party/PartyVisibilityDialog/index.tsx b/components/party/PartyVisibilityDialog/index.tsx new file mode 100644 index 00000000..cfe3f34f --- /dev/null +++ b/components/party/PartyVisibilityDialog/index.tsx @@ -0,0 +1,303 @@ +import React, { useEffect, useRef, useState } from 'react' +import { useSnapshot } from 'valtio' +import { useTranslation } from 'react-i18next' +import debounce from 'lodash.debounce' + +import * as RadioGroup from '@radix-ui/react-radio-group' +import Alert from '~components/common/Alert' +import Button from '~components/common/Button' +import { Dialog, DialogTrigger } from '~components/common/Dialog' +import DialogHeader from '~components/common/DialogHeader' +import DialogFooter from '~components/common/DialogFooter' +import DialogContent from '~components/common/DialogContent' + +import type { DetailsObject } from 'types' +import type { DialogProps } from '@radix-ui/react-dialog' + +import { appState } from '~utils/appState' + +import styles from './index.module.scss' + +interface Props extends DialogProps { + open: boolean + value: 1 | 2 | 3 + onOpenChange?: (open: boolean) => void + updateParty: (details: DetailsObject) => Promise +} + +const EditPartyModal = ({ + open, + value, + updateParty, + onOpenChange, + ...props +}: Props) => { + // Set up translation + const { t } = useTranslation('common') + + // Set up reactive state + const { party } = useSnapshot(appState) + + // Refs + const headerRef = React.createRef() + const topContainerRef = React.createRef() + const footerRef = React.createRef() + const radioItemRef = [ + React.createRef(), + React.createRef(), + React.createRef(), + ] + + // States: Component + const [alertOpen, setAlertOpen] = useState(false) + const [errors, setErrors] = useState<{ [key: string]: string }>({ + name: '', + description: '', + }) + + // States: Data + const [visibility, setVisibility] = useState(1) + + // Hooks + useEffect(() => { + setVisibility(party.visibility) + }, [value]) + + // Methods: Event handlers (Dialog) + function handleOpenChange() { + if (hasBeenModified() && open) { + setAlertOpen(true) + } else if (!hasBeenModified() && open) { + close() + } else { + if (onOpenChange) onOpenChange(true) + } + } + + function close() { + setAlertOpen(false) + setVisibility(party.visibility) + if (onOpenChange) onOpenChange(false) + } + + function onEscapeKeyDown(event: KeyboardEvent) { + event.preventDefault() + handleOpenChange() + } + + function onOpenAutoFocus(event: Event) { + event.preventDefault() + } + + // Methods: Event handlers (Fields) + function handleValueChange(value: string) { + const newVisibility = parseInt(value) + setVisibility(newVisibility) + } + + // Handlers + function handleScroll(event: React.UIEvent) { + const scrollTop = event.currentTarget.scrollTop + const scrollHeight = event.currentTarget.scrollHeight + const clientHeight = event.currentTarget.clientHeight + + if (topContainerRef && topContainerRef.current) + manipulateHeaderShadow(topContainerRef.current, scrollTop) + + if (footerRef && footerRef.current) + manipulateFooterShadow( + footerRef.current, + scrollTop, + scrollHeight, + clientHeight + ) + } + + function manipulateHeaderShadow(header: HTMLDivElement, scrollTop: number) { + const boxShadowBase = '0 2px 8px' + const maxValue = 50 + + if (scrollTop >= 0) { + const input = scrollTop > maxValue ? maxValue : scrollTop + + const boxShadowOpacity = mapRange(input, 0, maxValue, 0.0, 0.16) + const borderOpacity = mapRange(input, 0, maxValue, 0.0, 0.24) + + header.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, ${boxShadowOpacity})` + header.style.borderBottomColor = `rgba(0, 0, 0, ${borderOpacity})` + } + } + + function manipulateFooterShadow( + footer: HTMLDivElement, + scrollTop: number, + scrollHeight: number, + clientHeight: number + ) { + const boxShadowBase = '0 -2px 8px' + const minValue = scrollHeight - 200 + const currentScroll = scrollTop + clientHeight + + if (currentScroll >= minValue) { + const input = currentScroll < minValue ? minValue : currentScroll + + const boxShadowOpacity = mapRange( + input, + minValue, + scrollHeight, + 0.16, + 0.0 + ) + const borderOpacity = mapRange(input, minValue, scrollHeight, 0.24, 0.0) + + footer.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, ${boxShadowOpacity})` + footer.style.borderTopColor = `rgba(0, 0, 0, ${borderOpacity})` + } + } + + const calculateFooterShadow = debounce(() => { + const boxShadowBase = '0 -2px 8px' + const scrollable = document.querySelector(`.${styles.scrollable}`) + const footer = footerRef + + if (footer && footer.current) { + if (scrollable) { + if (scrollable.clientHeight >= scrollable.scrollHeight) { + footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0)` + footer.current.style.borderTopColor = `rgba(0, 0, 0, 0)` + } else { + footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0.16)` + footer.current.style.borderTopColor = `rgba(0, 0, 0, 0.24)` + } + } else { + footer.current.style.boxShadow = `${boxShadowBase} rgba(0, 0, 0, 0)` + footer.current.style.borderTopColor = `rgba(0, 0, 0, 0)` + } + } + }, 100) + + useEffect(() => { + window.addEventListener('resize', calculateFooterShadow) + calculateFooterShadow() + + return () => { + window.removeEventListener('resize', calculateFooterShadow) + } + }, [calculateFooterShadow]) + + function mapRange( + value: number, + low1: number, + high1: number, + low2: number, + high2: number + ) { + return low2 + ((high2 - low2) * (value - low1)) / (high1 - low1) + } + + // Methods: Modification checking + function hasBeenModified() { + return visibility !== party.visibility + } + + // Methods: Data methods + async function updateDetails(event: React.MouseEvent) { + const details: DetailsObject = { + visibility: visibility, + } + + await updateParty(details) + if (onOpenChange) onOpenChange(false) + } + + // Methods: Rendering methods + function renderRadioItem(value: string, label: string) { + return ( +
+ + + + +
+ ) + } + + const confirmationAlert = ( + setAlertOpen(false)} + /> + ) + + return ( + <> + {confirmationAlert} + + {props.children} + + + +
+

+ {t('modals.team_visibility.description')} +

+ + {renderRadioItem('1', 'public')} + {renderRadioItem('2', 'unlisted')} + {renderRadioItem('3', 'private')} + +
+ + onOpenChange && onOpenChange(false)} + key="cancel" + text={t('buttons.cancel')} + />, +
+ + ) +} + +export default EditPartyModal diff --git a/components/reps/CharacterRep/index.module.scss b/components/reps/CharacterRep/index.module.scss index 64eb801b..e79b138f 100644 --- a/components/reps/CharacterRep/index.module.scss +++ b/components/reps/CharacterRep/index.module.scss @@ -47,32 +47,32 @@ } &.fire { - background: var(--fire-hover-bg); + background: var(--fire-portrait-bg); border-color: var(--fire-bg); } &.water { - background: var(--water-hover-bg); + background: var(--water-portrait-bg); border-color: var(--water-bg); } &.wind { - background: var(--wind-hover-bg); + background: var(--wind-portrait-bg); border-color: var(--wind-bg); } &.earth { - background: var(--earth-hover-bg); + background: var(--earth-portrait-bg); border-color: var(--earth-bg); } &.light { - background: var(--light-hover-bg); + background: var(--light-portrait-bg); border-color: var(--light-bg); } &.dark { - background: var(--dark-hover-bg); + background: var(--dark-portrait-bg); border-color: var(--dark-bg); } diff --git a/components/reps/GridRep/index.module.scss b/components/reps/GridRep/index.module.scss index b5525d52..e3680ac6 100644 --- a/components/reps/GridRep/index.module.scss +++ b/components/reps/GridRep/index.module.scss @@ -6,11 +6,15 @@ display: grid; grid-template-rows: 1fr 1fr; gap: $unit; - padding: $unit-2x; + padding: $unit-2x $unit-2x 0 $unit-2x; min-width: 320px; width: 100%; opacity: 1; + @include breakpoint(phone) { + padding-bottom: $unit-2x; + } + &.visible { transition: opacity 0.3s ease-in-out; opacity: 1; @@ -29,6 +33,10 @@ text-decoration: none; } + .indicators { + opacity: 1; + } + .weaponGrid { cursor: pointer; @@ -46,7 +54,70 @@ } } - & > .weaponGrid { + .gridContainer { + aspect-ratio: 2/0.95; + width: 100%; + } + + .characterGrid { + aspect-ratio: 2/0.95; + display: flex; + justify-content: space-between; + + .protagonist { + border-width: 1px; + border-style: solid; + + &.fire { + background: var(--fire-portrait-bg); + border-color: var(--fire-bg); + } + + &.water { + background: var(--water-portrait-bg); + border-color: var(--water-bg); + } + + &.wind { + background: var(--wind-portrait-bg); + border-color: var(--wind-bg); + } + + &.earth { + background: var(--earth-portrait-bg); + border-color: var(--earth-bg); + } + + &.light { + background: var(--light-portrait-bg); + border-color: var(--light-bg); + } + + &.dark { + background: var(--dark-portrait-bg); + border-color: var(--dark-bg); + } + + &.empty { + background: var(--card-bg); + } + } + + .grid { + background: var(--background); + border-radius: $item-corner-small; + aspect-ratio: 69/142; + list-style: none; + height: calc(100% - $unit-half); + + img { + border-radius: $item-corner-small; + width: 100%; + } + } + } + + .weaponGrid { aspect-ratio: 2/0.95; display: grid; grid-template-columns: 1fr 3.36fr; /* left column takes up 1 fraction, right column takes up 3 fractions */ @@ -54,7 +125,7 @@ .weapon { background: var(--unit-bg); - border-radius: 4px; + border-radius: $item-corner-small; } .mainhand.weapon { @@ -91,6 +162,51 @@ } } + .summonGrid { + aspect-ratio: 2/0.94; + display: flex; + gap: $unit; + justify-content: space-between; + + .summon, + .mainSummon { + background: var(--background); + border-radius: $item-corner-small; + + img { + border-radius: $item-corner-small; + width: 100%; + } + } + + .mainSummon { + aspect-ratio: 56/97; + display: grid; + grid-column: 1 / 2; /* spans one column */ + } + + .summons { + display: grid; /* make the right-images container a grid */ + grid-template-columns: repeat( + 2, + 1fr + ); /* create 3 columns, each taking up 1 fraction */ + grid-template-rows: repeat( + 2, + 1fr + ); /* create 3 rows, each taking up 1 fraction */ + gap: $unit; + aspect-ratio: 83/100; + // column-gap: $unit; + // row-gap: $unit-2x; + } + + .summon { + aspect-ratio: 184 / 138; + display: grid; + } + } + .details { display: flex; flex-direction: column; @@ -104,6 +220,7 @@ padding-bottom: 1px; text-overflow: ellipsis; white-space: nowrap; + min-height: 24px; max-width: 258px; // Can we not do this? &.empty { @@ -124,6 +241,18 @@ gap: calc($unit / 2); } + .icon { + display: flex; + align-items: center; + justify-content: center; + width: $unit * 2.5; + height: $unit * 2.5; + + svg { + fill: var(--text-tertiary); + } + } + button svg { width: 14px; height: 14px; @@ -157,6 +286,7 @@ } time { + line-height: 18px; white-space: nowrap; } @@ -234,4 +364,41 @@ } } } + + .indicators { + display: flex; + flex-direction: row; + gap: $unit; + margin-top: $unit * -1; + margin-bottom: $unit-fourth; + justify-content: center; + opacity: 0; + + @include breakpoint(phone) { + display: none; + } + + li { + flex-grow: 1; + text-indent: -9999px; + padding: $unit 0; + + .indicator { + transition: background-color 0.12s ease-in-out; + height: $unit; + border-radius: $unit-half; + background-color: var(--button-contained-bg-hover); + } + + span { + text-indent: -9999px; + position: absolute; + } + + &:hover .indicator, + &.active .indicator { + background-color: var(--text-secondary); + } + } + } } diff --git a/components/reps/GridRep/index.tsx b/components/reps/GridRep/index.tsx index 84e9b86e..3c364e5e 100644 --- a/components/reps/GridRep/index.tsx +++ b/components/reps/GridRep/index.tsx @@ -10,29 +10,24 @@ import { accountState } from '~utils/accountState' import { formatTimeAgo } from '~utils/timeAgo' import Button from '~components/common/Button' +import Tooltip from '~components/common/Tooltip' import SaveIcon from '~public/icons/Save.svg' +import PrivateIcon from '~public/icons/Private.svg' +import UnlistedIcon from '~public/icons/Unlisted.svg' import ShieldIcon from '~public/icons/Shield.svg' import styles from './index.module.scss' interface Props { - shortcode: string - id: string - name: string - raid: Raid - grid: GridWeapon[] - user?: User - fullAuto: boolean - autoGuard: boolean - favorited: boolean + party: Party loading: boolean - createdAt: Date onClick: (shortcode: string) => void onSave?: (partyId: string, favorited: boolean) => void } -const GridRep = (props: Props) => { +const GridRep = ({ party, loading, onClick, onSave }: Props) => { const numWeapons: number = 9 + const numSummons: number = 6 const { account } = useSnapshot(accountState) @@ -42,27 +37,42 @@ const GridRep = (props: Props) => { router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' const [visible, setVisible] = useState(false) + const [currentView, setCurrentView] = useState< + 'characters' | 'weapons' | 'summons' + >('weapons') + const [mainhand, setMainhand] = useState() const [weapons, setWeapons] = useState>({}) - const [grid, setGrid] = useState>({}) + const [weaponGrid, setWeaponGrid] = useState>({}) + const [characters, setCharacters] = useState>({}) + const [characterGrid, setCharacterGrid] = useState>( + {} + ) + const [mainSummon, setMainSummon] = useState() + const [friendSummon, setFriendSummon] = useState() + const [summons, setSummons] = useState>({}) + const [summonGrid, setSummonGrid] = useState>({}) - const gridRepStyles = classNames({ + // Style construction + + const gridRepClasses = classNames({ [styles.gridRep]: true, [styles.visible]: visible, [styles.hidden]: !visible, }) + const titleClass = classNames({ - empty: !props.name, + empty: !party.name, }) const raidClass = classNames({ [styles.raid]: true, - [styles.empty]: !props.raid, + [styles.empty]: !party.raid, }) const userClass = classNames({ [styles.user]: true, - [styles.empty]: !props.user, + [styles.empty]: !party.user, }) const mainhandClasses = classNames({ @@ -75,8 +85,22 @@ const GridRep = (props: Props) => { [styles.grid]: true, }) + const protagonistClasses = classNames({ + [styles.protagonist]: true, + [styles.grid]: true, + [styles[`${numberToElement()}`]]: true, + [styles.empty]: !party.job || party.job.id === '-1', + }) + + const characterClasses = classNames({ + [styles.character]: true, + [styles.grid]: true, + }) + + // Hooks + useEffect(() => { - if (props.loading) { + if (loading) { setVisible(false) } else { const timeout = setTimeout(() => { @@ -84,7 +108,7 @@ const GridRep = (props: Props) => { }, 150) return () => clearTimeout(timeout) } - }, [props.loading]) + }, [loading]) useEffect(() => { setVisible(false) // Trigger fade out @@ -99,7 +123,7 @@ const GridRep = (props: Props) => { const gridWeapons = Array(numWeapons) let foundMainhand = false - for (const [key, value] of Object.entries(props.grid)) { + for (const [key, value] of Object.entries(party.weapons)) { if (value.position == -1) { setMainhand(value.object) foundMainhand = true @@ -114,18 +138,74 @@ const GridRep = (props: Props) => { } setWeapons(newWeapons) - setGrid(gridWeapons) - }, [props.grid]) + setWeaponGrid(gridWeapons) + }, [party]) - function navigate() { - props.onClick(props.shortcode) + useEffect(() => { + const newCharacters = Array(3) + const gridCharacters = Array(3) + + if (party.characters) { + for (const [key, value] of Object.entries(party.characters)) { + if (value.position != null) { + newCharacters[value.position] = value.object + gridCharacters[value.position] = value + } + } + + setCharacters(newCharacters) + setCharacterGrid(gridCharacters) + } + }, [party]) + + useEffect(() => { + const newSummons = Array(numSummons) + const gridSummons = Array(numSummons) + + if (party.summons) { + for (const [key, value] of Object.entries(party.summons)) { + if (value.main) { + setMainSummon(value) + } else if (value.friend) { + setFriendSummon(value) + } else if (!value.main && !value.friend && value.position != null) { + newSummons[value.position] = value.object + gridSummons[value.position] = value + } + } + + setSummons(newSummons) + setSummonGrid(gridSummons) + } + }, [party]) + + // Convert element to string + function numberToElement() { + switch (mainhand?.element || weaponGrid[0]?.element) { + case 1: + return 'wind' + case 2: + return 'fire' + case 3: + return 'water' + case 4: + return 'earth' + case 5: + return 'dark' + case 6: + return 'light' + default: + return '' + } } + // Methods: Image generation + function generateMainhandImage() { let url = '' if (mainhand) { - const weapon = Object.values(props.grid).find( + const weapon = Object.values(party.weapons).find( (w) => w && w.object.id === mainhand.id ) @@ -136,18 +216,18 @@ const GridRep = (props: Props) => { } } - return mainhand && props.grid[0] ? ( + return mainhand && party.weapons[0] ? ( {mainhand.name[locale]} ) : ( '' ) } - function generateGridImage(position: number) { + function generateWeaponGridImage(position: number) { let url = '' const weapon = weapons[position] - const gridWeapon = grid[position] + const gridWeapon = weaponGrid[position] if (weapon && gridWeapon) { if (weapon.element == 0 && gridWeapon.element) { @@ -164,19 +244,163 @@ const GridRep = (props: Props) => { ) } + function generateMCImage() { + let source = '' + + if (party.job) { + const slug = party.job.name.en.replaceAll(' ', '-').toLowerCase() + const gender = party.user?.gender == 1 ? 'b' : 'a' + source = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/job-portraits/${slug}_${gender}.png` + } + + return ( + party.job && + party.job.id !== '-1' && ( + {party.job + ) + ) + } + + function generateCharacterGridImage(position: number) { + let url = '' + + const gridCharacter = characterGrid[position] + const character = characters[position] + + if (character && gridCharacter) { + // Change the image based on the uncap level + let suffix = '01' + if (gridCharacter.transcendence_step > 0) suffix = '04' + else if (gridCharacter.uncap_level >= 5) suffix = '03' + else if (gridCharacter.uncap_level > 2) suffix = '02' + + if (gridCharacter.object.granblue_id === '3030182000') { + let element = 1 + if (mainhand && mainhand.element) { + element = mainhand.element + } + + suffix = `${suffix}_0${element}` + } + + const url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/character-main/${character.granblue_id}_${suffix}.jpg` + + return ( + characters[position] && ( + {characters[position]?.name[locale]} + ) + ) + } + } + + function generateMainSummonImage(position: 'main' | 'friend') { + let url = '' + + const upgradedSummons = [ + '2040094000', + '2040100000', + '2040080000', + '2040098000', + '2040090000', + '2040084000', + '2040003000', + '2040056000', + '2040020000', + '2040034000', + '2040028000', + '2040027000', + '2040046000', + '2040047000', + ] + + const summon = position === 'main' ? mainSummon : friendSummon + + if (summon) { + // Change the image based on the uncap level + let suffix = '' + if (summon.object.uncap.xlb && summon.uncap_level == 6) { + if (summon.transcendence_step >= 1 && summon.transcendence_step < 5) { + suffix = '_03' + } else if (summon.transcendence_step === 5) { + suffix = '_04' + } + } else if ( + upgradedSummons.indexOf(summon.object.granblue_id.toString()) != -1 && + summon.uncap_level == 5 + ) { + suffix = '_02' + } + + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-main/${summon.object.granblue_id}${suffix}.jpg` + } + + return summon && {summon.object.name[locale]} + } + + function generateSummonGridImage(position: number) { + let url = '' + + const gridSummon = summonGrid[position] + const summon = gridSummon?.object + + const upgradedSummons = [ + '2040094000', + '2040100000', + '2040080000', + '2040098000', + '2040090000', + '2040084000', + '2040003000', + '2040056000', + '2040020000', + '2040034000', + '2040028000', + '2040027000', + '2040046000', + '2040047000', + ] + + if (summon && gridSummon) { + // Change the image based on the uncap level + let suffix = '' + if (gridSummon.object.uncap.xlb && gridSummon.uncap_level == 6) { + if ( + gridSummon.transcendence_step >= 1 && + gridSummon.transcendence_step < 5 + ) { + suffix = '_03' + } else if (gridSummon.transcendence_step === 5) { + suffix = '_04' + } + } else if ( + upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 && + gridSummon.uncap_level == 5 + ) { + suffix = '_02' + } + + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}${suffix}.jpg` + } + return ( + summons[position] && ( + {summons[position]?.name[locale]} + ) + ) + } + function sendSaveData() { - if (props.onSave) props.onSave(props.id, props.favorited) + if (onSave) onSave(party.id, party.favorited) } const userImage = () => { - if (props.user && props.user.avatar) { + if (party.user && party.user.avatar) { return ( {props.user.avatar.picture} ) } else @@ -194,29 +418,103 @@ const GridRep = (props: Props) => { const attribution = () => ( {userImage()} - {props.user ? props.user.username : t('no_user')} + {party.user ? party.user.username : t('no_user')} ) - function fullAutoString() { - const fullAutoElement = ( - - {` · ${t('party.details.labels.full_auto')}`} - - ) + const renderWeaponGrid = ( +
+
{generateMainhandImage()}
- const autoGuardElement = ( - - - - ) +
    + {Array.from(Array(numWeapons)).map((x, i) => { + return ( +
  • + {generateWeaponGridImage(i)} +
  • + ) + })} +
+
+ ) - return ( -
- {fullAutoElement} - {props.autoGuard ? autoGuardElement : ''} + const renderCharacterGrid = ( +
+
{generateMCImage()}
+ {Array.from(Array(3)).map((x, i) => { + return ( +
  • + {generateCharacterGridImage(i)} +
  • + ) + })} +
    + ) + + const renderSummonGrid = ( +
    +
    {generateMainSummonImage('main')}
    +
      + {Array.from(Array(numSummons)).map((x, i) => { + return ( +
    • + {generateSummonGridImage(i)} +
    • + ) + })} +
    +
    + {generateMainSummonImage('friend')}
    - ) +
    + ) + + const favoriteButton = ( + +
    {attribution()}
    ) - return ( - - - {detailsWithUsername} -
    -
    {generateMainhandImage()}
    + function changeView(view: 'characters' | 'weapons' | 'summons') { + setCurrentView(view) + } -
      - {Array.from(Array(numWeapons)).map((x, i) => { - return ( -
    • - {generateGridImage(i)} -
    • - ) - })} -
    -
    -
    + return ( + changeView('weapons')} + > + {detailsWithUsername} +
    + {currentView === 'characters' + ? renderCharacterGrid + : currentView === 'summons' + ? renderSummonGrid + : renderWeaponGrid} +
    +
      +
    • changeView('characters')} + > +
      + Characters +
    • +
    • changeView('weapons')} + > +
      + Weapons +
    • +
    • changeView('summons')} + > +
      + Summons +
    • +
    ) } diff --git a/components/weapon/WeaponModal/index.tsx b/components/weapon/WeaponModal/index.tsx index 13d63335..18cf3f4b 100644 --- a/components/weapon/WeaponModal/index.tsx +++ b/components/weapon/WeaponModal/index.tsx @@ -444,7 +444,7 @@ const WeaponModal = ({ - + You will lose all changes to{' '} {{ objectName: gridWeapon.object.name[locale] }} if you continue. @@ -455,9 +455,9 @@ const WeaponModal = ({ } open={alertOpen} - primaryActionText="Close" + primaryActionText={t('alert.unsaved_changes.buttons.confirm')} primaryAction={close} - cancelActionText="Nevermind" + cancelActionText={t('alert.unsaved_changes.buttons.cancel')} cancelAction={() => setAlertOpen(false)} /> ) diff --git a/pages/[username].tsx b/pages/[username].tsx index 1b7a976c..697abbdf 100644 --- a/pages/[username].tsx +++ b/pages/[username].tsx @@ -255,16 +255,7 @@ const ProfileRoute: React.FC = ({ return parties.map((party, i) => { return ( = ({ return parties.map((party, i) => { return ( = ({ return parties.map((party, i) => { return ( + + diff --git a/public/icons/Public.svg b/public/icons/Public.svg new file mode 100644 index 00000000..14e7d155 --- /dev/null +++ b/public/icons/Public.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/Unlisted.svg b/public/icons/Unlisted.svg new file mode 100644 index 00000000..304f9b4a --- /dev/null +++ b/public/icons/Unlisted.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 0bc3c376..c480b5c4 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -12,11 +12,15 @@ }, "alert": { "confirm_logout": "Are you sure you want to log out?", + "incompatible_weapon": "You've selected a weapon that can't be added to the Additional Weapon slots.", "unsaved_changes": { - "party": "You will lose all changes to your party {objectName} if you continue.

    Are you sure you want to continue without saving?", - "object": "You will lose all changes to {objectName} if you continue.

    Are you sure you want to continue without saving?" - }, - "incompatible_weapon": "You've selected a weapon that can't be added to the Additional Weapon slots." + "party": "You will lose all changes to your party {{objectName}} if you continue.

    Are you sure you want to continue without saving?", + "object": "You will lose all changes to {{objectName}} if you continue.

    Are you sure you want to continue without saving?", + "buttons": { + "confirm": "Continue without saving", + "cancel": "Nevermind" + } + } }, "ax": { "no_skill": "No AX Skill", @@ -65,6 +69,7 @@ }, "dropdown": { "party": { + "visibility": "Change team visibility", "copy": "Copy link to team", "delete": "Delete team", "remix": "Remix team" @@ -397,6 +402,33 @@ "remove": "Remove summon" } }, + "team_visibility": { + "title": "Change team visibility", + "label": "Team visibility", + "description": "Change who can see this team and where it shows up on the site", + "alerts": { + "unsaved_changes": { + "message": "Are you sure you want to continue without changing your team's visibility?", + "buttons": { + "confirm": "Continue without saving", + "cancel": "Nevermind" + } + } + }, + "options": { + "public": "Public", + "unlisted": "Unlisted", + "private": "Private" + }, + "descriptions": { + "public": "Visible to everyone and appears on the Teams page", + "unlisted": "Only visible to people with the link and does not appear on the Teams page", + "private": "Only visible to you and does not appear on the Teams page" + }, + "buttons": { + "confirm": "Change visibility" + } + }, "weapon": { "title": "Modify Weapon", "buttons": { @@ -459,6 +491,18 @@ "with_count_one": "{{count}} turn", "with_count_other": "{{count}} turns" } + }, + "notices": { + "unlisted": "This party is unlisted. Only people with the link with can see it.", + "private": "This party is private. Only you can see it.", + "buttons": { + "copy_link": "Copy link", + "change_visibility": "Change visibility" + } + }, + "tooltips": { + "unlisted": "This party is unlisted", + "private": "This party is private" } }, "proficiencies": { diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index 21ddb6ef..c036d01a 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -12,9 +12,15 @@ }, "alert": { "confirm_logout": "ログアウトしますか", - "unsaved_changes_party": "「{objectName}」という編成の変更は保存していません。

    変更を保存せずに続けますか?", - "unsaved_changes_object": "「{objectName}」の変更は保存していません。

    変更を保存せずに続けますか?", - "incompatible_weapon": "Additional Weaponsに装備できない武器を入れました。" + "incompatible_weapon": "Additional Weaponsに装備できない武器を入れました。", + "unsaved_changes": { + "party": "「{{objectName}}」という編成の変更は保存していません。

    変更を保存せずに続けますか?", + "object": "「{{objectName}}」の変更は保存していません。

    変更を保存せずに続けますか?", + "buttons": { + "confirm": "変更せずに続ける", + "cancel": "キャンセル" + } + } }, "ax": { "no_skill": "EXスキルなし", @@ -63,6 +69,7 @@ }, "dropdown": { "party": { + "visibility": "編成のプライバシー設定を変更", "copy": "編成のリンクをコピー", "delete": "編成を削除", "remix": "編成をリミックス" @@ -395,6 +402,33 @@ "remove": "召喚石を削除する" } }, + "team_visibility": { + "title": "編成のプライバシー設定を変更", + "label": "プライバシー設定", + "description": "この編成は誰が共有できるかと編成一覧に表示されるかを変更できます", + "alerts": { + "unsaved_changes": { + "message": "編成のプライバシー設定を変更せずに続けますか?", + "buttons": { + "confirm": "変更せずに続ける", + "cancel": "キャンセル" + } + } + }, + "options": { + "public": "公開", + "unlisted": "限定公開", + "private": "非公開" + }, + "descriptions": { + "public": "誰でも自由に共有可能。編成一覧に表示します。", + "unlisted": "リンクが知っている人のみが共有可能。編成一覧に表示しません。", + "private": "自分自身だけが可視。編成一覧に表示しません。" + }, + "buttons": { + "confirm": "プライバシー設定を変更" + } + }, "weapon": { "title": "武器変更", "buttons": { @@ -453,10 +487,23 @@ "minutes": "分", "seconds": "秒" }, + "turns": { "with_count_one": "{{count}}ターン", "with_count_other": "{{count}}ターン" } + }, + "notices": { + "unlisted": "この編成は限定公開でリンクが知っている人のみが共有可能", + "private": "この編成は未公開で自分自身だけが可視", + "buttons": { + "copy_link": "リンクをコピー", + "change_visibility": "プライバシー設定を変更" + } + }, + "tooltips": { + "unlisted": "この編成は限定公開", + "private": "この編成は未公開" } }, "proficiencies": { diff --git a/styles/themes.scss b/styles/themes.scss index d4b18529..afeaa451 100644 --- a/styles/themes.scss +++ b/styles/themes.scss @@ -81,6 +81,11 @@ --notice-bg: #{$notice--bg--light}; --notice-text: #{$notice--text--light}; + --notice-button-bg: #{$notice--button--bg--light}; + --notice-button-bg-hover: #{$notice--button--bg--light--hover}; + --notice-button-text: #{$notice--button--text--light}; + --notice-button-text-hover: #{$notice--button--text--light--hover}; + // Light - Buttons --button-bg: #{$button--bg--light}; --button-bg-hover: #{$button--bg--light--hover}; @@ -112,6 +117,13 @@ --slider-thumb-shadow: #{$slider--thumb--shadow--light}; --slider-thumb-shadow-hover: #{$slider--thumb--shadow--light--hover}; + // Light - Radio Buttons + --radio-button-bg: #{$radio--bg--light}; + --radio-button-bg-hover: #{$radio--bg--light--hover}; + + --radio-active-bg: #{$radio--active--bg--light}; + --radio-active-bg-hover: #{$radio--active--bg--light--hover}; + // Light - About --link-item-bg: #{$link--item--bg--light}; --link-item-image-color: #{$link--item--bg--image--light}; @@ -173,6 +185,7 @@ --wind-bg: #{$wind--bg--light}; --wind-bg-hover: #{$wind--bg--hover--light}; + --wind-portrait-bg: #{$wind--portrait--bg--light}; --wind-text: #{$wind--text--light}; --wind-raid-text: #{$wind--text--raid--light}; --wind-text-hover: #{$wind--text--hover--light}; @@ -181,6 +194,7 @@ --fire-bg: #{$fire--bg--light}; --fire-bg-hover: #{$fire--bg--hover--light}; + --fire-portrait-bg: #{$fire--portrait--bg--light}; --fire-text: #{$fire--text--light}; --fire-raid-text: #{$fire--text--raid--light}; --fire-text-hover: #{$fire--text--hover--light}; @@ -189,6 +203,7 @@ --water-bg: #{$water--bg--light}; --water-bg-hover: #{$water--bg--hover--light}; + --water-portrait-bg: #{$water--portrait--bg--light}; --water-text: #{$water--text--light}; --water-raid-text: #{$water--text--raid--light}; --water-text-hover: #{$water--text--hover--light}; @@ -197,6 +212,7 @@ --earth-bg: #{$earth--bg--light}; --earth-bg-hover: #{$earth--bg--hover--light}; + --earth-portrait-bg: #{$earth--portrait--bg--light}; --earth-text: #{$earth--text--light}; --earth-raid-text: #{$earth--text--raid--light}; --earth-text-hover: #{$earth--text--hover--light}; @@ -205,6 +221,7 @@ --dark-bg: #{$dark--bg--light}; --dark-bg-hover: #{$dark--bg--hover--light}; + --dark-portrait-bg: #{$dark--portrait--bg--light}; --dark-text: #{$dark--text--light}; --dark-raid-text: #{$dark--text--raid--light}; --dark-text-hover: #{$dark--text--hover--light}; @@ -213,6 +230,7 @@ --light-bg: #{$light--bg--light}; --light-bg-hover: #{$light--bg--hover--light}; + --light-portrait-bg: #{$light--portrait--bg--light}; --light-text: #{$light--text--light}; --light-raid-text: #{$light--text--raid--light}; --light-text-hover: #{$light--text--hover--light}; @@ -307,6 +325,11 @@ --notice-bg: #{$notice--bg--dark}; --notice-text: #{$notice--text--dark}; + --notice-button-bg: #{$notice--button--bg--dark}; + --notice-button-bg-hover: #{$notice--button--bg--dark--hover}; + --notice-button-text: #{$notice--button--text--dark}; + --notice-button-text-hover: #{$notice--button--text--dark--hover}; + // Dark - Buttons --button-bg: #{$button--bg--dark}; --button-bg-hover: #{$button--bg--dark--hover}; @@ -338,6 +361,13 @@ --slider-thumb-shadow: #{$slider--thumb--shadow--dark}; --slider-thumb-shadow-hover: #{$slider--thumb--shadow--dark--hover}; + // Dark - Radio Buttons + --radio-button-bg: #{$radio--bg--dark}; + --radio-button-bg-hover: #{$radio--bg--dark--hover}; + + --radio-active-bg: #{$radio--active--bg--dark}; + --radio-active-bg-hover: #{$radio--active--bg--dark--hover}; + // Dark - About --link-item-bg: #{$link--item--bg--dark}; --link-item-image-color: #{$link--item--bg--image--dark}; @@ -399,6 +429,7 @@ --wind-bg: #{$wind--bg--dark}; --wind-bg-hover: #{$wind--bg--hover--dark}; + --wind-portrait-bg: #{$wind--portrait--bg--dark}; --wind-text: #{$wind--text--dark}; --wind-raid-text: #{$wind--text--raid--dark}; --wind-text-hover: #{$wind--text--hover--dark}; @@ -407,6 +438,7 @@ --fire-bg: #{$fire--bg--dark}; --fire-bg-hover: #{$fire--bg--hover--dark}; + --fire-portrait-bg: #{$fire--portrait--bg--dark}; --fire-text: #{$fire--text--dark}; --fire-raid-text: #{$fire--text--raid--dark}; --fire-text-hover: #{$fire--text--hover--dark}; @@ -415,6 +447,7 @@ --water-bg: #{$water--bg--dark}; --water-bg-hover: #{$water--bg--hover--dark}; + --water-portrait-bg: #{$water--portrait--bg--dark}; --water-text: #{$water--text--dark}; --water-raid-text: #{$water--text--raid--dark}; --water-text-hover: #{$water--text--hover--dark}; @@ -423,6 +456,7 @@ --earth-bg: #{$earth--bg--dark}; --earth-bg-hover: #{$earth--bg--hover--dark}; + --earth-portrait-bg: #{$earth--portrait--bg--dark}; --earth-text: #{$earth--text--dark}; --earth-raid-text: #{$earth--text--raid--dark}; --earth-text-hover: #{$earth--text--hover--dark}; @@ -431,6 +465,7 @@ --dark-bg: #{$dark--bg--dark}; --dark-bg-hover: #{$dark--bg--hover--dark}; + --dark-portrait-bg: #{$dark--portrait--bg--dark}; --dark-text: #{$dark--text--dark}; --dark-raid-text: #{$dark--text--raid--dark}; --dark-text-hover: #{$dark--text--hover--dark}; @@ -439,6 +474,7 @@ --light-bg: #{$light--bg--dark}; --light-bg-hover: #{$light--bg--hover--dark}; + --light-portrait-bg: #{$light--portrait--bg--dark}; --light-text: #{$light--text--dark}; --light-raid-text: #{$light--text--raid--dark}; --light-text-hover: #{$light--text--hover--dark}; diff --git a/styles/variables.scss b/styles/variables.scss index dc8e6336..80b7e2b7 100644 --- a/styles/variables.scss +++ b/styles/variables.scss @@ -107,10 +107,13 @@ $yellow-text-20: #ffed4c; $highlight-yellow: #ffed4c55; $accent--yellow--00: #463805; -$accent--yellow--20: #7f6a00; +$accent--yellow--10: #6c5a01; +$accent--yellow--20: #776300; $accent--yellow--40: #a39200; $accent--yellow--60: #c89d39; +$accent--yellow--70: #d1aa4f; $accent--yellow--80: #deb351; +$accent--yellow--90: #e6bd5e; $accent--yellow--100: #f9cc64; $selected--item--bg--dark: #f9cc645d; @@ -222,6 +225,18 @@ $notice--bg--dark: $accent--yellow--00; $notice--text--light: $accent--yellow--20; $notice--text--dark: $accent--yellow--100; +$notice--button--bg--light: $accent--yellow--80; +$notice--button--bg--dark: $accent--yellow--20; + +$notice--button--bg--light--hover: $accent--yellow--70; +$notice--button--bg--dark--hover: $accent--yellow--10; + +$notice--button--text--light: $accent--yellow--10; +$notice--button--text--dark: $accent--yellow--90; + +$notice--button--text--light--hover: $accent--yellow--00; +$notice--button--text--dark--hover: $accent--yellow--100; + // Color Definitions: Button $button--bg--light: $grey-80; $button--bg--light--hover: $grey-100; @@ -440,6 +455,19 @@ $icon--secondary--color--dark: $grey-50; $icon--secondary--hover--light: $grey-50; $icon--secondary--hover--dark: $grey-70; +// Color Definitions: Radio Buttons +$radio--bg--light: $grey-75; +$radio--bg--dark: $grey-10; + +$radio--bg--light--hover: $grey-70; +$radio--bg--dark--hover: $grey-00; + +$radio--active--bg--light: $accent--blue--light; +$radio--active--bg--dark: $accent--blue--dark; + +$radio--active--bg--light--hover: $accent--blue--light--focus; +$radio--active--bg--dark--hover: $accent--blue--dark--focus; + // Color Definitions: Tag $tag--bg--light: $grey-60; $tag--bg--dark: $grey-00; @@ -450,6 +478,9 @@ $tag--text--dark: $grey-50; $wind--bg--light: $wind-bg-10; $wind--bg--dark: $wind-bg-10; +$wind--portrait--bg--light: $wind-bg-20; +$wind--portrait--bg--dark: $wind-bg-20; + $wind--bg--hover--light: $wind-bg-00; $wind--bg--hover--dark: $wind-bg-00; @@ -494,6 +525,9 @@ $null--shadow--dark--hover: fade-out($grey-10, 0.3); $fire--bg--light: $fire-bg-10; $fire--bg--dark: $fire-bg-10; +$fire--portrait--bg--light: $fire-bg-20; +$fire--portrait--bg--dark: $fire-bg-20; + $fire--bg--hover--light: $fire-bg-00; $fire--bg--hover--dark: $fire-bg-00; @@ -516,6 +550,9 @@ $fire--shadow--dark--hover: fade-out($fire-text-00, 0.3); $water--bg--light: $water-bg-10; $water--bg--dark: $water-bg-10; +$water--portrait--bg--light: $water-bg-20; +$water--portrait--bg--dark: $water-bg-20; + $water--bg--hover--light: $water-bg-00; $water--bg--hover--dark: $water-bg-00; @@ -538,6 +575,9 @@ $water--shadow--dark--hover: fade-out($water-text-00, 0.3); $earth--bg--light: $earth-bg-10; $earth--bg--dark: $earth-bg-10; +$earth--portrait--bg--light: $earth-bg-20; +$earth--portrait--bg--dark: $earth-bg-20; + $earth--bg--hover--light: $earth-bg-00; $earth--bg--hover--dark: $earth-bg-00; @@ -560,6 +600,9 @@ $earth--shadow--dark--hover: fade-out($earth-text-00, 0.3); $dark--bg--light: $dark-bg-10; $dark--bg--dark: $dark-bg-10; +$dark--portrait--bg--light: $dark-bg-20; +$dark--portrait--bg--dark: $dark-bg-20; + $dark--bg--hover--light: $dark-bg-00; $dark--bg--hover--dark: $dark-bg-00; @@ -582,6 +625,9 @@ $dark--shadow--dark--hover: fade-out($dark-text-00, 0.3); $light--bg--light: $light-bg-10; $light--bg--dark: $light-bg-10; +$light--portrait--bg--light: $light-bg-20; +$light--portrait--bg--dark: $light-bg-20; + $light--bg--hover--light: $light-bg-00; $light--bg--hover--dark: $light-bg-00; diff --git a/types/Party.d.ts b/types/Party.d.ts index e2b4d23c..74d35263 100644 --- a/types/Party.d.ts +++ b/types/Party.d.ts @@ -43,6 +43,7 @@ interface Party { local_id?: string remix: boolean remixes: Party[] + visibility: number created_at: string updated_at: string } diff --git a/types/index.d.ts b/types/index.d.ts index f00975cd..add8e5c5 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -41,6 +41,7 @@ export type DetailsObject = { job?: Job extra?: boolean guidebooks?: string[] + visibility?: number } export type ExtendedMastery = { diff --git a/utils/appState.tsx b/utils/appState.tsx index 73657e9c..c1706080 100644 --- a/utils/appState.tsx +++ b/utils/appState.tsx @@ -61,6 +61,7 @@ interface AppState { favorited: boolean remix: boolean remixes: Party[] + visibility: number sourceParty?: Party created_at: string updated_at: string @@ -128,6 +129,7 @@ export const initialAppState: AppState = { favorited: false, remix: false, remixes: [], + visibility: 1, sourceParty: undefined, created_at: '', updated_at: '',