Merge branch 'staging' of github.com:jedmund/hensei-web into staging

This commit is contained in:
Justin Edmund 2023-06-21 03:44:23 -07:00
commit 9de87abd1e
26 changed files with 384 additions and 369 deletions

1
.gitignore vendored
View file

@ -58,6 +58,7 @@ public/images/mastery*
public/images/updates*
public/images/guidebooks*
public/images/raids*
public/images/gacha*
# Typescript v1 declaration files
typings/

View file

@ -47,32 +47,32 @@
&.fire {
background: var(--fire-bg);
color: var(--fire-text);
color: var(--fire-hover-text);
}
&.water {
background: var(--water-bg);
color: var(--water-text);
color: var(--water-hover-text);
}
&.earth {
background: var(--earth-bg);
color: var(--earth-text);
color: var(--earth-hover-text);
}
&.wind {
background: var(--wind-bg);
color: var(--wind-text);
color: var(--wind-hover-text);
}
&.dark {
background: var(--dark-bg);
color: var(--dark-text);
color: var(--dark-hover-text);
}
&.light {
background: var(--light-bg);
color: var(--light-text);
color: var(--light-hover-text);
}
}
}

View file

@ -102,6 +102,23 @@
}
}
.Filter.Button {
justify-content: center;
.Text {
display: none;
width: auto;
@include breakpoint(tablet) {
display: block;
}
@include breakpoint(phone) {
display: block;
}
}
}
.UserInfo {
align-items: center;
display: flex;

View file

@ -181,6 +181,7 @@ const FilterBar = (props: Props) => {
className={filterButtonClasses}
blended={true}
leftAccessoryIcon={<FilterIcon />}
text={t('filters.name')}
onClick={() => setFilterModalOpen(true)}
/>
</div>

View file

@ -7,7 +7,6 @@ import classNames from 'classnames'
import clonedeep from 'lodash.clonedeep'
import Link from 'next/link'
import api from '~utils/api'
import { accountState, initialAccountState } from '~utils/accountState'
import { appState, initialAppState } from '~utils/appState'
import { getLocalId } from '~utils/localId'
@ -32,11 +31,8 @@ import Tooltip from '~components/common/Tooltip'
import * as Switch from '@radix-ui/react-switch'
import ChevronIcon from '~public/icons/Chevron.svg'
import LinkIcon from '~public/icons/Link.svg'
import MenuIcon from '~public/icons/Menu.svg'
import RemixIcon from '~public/icons/Remix.svg'
import PlusIcon from '~public/icons/Add.svg'
import SaveIcon from '~public/icons/Save.svg'
import './index.scss'
@ -51,7 +47,6 @@ const Header = () => {
const localeData = retrieveLocaleCookies()
// State management
const [copyToastOpen, setCopyToastOpen] = useState(false)
const [remixToastOpen, setRemixToastOpen] = useState(false)
const [loginModalOpen, setLoginModalOpen] = useState(false)
const [signupModalOpen, setSignupModalOpen] = useState(false)
@ -64,7 +59,6 @@ const Header = () => {
const [originalName, setOriginalName] = useState('')
// Snapshots
const { account } = useSnapshot(accountState)
const { party: partySnapshot } = useSnapshot(appState)
// Subscribe to app state to listen for party name and
@ -108,15 +102,6 @@ const Header = () => {
setRightMenuOpen(false)
}
// Methods: Event handlers (Copy toast)
function handleCopyToastOpenChanged(open: boolean) {
setCopyToastOpen(open)
}
function handleCopyToastCloseClicked() {
setCopyToastOpen(false)
}
// Methods: Event handlers (Remix toasts)
function handleRemixToastOpenChanged(open: boolean) {
setRemixToastOpen(open)
@ -142,23 +127,6 @@ const Header = () => {
router.push(router.asPath, undefined, { locale: language })
}
function copyToClipboard() {
const path = router.asPath.split('/')[1]
if (path === 'p') {
const el = document.createElement('input')
el.value = window.location.href
el.id = 'url-input'
document.body.appendChild(el)
el.select()
document.execCommand('copy')
el.remove()
setCopyToastOpen(true)
}
}
function logout() {
// Close menu
closeRightMenu()
@ -188,84 +156,6 @@ const Header = () => {
router.push('/new')
}
function remixTeam() {
setOriginalName(partySnapshot.name ? partySnapshot.name : t('no_title'))
if (partySnapshot.shortcode) {
const body = getLocalId()
api
.remix({ shortcode: partySnapshot.shortcode, body: body })
.then((response) => {
const remix = response.data.party
// Store the edit key in local storage
if (remix.edit_key) {
storeEditKey(remix.id, remix.edit_key)
setEditKey(remix.id, remix.user)
}
router.push(`/p/${remix.shortcode}`)
setRemixToastOpen(true)
})
}
}
function toggleFavorite() {
if (partySnapshot.favorited) unsaveFavorite()
else saveFavorite()
}
function saveFavorite() {
if (partySnapshot.id)
api.saveTeam({ id: partySnapshot.id }).then((response) => {
if (response.status == 201) appState.party.favorited = true
})
else console.error('Failed to save team: No party ID')
}
function unsaveFavorite() {
if (partySnapshot.id)
api.unsaveTeam({ id: partySnapshot.id }).then((response) => {
if (response.status == 200) appState.party.favorited = false
})
else console.error('Failed to unsave team: No party ID')
}
// Rendering: Elements
const pageTitle = () => {
let title = ''
let hasAccessory = false
const path = router.asPath.split('/')[1]
if (path === 'p') {
hasAccessory = true
if (appState.party && appState.party.name) {
title = appState.party.name
} else {
title = t('no_title')
}
} else {
title = ''
}
return title !== '' ? (
<Tooltip content={t('tooltips.copy_url')}>
<Button
blended={true}
rightAccessoryIcon={
path === 'p' && hasAccessory ? (
<LinkIcon className="stroke" />
) : undefined
}
text={title}
onClick={copyToClipboard}
/>
</Tooltip>
) : (
''
)
}
const profileImage = () => {
let image
@ -310,21 +200,6 @@ const Header = () => {
)
}
// Rendering: Toasts
const urlCopyToast = () => {
return (
<Toast
altText={t('toasts.copied')}
open={copyToastOpen}
duration={2400}
type="foreground"
content={t('toasts.copied')}
onOpenChange={handleCopyToastOpenChanged}
onCloseClick={handleCopyToastCloseClicked}
/>
)
}
const remixToast = () => {
return (
<Toast
@ -394,7 +269,6 @@ const Header = () => {
</DropdownMenuContent>
</DropdownMenu>
</div>
{!appState.errorCode ? pageTitle() : ''}
</section>
)
}
@ -564,8 +438,6 @@ const Header = () => {
<nav id="Header">
{left()}
{right()}
{urlCopyToast()}
{remixToast()}
{settingsModal()}
{loginModal()}
{signupModal()}

View file

@ -22,9 +22,14 @@
max-width: 30vw;
padding: $unit * 4;
@include breakpoint(tablet) {
max-width: inherit;
max-width: 60vw;
}
@include breakpoint(phone) {
max-width: inherit;
width: 60vw;
width: 70vw;
}
.description {
@ -41,5 +46,15 @@
display: flex;
align-self: flex-end;
gap: $unit;
@include breakpoint(phone) {
flex-direction: column-reverse;
align-self: center;
width: 100%;
.Button {
width: 100%;
}
}
}
}

View file

@ -0,0 +1,3 @@
.Joined .Input::placeholder {
color: var(--text-tertiary);
}

View file

@ -56,6 +56,7 @@ const CharLimitedFieldset: ForwardRefRenderFunction<HTMLInputElement, Props> = (
<div className={classNames({ Joined: true }, props.className)}>
<input
{...props}
data-1p-ignore
autoComplete="off"
className="Input"
type={props.type}

View file

@ -50,6 +50,6 @@
.Input::placeholder {
/* Chrome, Firefox, Opera, Safari 10.1+ */
color: var(--text-secondary) !important;
color: var(--text-secondary);
opacity: 1; /* Firefox */
}

View file

@ -8,7 +8,6 @@
padding: $unit;
transform-origin: var(--radix-popover-content-transform-origin);
width: var(--radix-popover-trigger-width);
min-width: 440px;
z-index: 5;
@include breakpoint(phone) {

View file

@ -19,7 +19,8 @@
color: var(--full-auto-text);
}
&.AutoGuard.On {
&.AutoGuard.On,
&.AutoSummon.On {
background: var(--auto-guard-bg);
color: var(--auto-guard-text);
}

View file

@ -46,7 +46,7 @@ const RemixTeamAlert = ({
<Trans i18nKey="modals.remix_team.description.viewer">
Remixing a team makes a copy of it in your account so you can make
your own changes.\n\nWould you like to remix{' '}
<strong>{{ name: 'HEY' }}</strong>?
<strong>{{ name: name }}</strong>?
</Trans>
)
}

View file

@ -1,5 +1,6 @@
import React, { useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio'
import { useTranslation } from 'react-i18next'
import {
@ -22,6 +23,8 @@ import TableField from '~components/common/TableField'
import type { DetailsObject } from 'types'
import type { DialogProps } from '@radix-ui/react-dialog'
import { appState } from '~utils/appState'
import CheckIcon from '~public/icons/Check.svg'
import CrossIcon from '~public/icons/Cross.svg'
import './index.scss'
@ -31,14 +34,16 @@ interface Props extends DialogProps {
updateCallback: (details: DetailsObject) => void
}
const EditPartyModal = ({ party, updateCallback, ...props }: Props) => {
const EditPartyModal = ({ updateCallback, ...props }: Props) => {
// Set up router
const router = useRouter()
const locale = router.locale
// Set up translation
const { t } = useTranslation('common')
// Set up reactive state
const { party } = useSnapshot(appState)
// Refs
const headerRef = React.createRef<HTMLDivElement>()
const footerRef = React.createRef<HTMLDivElement>()
@ -54,6 +59,7 @@ const EditPartyModal = ({ party, updateCallback, ...props }: Props) => {
// States: Data
const [name, setName] = useState('')
const [description, setDescription] = useState('')
const [raid, setRaid] = useState<Raid>()
const [extra, setExtra] = useState(false)
const [chargeAttack, setChargeAttack] = useState(true)
@ -68,24 +74,15 @@ const EditPartyModal = ({ party, updateCallback, ...props }: Props) => {
// Hooks
useEffect(() => {
if (!party) return
setName(party.name)
setRaid(party.raid)
setAutoGuard(party.auto_guard)
setAutoSummon(party.auto_summon)
setFullAuto(party.full_auto)
setChargeAttack(party.charge_attack)
setClearTime(party.clear_time)
if (party.turn_count) setTurnCount(party.turn_count)
if (party.button_count) setButtonCount(party.button_count)
if (party.chain_count) setChainCount(party.chain_count)
persistFromState()
}, [party])
// Methods: Event handlers (Dialog)
function openChange() {
if (open) {
setOpen(false)
setCurrentSegment(0)
persistFromState()
if (props.onOpenChange) props.onOpenChange(false)
} else {
setOpen(true)
@ -176,6 +173,21 @@ const EditPartyModal = ({ party, updateCallback, ...props }: Props) => {
}
// Methods: Data methods
function persistFromState() {
if (!party) return
setName(party.name ? party.name : '')
setDescription(party.description ? party.description : '')
setRaid(party.raid)
setAutoGuard(party.autoGuard)
setAutoSummon(party.autoSummon)
setFullAuto(party.fullAuto)
setChargeAttack(party.chargeAttack)
setClearTime(party.clearTime)
if (party.turnCount) setTurnCount(party.turnCount)
if (party.buttonCount) setButtonCount(party.buttonCount)
if (party.chainCount) setChainCount(party.chainCount)
}
function updateDetails(event: React.MouseEvent) {
const descriptionValue = descriptionInput.current?.value
const details: DetailsObject = {
@ -272,9 +284,8 @@ const EditPartyModal = ({ party, updateCallback, ...props }: Props) => {
}
onChange={handleTextAreaChanged}
ref={descriptionInput}
>
{party ? party.description : ''}
</textarea>
defaultValue={description}
/>
</div>
)
}

View file

@ -7,7 +7,7 @@ import clonedeep from 'lodash.clonedeep'
import Alert from '~components/common/Alert'
import PartySegmentedControl from '~components/party/PartySegmentedControl'
import PartyDetails from '~components/party/PartyDetails'
import PartyFooter from '~components/party/PartyFooter'
import PartyHeader from '~components/party/PartyHeader'
import WeaponGrid from '~components/weapon/WeaponGrid'
import SummonGrid from '~components/summon/SummonGrid'
@ -145,37 +145,27 @@ const Party = (props: Props) => {
function formatDetailsObject(details: DetailsObject) {
const payload: { [key: string]: any } = {}
const mappings: { [key: string]: string } = {
name: 'name',
description: 'description',
chargeAttack: 'charge_attack',
fullAuto: 'full_auto',
autoGuard: 'auto_guard',
autoSummon: 'auto_summon',
clearTime: 'clear_time',
buttonCount: 'button_count',
chainCount: 'chain_count',
turnCount: 'turn_count',
extra: 'extra',
job: 'job_id',
guidebook1_id: 'guidebook1_id',
guidebook2_id: 'guidebook2_id',
guidebook3_id: 'guidebook3_id',
}
Object.entries(mappings).forEach(([key, value]) => {
if (details[key]) {
payload[value] = details[key]
}
})
if (details.name) payload.name = details.name
if (details.description) payload.description = details.description
if (details.raid) payload.raid_id = details.raid.id
if (details.chargeAttack != undefined)
payload.charge_attack = details.chargeAttack
if (details.fullAuto != undefined) payload.full_auto = details.fullAuto
if (details.autoGuard != undefined) payload.auto_guard = details.autoGuard
if (details.autoSummon != undefined)
payload.auto_summon = details.autoSummon
if (details.clearTime) payload.clear_time = details.clearTime
if (details.buttonCount) payload.button_count = details.buttonCount
if (details.chainCount) payload.chain_count = details.chainCount
if (details.turnCount) payload.turn_count = details.turnCount
if (details.extra != undefined) payload.extra = details.extra
if (details.job) payload.job_id = details.job.id
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 (Object.keys(payload).length >= 1) {
return { party: payload }
} else {
return {}
}
if (Object.keys(payload).length >= 1) return { party: payload }
else return {}
}
function cancelAlert() {
@ -275,6 +265,15 @@ const Party = (props: Props) => {
appState.party.jobSkills = team.job_skills
appState.party.accessory = team.accessory
appState.party.chargeAttack = team.charge_attack
appState.party.fullAuto = team.full_auto
appState.party.autoGuard = team.auto_guard
appState.party.autoSummon = team.auto_summon
appState.party.clearTime = team.clear_time
appState.party.buttonCount = team.button_count
appState.party.chainCount = team.chain_count
appState.party.turnCount = team.turn_count
appState.party.id = team.id
appState.party.shortcode = team.shortcode
appState.party.extra = team.extra
@ -455,7 +454,7 @@ const Party = (props: Props) => {
<section id="Party">{currentGrid()}</section>
<PartyDetails
<PartyFooter
party={props.team}
new={props.new || false}
editable={party.editable}

View file

@ -2,8 +2,7 @@
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { subscribe, useSnapshot } from 'valtio'
import { Trans, useTranslation } from 'next-i18next'
import Link from 'next/link'
import { useTranslation } from 'next-i18next'
import classNames from 'classnames'
// Dependencies: Common
@ -125,22 +124,27 @@ const PartyDropdown = ({
// Toasts / Copy URL
function handleCopyToastOpenChanged(open: boolean) {
setCopyToastOpen(open)
setCopyToastOpen(!open)
}
function handleCopyToastCloseClicked() {
setCopyToastOpen(false)
}
// Toasts / Remix team
// Toasts: Remix team
function handleRemixToastOpenChanged(open: boolean) {
setRemixToastOpen(open)
setRemixToastOpen(!open)
}
function handleRemixToastCloseClicked() {
setRemixToastOpen(false)
}
function remixCallback() {
setRemixToastOpen(true)
remixTeamCallback()
}
const editableItems = () => {
return (
<>
@ -185,10 +189,23 @@ const PartyDropdown = ({
<RemixTeamAlert
creator={editable}
name={partySnapshot.name ? partySnapshot.name : t('no_title')}
name={partySnapshot.name || t('no_title')}
open={remixAlertOpen}
onOpenChange={handleRemixTeamAlertChange}
remixCallback={remixTeamCallback}
remixCallback={remixCallback}
/>
<RemixedToast
open={remixToastOpen}
partyName={partySnapshot.name || t('no_title')}
onOpenChange={handleRemixToastOpenChanged}
onCloseClick={handleRemixToastCloseClicked}
/>
<UrlCopiedToast
open={copyToastOpen}
onOpenChange={handleCopyToastOpenChanged}
onCloseClick={handleCopyToastCloseClicked}
/>
</>
)

View file

@ -1,4 +1,4 @@
.DetailsWrapper {
.FooterWrapper {
display: flex;
flex-direction: column;
gap: $unit-2x;
@ -16,10 +16,13 @@
}
}
.PartyDetails {
.PartyFooter {
box-sizing: border-box;
display: none;
line-height: 1.4;
white-space: pre-wrap;
margin: 0 auto $unit-2x;
margin-bottom: $unit-12x;
min-height: 10vh;
max-width: $unit * 94;
overflow: hidden;
width: 100%;
@ -27,11 +30,6 @@
@include breakpoint(phone) {
padding: 0 $unit;
}
&.Visible {
// margin-bottom: $unit-12x;
}
&.Editable {
gap: $unit;
@ -174,11 +172,6 @@
}
}
&.ReadOnly {
box-sizing: border-box;
line-height: 1.4;
white-space: pre-wrap;
&.Visible {
display: block;
}
@ -285,7 +278,6 @@
}
}
}
}
.PartyInfo {
box-sizing: border-box;

View file

@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio'
import { useTranslation } from 'next-i18next'
import clonedeep from 'lodash.clonedeep'
@ -27,10 +28,12 @@ interface Props {
updateCallback: (details: DetailsObject) => void
}
const PartyDetails = (props: Props) => {
const PartyFooter = (props: Props) => {
const { t } = useTranslation('common')
const router = useRouter()
const { party } = useSnapshot(appState)
const youtubeUrlRegex =
/(?:https:\/\/www\.youtube\.com\/watch\?v=|https:\/\/youtu\.be\/)([\w-]+)/g
@ -40,16 +43,10 @@ const PartyDetails = (props: Props) => {
const [embeddedDescription, setEmbeddedDescription] =
useState<React.ReactNode>()
const readOnlyClasses = classNames({
PartyDetails: true,
ReadOnly: true,
Visible: !open,
})
useEffect(() => {
// Extract the video IDs from the description
if (appState.party.description) {
const videoIds = extractYoutubeVideoIds(appState.party.description)
if (party.description) {
const videoIds = extractYoutubeVideoIds(party.description)
// Fetch the video titles for each ID
const fetchPromises = videoIds.map(({ id }) => fetchYoutubeData(id))
@ -58,7 +55,7 @@ const PartyDetails = (props: Props) => {
Promise.all(fetchPromises).then((videoTitles) => {
// Replace the video URLs in the description with LiteYoutubeEmbed elements
const newDescription = reactStringReplace(
appState.party.description,
party.description,
youtubeUrlRegex,
(match, i) => (
<LiteYouTubeEmbed
@ -77,7 +74,7 @@ const PartyDetails = (props: Props) => {
} else {
setEmbeddedDescription('')
}
}, [appState.party.description])
}, [party.description])
async function fetchYoutubeData(videoId: string) {
return await youtube
@ -173,14 +170,6 @@ const PartyDetails = (props: Props) => {
})
}
const readOnly = () => {
return (
<section className={readOnlyClasses}>
<Linkify>{embeddedDescription}</Linkify>
</section>
)
}
const remixSection = () => {
return (
<section className="Remixes">
@ -192,10 +181,14 @@ const PartyDetails = (props: Props) => {
return (
<>
<section className="DetailsWrapper">{readOnly()}</section>
<section className="FooterWrapper">
<section className="PartyFooter">
<Linkify>{embeddedDescription}</Linkify>
</section>
</section>
{remixes && remixes.length > 0 ? remixSection() : ''}
</>
)
}
export default PartyDetails
export default PartyFooter

View file

@ -12,6 +12,7 @@ import Token from '~components/common/Token'
import EditPartyModal from '~components/party/EditPartyModal'
import PartyDropdown from '~components/party/PartyDropdown'
import api from '~utils/api'
import { accountState } from '~utils/accountState'
import { appState, initialAppState } from '~utils/appState'
import { formatTimeAgo } from '~utils/timeAgo'
@ -23,7 +24,9 @@ import SaveIcon from '~public/icons/Save.svg'
import type { DetailsObject } from 'types'
import './index.scss'
import api from '~utils/api'
import RemixTeamAlert from '~components/dialogs/RemixTeamAlert'
import RemixedToast from '~components/toasts/RemixedToast'
import { set } from 'local-storage'
// Props
interface Props {
@ -44,12 +47,16 @@ const PartyHeader = (props: Props) => {
const { party: partySnapshot } = useSnapshot(appState)
const [name, setName] = useState('')
// State: Component
const [remixAlertOpen, setRemixAlertOpen] = useState(false)
const [remixToastOpen, setRemixToastOpen] = useState(false)
// State: Data
const [name, setName] = useState('')
const [chargeAttack, setChargeAttack] = useState(true)
const [fullAuto, setFullAuto] = useState(false)
const [autoGuard, setAutoGuard] = useState(false)
const [autoSummon, setAutoSummon] = useState(false)
const [buttonCount, setButtonCount] = useState<number | undefined>(undefined)
const [chainCount, setChainCount] = useState<number | undefined>(undefined)
const [turnCount, setTurnCount] = useState<number | undefined>(undefined)
@ -78,6 +85,7 @@ const PartyHeader = (props: Props) => {
setName(props.party.name)
setAutoGuard(props.party.auto_guard)
setFullAuto(props.party.full_auto)
setAutoSummon(props.party.auto_summon)
setChargeAttack(props.party.charge_attack)
setClearTime(props.party.clear_time)
if (props.party.turn_count) setTurnCount(props.party.turn_count)
@ -155,6 +163,32 @@ const PartyHeader = (props: Props) => {
)
}
// Actions: Remix team
function remixTeamCallback() {
setRemixToastOpen(true)
props.remixCallback()
}
// Alerts: Remix team
function openRemixTeamAlert() {
setRemixAlertOpen(true)
}
function handleRemixTeamAlertChange(open: boolean) {
setRemixAlertOpen(open)
}
// Toasts: Remix team
function handleRemixToastOpenChanged(open: boolean) {
setRemixToastOpen(!open)
}
function handleRemixToastCloseClicked() {
setRemixToastOpen(false)
}
// Rendering
const userBlock = (username?: string, picture?: string, element?: string) => {
return (
<div className={userClass}>
@ -212,12 +246,12 @@ const PartyHeader = (props: Props) => {
<Token
className={classNames({
ChargeAttack: true,
On: chargeAttack,
Off: !chargeAttack,
On: party.chargeAttack,
Off: !party.chargeAttack,
})}
>
{`${t('party.details.labels.charge_attack')} ${
chargeAttack ? 'On' : 'Off'
party.chargeAttack ? 'On' : 'Off'
}`}
</Token>
)
@ -226,11 +260,13 @@ const PartyHeader = (props: Props) => {
<Token
className={classNames({
FullAuto: true,
On: fullAuto,
Off: !fullAuto,
On: party.fullAuto,
Off: !party.fullAuto,
})}
>
{`${t('party.details.labels.full_auto')} ${fullAuto ? 'On' : 'Off'}`}
{`${t('party.details.labels.full_auto')} ${
party.fullAuto ? 'On' : 'Off'
}`}
</Token>
)
@ -238,37 +274,57 @@ const PartyHeader = (props: Props) => {
<Token
className={classNames({
AutoGuard: true,
On: autoGuard,
Off: !autoGuard,
On: party.autoGuard,
Off: !party.autoGuard,
})}
>
{`${t('party.details.labels.auto_guard')} ${autoGuard ? 'On' : 'Off'}`}
{`${t('party.details.labels.auto_guard')} ${
party.autoGuard ? 'On' : 'Off'
}`}
</Token>
)
const autoSummonToken = (
<Token
className={classNames({
AutoSummon: true,
On: party.autoSummon,
Off: !party.autoSummon,
})}
>
{`${t('party.details.labels.auto_summon')} ${
party.autoSummon ? 'On' : 'Off'
}`}
</Token>
)
const turnCountToken = (
<Token>
{t('party.details.turns.with_count', {
count: turnCount,
count: party.turnCount,
})}
</Token>
)
const buttonChainToken = () => {
if (buttonCount || chainCount) {
if (party.buttonCount || party.chainCount) {
let string = ''
if (buttonCount && buttonCount > 0) {
string += `${buttonCount}b`
if (party.buttonCount && party.buttonCount > 0) {
string += `${party.buttonCount}b`
}
if (!buttonCount && chainCount && chainCount > 0) {
string += `0${t('party.details.suffix.buttons')}${chainCount}${t(
if (!party.buttonCount && party.chainCount && party.chainCount > 0) {
string += `0${t('party.details.suffix.buttons')}${party.chainCount}${t(
'party.details.suffix.chains'
)}`
} else if (buttonCount && chainCount && chainCount > 0) {
string += `${chainCount}${t('party.details.suffix.chains')}`
} else if (buttonCount && !chainCount) {
} else if (
party.buttonCount &&
party.chainCount &&
party.chainCount > 0
) {
string += `${party.chainCount}${t('party.details.suffix.chains')}`
} else if (party.buttonCount && !party.chainCount) {
string += `0${t('party.details.suffix.chains')}`
}
@ -277,8 +333,8 @@ const PartyHeader = (props: Props) => {
}
const clearTimeToken = () => {
const minutes = Math.floor(clearTime / 60)
const seconds = clearTime - minutes * 60
const minutes = Math.floor(party.clearTime / 60)
const seconds = party.clearTime - minutes * 60
let string = ''
if (minutes > 0)
@ -296,8 +352,9 @@ const PartyHeader = (props: Props) => {
{chargeAttackToken}
{fullAutoToken}
{autoGuardToken}
{turnCount ? turnCountToken : ''}
{clearTime > 0 ? clearTimeToken() : ''}
{autoSummonToken}
{party.turnCount ? turnCountToken : ''}
{party.clearTime > 0 ? clearTimeToken() : ''}
{buttonChainToken()}
</section>
)
@ -329,7 +386,7 @@ const PartyHeader = (props: Props) => {
leftAccessoryIcon={<RemixIcon />}
className="Remix"
text={t('buttons.remix')}
onClick={props.remixCallback}
onClick={openRemixTeamAlert}
/>
</Tooltip>
)
@ -341,8 +398,8 @@ const PartyHeader = (props: Props) => {
<div className="PartyInfo">
<div className="Left">
<div className="Header">
<h1 className={name ? '' : 'empty'}>
{name ? name : t('no_title')}
<h1 className={party.name ? '' : 'empty'}>
{party.name ? party.name : t('no_title')}
</h1>
{party.remix && party.sourceParty ? (
<Tooltip content={t('tooltips.source')}>
@ -398,6 +455,21 @@ const PartyHeader = (props: Props) => {
</div>
<section className={classes}>{renderTokens()}</section>
</section>
<RemixTeamAlert
creator={props.editable}
name={partySnapshot.name ? partySnapshot.name : t('no_title')}
open={remixAlertOpen}
onOpenChange={handleRemixTeamAlertChange}
remixCallback={remixTeamCallback}
/>
<RemixedToast
open={remixToastOpen}
partyName={props.party?.name || t('no_title')}
onOpenChange={handleRemixToastOpenChanged}
onCloseClick={handleRemixToastCloseClicked}
/>
</>
)
}

View file

@ -1,5 +1,6 @@
.Combobox.Raid {
box-sizing: border-box;
min-width: 440px;
.Header {
background: var(--dialog-bg);
@ -184,6 +185,10 @@
}
}
.SelectTrigger.Raid .Value.Empty {
color: var(--text-tertiary);
}
.Filters .SelectTrigger.Raid {
& > span {
overflow: hidden;

View file

@ -105,19 +105,25 @@ const RaidCombobox = (props: Props) => {
// Set current raid and section when the component mounts
useEffect(() => {
if (appState.party.raid) {
setCurrentRaid(appState.party.raid)
setCurrentSection(appState.party.raid.group.section)
} else if (props.showAllRaidsOption && !currentRaid) {
setCurrentRaid(allRaidsOption)
}
// if (appState.party.raid) {
// setCurrentRaid(appState.party.raid)
// if (appState.party.raid.group.section > 0) {
// setCurrentSection(appState.party.raid.group.section)
// } else {
// setCurrentSection(1)
// }
// } else if (props.showAllRaidsOption && !currentRaid) {
// setCurrentRaid(allRaidsOption)
// }
}, [])
// Set current raid and section when the current raid changes
useEffect(() => {
if (props.currentRaid) {
setCurrentRaid(props.currentRaid)
if (appState.party.raid && appState.party.raid.group.section > 0)
setCurrentSection(props.currentRaid.group.section)
else setCurrentSection(1)
}
}, [props.currentRaid])
@ -260,7 +266,11 @@ const RaidCombobox = (props: Props) => {
// Toggle the open state of the combobox
function toggleOpen() {
if (open) {
if (currentRaid && currentRaid.slug !== 'all') {
if (
currentRaid &&
currentRaid.slug !== 'all' &&
currentRaid.group.section > 0
) {
setCurrentSection(currentRaid.group.section)
}
setScrolled(false)

View file

@ -1,9 +1,7 @@
import React from 'react'
import React, { useEffect } from 'react'
import Toast from '~components/common/Toast'
import { Trans, useTranslation } from 'next-i18next'
import './index.scss'
interface Props {
partyName: string
open: boolean
@ -19,7 +17,9 @@ const RemixedToast = ({
onCloseClick,
}: Props) => {
const { t } = useTranslation('common')
useEffect(() => {
console.log(partyName)
}, [])
// Methods: Event handlers
function handleOpenChange() {
onOpenChange(open)

View file

@ -1,7 +1,5 @@
import React from 'react'
import Toast from '~components/common/Toast'
import './index.scss'
import { useTranslation } from 'next-i18next'
interface Props {

View file

@ -394,6 +394,7 @@ const WeaponModal = ({
{gridWeapon.object.awakenings ? awakeningSelect() : ''}
</div>
<div className="DialogFooter" ref={footerRef}>
<div className="actions">
<Button
contained={true}
onClick={updateWeapon}
@ -401,6 +402,7 @@ const WeaponModal = ({
text={t('modals.weapon.buttons.confirm')}
/>
</div>
</div>
</DialogContent>
</Dialog>
)

View file

@ -93,6 +93,7 @@
"unauthorized": "You don't have permission to perform that action"
},
"filters": {
"name": "Filter",
"labels": {
"element": "Element",
"series": "Series",
@ -383,6 +384,7 @@
"charge_attack": "Charge Attack",
"full_auto": "Full Auto",
"auto_guard": "Auto Guard",
"auto_summon": "Auto Summon",
"turn_count": "Turn count",
"button_chain": "Buttons/Chains",
"clear_time": "Clear time"

View file

@ -93,6 +93,7 @@
"unauthorized": "行ったアクションを実行する権限がありません"
},
"filters": {
"name": "フィルター",
"labels": {
"element": "属性",
"series": "シリーズ",
@ -380,6 +381,7 @@
"charge_attack": "奥義",
"full_auto": "フルオート",
"auto_guard": "オートガード",
"auto_summon": "オート召喚",
"turn_count": "経過ターン",
"button_chain": "ポチチェイン",
"clear_time": "討伐時間"

View file

@ -49,6 +49,7 @@ interface AppState {
element: number
fullAuto: boolean
autoGuard: boolean
autoSummon: boolean
chargeAttack: boolean
clearTime: number
buttonCount?: number
@ -110,6 +111,7 @@ export const initialAppState: AppState = {
raid: undefined,
fullAuto: false,
autoGuard: false,
autoSummon: false,
chargeAttack: true,
clearTime: 0,
buttonCount: undefined,