commit
f293f92e23
19 changed files with 401 additions and 52 deletions
|
|
@ -31,6 +31,15 @@
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
&.IconButton.medium {
|
||||
height: inherit;
|
||||
padding: $unit-half;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.Contained {
|
||||
background: var(--button-contained-bg);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import Link from 'next/link'
|
|||
|
||||
import api from '~utils/api'
|
||||
import { accountState, initialAccountState } from '~utils/accountState'
|
||||
import { appState, initialAppState } from '~utils/appState'
|
||||
import { appState } from '~utils/appState'
|
||||
import capitalizeFirstLetter from '~utils/capitalizeFirstLetter'
|
||||
|
||||
import {
|
||||
|
|
@ -26,12 +26,14 @@ import AccountModal from '~components/AccountModal'
|
|||
import Toast from '~components/Toast'
|
||||
import Button from '~components/Button'
|
||||
|
||||
import ArrowIcon from '~public/icons/Arrow.svg'
|
||||
import LinkIcon from '~public/icons/Link.svg'
|
||||
import MenuIcon from '~public/icons/Menu.svg'
|
||||
import ArrowIcon from '~public/icons/Arrow.svg'
|
||||
import RemixIcon from '~public/icons/Remix.svg'
|
||||
import SaveIcon from '~public/icons/Save.svg'
|
||||
|
||||
import './index.scss'
|
||||
import Tooltip from '~components/Tooltip'
|
||||
|
||||
const Header = () => {
|
||||
// Localization
|
||||
|
|
@ -83,10 +85,6 @@ const Header = () => {
|
|||
setRightMenuOpen(false)
|
||||
}
|
||||
|
||||
function handleSettingsOpenChanged(open: boolean) {
|
||||
setRightMenuOpen(false)
|
||||
}
|
||||
|
||||
function copyToClipboard() {
|
||||
const el = document.createElement('input')
|
||||
el.value = window.location.href
|
||||
|
|
@ -106,15 +104,6 @@ const Header = () => {
|
|||
// Push the root URL
|
||||
router.push(path)
|
||||
|
||||
// Clean state
|
||||
const resetState = clonedeep(initialAppState)
|
||||
Object.keys(resetState).forEach((key) => {
|
||||
appState[key] = resetState[key]
|
||||
})
|
||||
|
||||
// Set party to be editable
|
||||
appState.party.editable = true
|
||||
|
||||
// Close right menu
|
||||
closeRightMenu()
|
||||
}
|
||||
|
|
@ -158,6 +147,14 @@ const Header = () => {
|
|||
else console.error('Failed to unsave team: No party ID')
|
||||
}
|
||||
|
||||
function remixTeam() {
|
||||
if (party.shortcode)
|
||||
api.remix(party.shortcode).then((response) => {
|
||||
const remix = response.data.party
|
||||
router.push(`/p/${remix.shortcode}`)
|
||||
})
|
||||
}
|
||||
|
||||
const pageTitle = () => {
|
||||
let title = ''
|
||||
let hasAccessory = false
|
||||
|
|
@ -219,7 +216,7 @@ const Header = () => {
|
|||
open={copyToastOpen}
|
||||
duration={2400}
|
||||
type="foreground"
|
||||
content="This party's URL was copied to your clipboard"
|
||||
content={t('toasts.copied')}
|
||||
onOpenChange={handleCopyToastOpenChanged}
|
||||
onCloseClick={handleCopyToastCloseClicked}
|
||||
/>
|
||||
|
|
@ -228,16 +225,32 @@ const Header = () => {
|
|||
|
||||
const saveButton = () => {
|
||||
return (
|
||||
<Button
|
||||
leftAccessoryIcon={<SaveIcon />}
|
||||
className={classNames({
|
||||
Save: true,
|
||||
Saved: party.favorited,
|
||||
})}
|
||||
blended={true}
|
||||
text={party.favorited ? 'Saved' : 'Save'}
|
||||
onClick={toggleFavorite}
|
||||
/>
|
||||
<Tooltip content={t('tooltips.save')}>
|
||||
<Button
|
||||
leftAccessoryIcon={<SaveIcon />}
|
||||
className={classNames({
|
||||
Save: true,
|
||||
Saved: party.favorited,
|
||||
})}
|
||||
blended={true}
|
||||
text={party.favorited ? t('buttons.saved') : t('buttons.save')}
|
||||
onClick={toggleFavorite}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const remixButton = () => {
|
||||
return (
|
||||
<Tooltip content={t('tooltips.remix')}>
|
||||
<Button
|
||||
leftAccessoryIcon={<RemixIcon />}
|
||||
className="Remix"
|
||||
blended={true}
|
||||
text={t('buttons.remix')}
|
||||
onClick={remixTeam}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -303,7 +316,7 @@ const Header = () => {
|
|||
(!party.user || party.user.id !== account.user.id)
|
||||
? saveButton()
|
||||
: ''}
|
||||
|
||||
{router.route === '/p/[party]' ? remixButton() : ''}
|
||||
<DropdownMenu
|
||||
open={rightMenuOpen}
|
||||
onOpenChange={handleRightMenuOpenChange}
|
||||
|
|
|
|||
|
|
@ -150,9 +150,13 @@ const Party = (props: Props) => {
|
|||
appState.party.accessory = team.accessory
|
||||
|
||||
appState.party.id = team.id
|
||||
appState.party.shortcode = team.shortcode
|
||||
appState.party.extra = team.extra
|
||||
appState.party.user = team.user
|
||||
appState.party.favorited = team.favorited
|
||||
appState.party.remix = team.remix
|
||||
appState.party.remixes = team.remixes
|
||||
appState.party.sourceParty = team.source_party
|
||||
appState.party.created_at = team.created_at
|
||||
appState.party.updated_at = team.updated_at
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
flex-direction: column;
|
||||
margin: $unit-4x auto 0 auto;
|
||||
max-width: $grid-width;
|
||||
padding-bottom: $unit-12x;
|
||||
|
||||
@include breakpoint(phone) {
|
||||
padding: 0 $unit;
|
||||
|
|
@ -10,13 +11,13 @@
|
|||
|
||||
.PartyDetails {
|
||||
display: none;
|
||||
margin: 0 auto;
|
||||
margin: 0 auto $unit-2x;
|
||||
max-width: $unit * 94;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
&.Visible {
|
||||
margin-bottom: $unit-12x;
|
||||
// margin-bottom: $unit-12x;
|
||||
}
|
||||
|
||||
&.Editable {
|
||||
|
|
@ -269,15 +270,21 @@
|
|||
.Left {
|
||||
flex-grow: 1;
|
||||
|
||||
h1 {
|
||||
font-size: $font-xlarge;
|
||||
font-weight: $normal;
|
||||
text-align: left;
|
||||
.Header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: $unit;
|
||||
margin-bottom: $unit;
|
||||
color: var(--text-primary);
|
||||
|
||||
&.empty {
|
||||
color: var(--text-secondary);
|
||||
h1 {
|
||||
font-size: $font-xlarge;
|
||||
font-weight: $normal;
|
||||
text-align: left;
|
||||
color: var(--text-primary);
|
||||
|
||||
&.empty {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -332,4 +339,26 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Remixes {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-2x;
|
||||
width: 752px;
|
||||
|
||||
h3 {
|
||||
font-size: $font-medium;
|
||||
font-weight: $medium;
|
||||
}
|
||||
|
||||
.GridRepCollection {
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
margin-left: $unit-2x * -1;
|
||||
margin-right: $unit-2x * -1;
|
||||
|
||||
.GridRep {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import Link from 'next/link'
|
|||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
|
||||
import Linkify from 'react-linkify'
|
||||
import LiteYouTubeEmbed from 'react-lite-youtube-embed'
|
||||
|
|
@ -10,17 +11,19 @@ import classNames from 'classnames'
|
|||
import reactStringReplace from 'react-string-replace'
|
||||
|
||||
import Alert from '~components/Alert'
|
||||
|
||||
import Button from '~components/Button'
|
||||
import CharLimitedFieldset from '~components/CharLimitedFieldset'
|
||||
import Input from '~components/Input'
|
||||
import DurationInput from '~components/DurationInput'
|
||||
import GridRepCollection from '~components/GridRepCollection'
|
||||
import GridRep from '~components/GridRep'
|
||||
import Input from '~components/Input'
|
||||
import RaidDropdown from '~components/RaidDropdown'
|
||||
import Switch from '~components/Switch'
|
||||
import Tooltip from '~components/Tooltip'
|
||||
import TextFieldset from '~components/TextFieldset'
|
||||
import Token from '~components/Token'
|
||||
|
||||
import RaidDropdown from '~components/RaidDropdown'
|
||||
import TextFieldset from '~components/TextFieldset'
|
||||
import Switch from '~components/Switch'
|
||||
|
||||
import api from '~utils/api'
|
||||
import { accountState } from '~utils/accountState'
|
||||
import { appState } from '~utils/appState'
|
||||
import { formatTimeAgo } from '~utils/timeAgo'
|
||||
|
|
@ -29,6 +32,7 @@ import { youtube } from '~utils/youtube'
|
|||
import CheckIcon from '~public/icons/Check.svg'
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import EditIcon from '~public/icons/Edit.svg'
|
||||
import RemixIcon from '~public/icons/Remix.svg'
|
||||
|
||||
import type { DetailsObject } from 'types'
|
||||
|
||||
|
|
@ -69,6 +73,8 @@ const PartyDetails = (props: Props) => {
|
|||
const [turnCount, setTurnCount] = useState<number | undefined>(undefined)
|
||||
const [clearTime, setClearTime] = useState(0)
|
||||
|
||||
const [remixes, setRemixes] = useState<Party[]>([])
|
||||
|
||||
const [raidSlug, setRaidSlug] = useState('')
|
||||
const [embeddedDescription, setEmbeddedDescription] =
|
||||
useState<React.ReactNode>()
|
||||
|
|
@ -111,6 +117,7 @@ const PartyDetails = (props: Props) => {
|
|||
setFullAuto(props.party.full_auto)
|
||||
setChargeAttack(props.party.charge_attack)
|
||||
setClearTime(props.party.clear_time)
|
||||
setRemixes(props.party.remixes)
|
||||
if (props.party.turn_count) setTurnCount(props.party.turn_count)
|
||||
if (props.party.button_count) setButtonCount(props.party.button_count)
|
||||
if (props.party.chain_count) setChainCount(props.party.chain_count)
|
||||
|
|
@ -300,6 +307,49 @@ const PartyDetails = (props: Props) => {
|
|||
props.deleteCallback()
|
||||
}
|
||||
|
||||
// Methods: Navigation
|
||||
function goTo(shortcode?: string) {
|
||||
if (shortcode) router.push(`/p/${shortcode}`)
|
||||
}
|
||||
|
||||
// Methods: Favorites
|
||||
function toggleFavorite(teamId: string, favorited: boolean) {
|
||||
if (favorited) unsaveFavorite(teamId)
|
||||
else saveFavorite(teamId)
|
||||
}
|
||||
|
||||
function saveFavorite(teamId: string) {
|
||||
api.saveTeam({ id: teamId }).then((response) => {
|
||||
if (response.status == 201) {
|
||||
const index = remixes.findIndex((p) => p.id === teamId)
|
||||
const party = remixes[index]
|
||||
|
||||
party.favorited = true
|
||||
|
||||
let clonedParties = clonedeep(remixes)
|
||||
clonedParties[index] = party
|
||||
|
||||
setRemixes(clonedParties)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function unsaveFavorite(teamId: string) {
|
||||
api.unsaveTeam({ id: teamId }).then((response) => {
|
||||
if (response.status == 200) {
|
||||
const index = remixes.findIndex((p) => p.id === teamId)
|
||||
const party = remixes[index]
|
||||
|
||||
party.favorited = false
|
||||
|
||||
let clonedParties = clonedeep(remixes)
|
||||
clonedParties[index] = party
|
||||
|
||||
setRemixes(clonedParties)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function extractYoutubeVideoIds(text: string) {
|
||||
// Initialize an array to store the video IDs
|
||||
const videoIds = []
|
||||
|
|
@ -388,6 +438,28 @@ const PartyDetails = (props: Props) => {
|
|||
)
|
||||
}
|
||||
|
||||
function renderRemixes() {
|
||||
return remixes.map((party, i) => {
|
||||
return (
|
||||
<GridRep
|
||||
id={party.id}
|
||||
shortcode={party.shortcode}
|
||||
name={party.name}
|
||||
createdAt={new Date(party.created_at)}
|
||||
raid={party.raid}
|
||||
grid={party.weapons}
|
||||
user={party.user}
|
||||
favorited={party.favorited}
|
||||
fullAuto={party.full_auto}
|
||||
key={`party-${i}`}
|
||||
displayUser={true}
|
||||
onClick={goTo}
|
||||
onSave={toggleFavorite}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const deleteAlert = () => {
|
||||
if (party.editable) {
|
||||
return (
|
||||
|
|
@ -620,11 +692,35 @@ const PartyDetails = (props: Props) => {
|
|||
</section>
|
||||
)
|
||||
|
||||
const remixSection = () => {
|
||||
return (
|
||||
<section className="Remixes">
|
||||
<h3>{t('remixes')}</h3>
|
||||
{<GridRepCollection>{renderRemixes()}</GridRepCollection>}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="DetailsWrapper">
|
||||
<div className="PartyInfo">
|
||||
<div className="Left">
|
||||
<h1 className={name ? '' : 'empty'}>{name ? name : t('no_title')}</h1>
|
||||
<div className="Header">
|
||||
<h1 className={name ? '' : 'empty'}>
|
||||
{name ? name : t('no_title')}
|
||||
</h1>
|
||||
{party.remix && party.sourceParty ? (
|
||||
<Tooltip content={t('tooltips.source')}>
|
||||
<Button
|
||||
className="IconButton Blended"
|
||||
leftAccessoryIcon={<RemixIcon />}
|
||||
onClick={() => goTo(party.sourceParty?.shortcode)}
|
||||
/>
|
||||
</Tooltip>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
<div className="attribution">
|
||||
{renderUserBlock()}
|
||||
{party.raid ? linkedRaidBlock(party.raid) : ''}
|
||||
|
|
@ -654,6 +750,7 @@ const PartyDetails = (props: Props) => {
|
|||
</div>
|
||||
{readOnly}
|
||||
{editable}
|
||||
{remixes && remixes.length > 0 ? remixSection() : ''}
|
||||
{deleteAlert()}
|
||||
</section>
|
||||
)
|
||||
|
|
|
|||
8
components/Tooltip/index.scss
Normal file
8
components/Tooltip/index.scss
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.Tooltip {
|
||||
background: var(--dialog-bg);
|
||||
border-radius: $card-corner;
|
||||
line-height: 1.3;
|
||||
padding: $unit * 1.5;
|
||||
z-index: 35;
|
||||
max-width: 200px;
|
||||
}
|
||||
39
components/Tooltip/index.tsx
Normal file
39
components/Tooltip/index.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import React, { PropsWithChildren } from 'react'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
|
||||
|
||||
import './index.scss'
|
||||
interface Props extends TooltipPrimitive.TooltipContentProps {
|
||||
content: React.ReactNode
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}
|
||||
|
||||
export default function Tooltip({
|
||||
children,
|
||||
content,
|
||||
open,
|
||||
onOpenChange,
|
||||
...props
|
||||
}: PropsWithChildren<Props>) {
|
||||
const classes = classNames(props.className, {
|
||||
Tooltip: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<TooltipPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
||||
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
|
||||
<TooltipPrimitive.Content
|
||||
side="top"
|
||||
align="center"
|
||||
className={classes}
|
||||
sideOffset={4}
|
||||
{...props}
|
||||
>
|
||||
{content}
|
||||
{/* <TooltipPrimitive.Arrow width={11} height={5} /> */}
|
||||
</TooltipPrimitive.Content>
|
||||
</TooltipPrimitive.Root>
|
||||
)
|
||||
}
|
||||
87
package-lock.json
generated
87
package-lock.json
generated
|
|
@ -16,6 +16,7 @@
|
|||
"@radix-ui/react-switch": "^1.0.1",
|
||||
"@radix-ui/react-toast": "^1.1.2",
|
||||
"@radix-ui/react-toggle-group": "^1.0.1",
|
||||
"@radix-ui/react-tooltip": "^1.0.3",
|
||||
"@svgr/webpack": "^6.2.0",
|
||||
"axios": "^0.25.0",
|
||||
"classnames": "^2.3.1",
|
||||
|
|
@ -2623,6 +2624,52 @@
|
|||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tooltip": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.3.tgz",
|
||||
"integrity": "sha512-cmc9qV4KpgqdXVTn1K8KN8MnuSXvw+E719pKwyvpCGrQ+0AA2qTjcIL3uxCj4jc4k3sDR36RF7R3H7N5hPybBQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.0",
|
||||
"@radix-ui/react-compose-refs": "1.0.0",
|
||||
"@radix-ui/react-context": "1.0.0",
|
||||
"@radix-ui/react-dismissable-layer": "1.0.2",
|
||||
"@radix-ui/react-id": "1.0.0",
|
||||
"@radix-ui/react-popper": "1.1.0",
|
||||
"@radix-ui/react-portal": "1.0.1",
|
||||
"@radix-ui/react-presence": "1.0.0",
|
||||
"@radix-ui/react-primitive": "1.0.1",
|
||||
"@radix-ui/react-slot": "1.0.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.0.0",
|
||||
"@radix-ui/react-visually-hidden": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.0.tgz",
|
||||
"integrity": "sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@floating-ui/react-dom": "0.7.2",
|
||||
"@radix-ui/react-arrow": "1.0.1",
|
||||
"@radix-ui/react-compose-refs": "1.0.0",
|
||||
"@radix-ui/react-context": "1.0.0",
|
||||
"@radix-ui/react-primitive": "1.0.1",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.0.0",
|
||||
"@radix-ui/react-use-rect": "1.0.0",
|
||||
"@radix-ui/react-use-size": "1.0.0",
|
||||
"@radix-ui/rect": "1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz",
|
||||
|
|
@ -9156,6 +9203,46 @@
|
|||
"@radix-ui/react-use-controllable-state": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@radix-ui/react-tooltip": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.3.tgz",
|
||||
"integrity": "sha512-cmc9qV4KpgqdXVTn1K8KN8MnuSXvw+E719pKwyvpCGrQ+0AA2qTjcIL3uxCj4jc4k3sDR36RF7R3H7N5hPybBQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.0",
|
||||
"@radix-ui/react-compose-refs": "1.0.0",
|
||||
"@radix-ui/react-context": "1.0.0",
|
||||
"@radix-ui/react-dismissable-layer": "1.0.2",
|
||||
"@radix-ui/react-id": "1.0.0",
|
||||
"@radix-ui/react-popper": "1.1.0",
|
||||
"@radix-ui/react-portal": "1.0.1",
|
||||
"@radix-ui/react-presence": "1.0.0",
|
||||
"@radix-ui/react-primitive": "1.0.1",
|
||||
"@radix-ui/react-slot": "1.0.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.0.0",
|
||||
"@radix-ui/react-visually-hidden": "1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-popper": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.0.tgz",
|
||||
"integrity": "sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@floating-ui/react-dom": "0.7.2",
|
||||
"@radix-ui/react-arrow": "1.0.1",
|
||||
"@radix-ui/react-compose-refs": "1.0.0",
|
||||
"@radix-ui/react-context": "1.0.0",
|
||||
"@radix-ui/react-primitive": "1.0.1",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.0.0",
|
||||
"@radix-ui/react-use-rect": "1.0.0",
|
||||
"@radix-ui/react-use-size": "1.0.0",
|
||||
"@radix-ui/rect": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"@radix-ui/react-switch": "^1.0.1",
|
||||
"@radix-ui/react-toast": "^1.1.2",
|
||||
"@radix-ui/react-toggle-group": "^1.0.1",
|
||||
"@radix-ui/react-tooltip": "^1.0.3",
|
||||
"@svgr/webpack": "^6.2.0",
|
||||
"axios": "^0.25.0",
|
||||
"classnames": "^2.3.1",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import setUserToken from '~utils/setUserToken'
|
|||
|
||||
import '../styles/globals.scss'
|
||||
import { ToastProvider, Viewport } from '@radix-ui/react-toast'
|
||||
import { TooltipProvider } from '@radix-ui/react-tooltip'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
const accountCookie = getCookie('account')
|
||||
|
|
@ -45,10 +46,12 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
return (
|
||||
<ThemeProvider>
|
||||
<ToastProvider swipeDirection="right">
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
<Viewport className="ToastViewport" />
|
||||
<TooltipProvider>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
<Viewport className="ToastViewport" />
|
||||
</TooltipProvider>
|
||||
</ToastProvider>
|
||||
</ThemeProvider>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import Head from 'next/head'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
|
||||
import Party from '~components/Party'
|
||||
|
||||
|
|
@ -9,7 +11,7 @@ import api from '~utils/api'
|
|||
import fetchLatestVersion from '~utils/fetchLatestVersion'
|
||||
import organizeRaids from '~utils/organizeRaids'
|
||||
import setUserToken from '~utils/setUserToken'
|
||||
import { appState } from '~utils/appState'
|
||||
import { appState, initialAppState } from '~utils/appState'
|
||||
import { groupWeaponKeys } from '~utils/groupWeaponKeys'
|
||||
import { printError } from '~utils/reportError'
|
||||
|
||||
|
|
@ -29,6 +31,9 @@ const NewRoute: React.FC<Props> = (props: Props) => {
|
|||
// Import translations
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Set up router
|
||||
const router = useRouter()
|
||||
|
||||
function callback(path: string) {
|
||||
// This is scuffed, how do we do this natively?
|
||||
window.history.replaceState(null, `Grid Tool`, `${path}`)
|
||||
|
|
@ -38,6 +43,16 @@ const NewRoute: React.FC<Props> = (props: Props) => {
|
|||
persistStaticData()
|
||||
}, [persistStaticData])
|
||||
|
||||
useEffect(() => {
|
||||
// Clean state
|
||||
const resetState = clonedeep(initialAppState)
|
||||
Object.keys(resetState).forEach((key) => {
|
||||
appState[key] = resetState[key]
|
||||
})
|
||||
// Set party to be editable
|
||||
appState.party.editable = true
|
||||
}, [])
|
||||
|
||||
function persistStaticData() {
|
||||
appState.raids = props.raids
|
||||
appState.jobs = props.jobs
|
||||
|
|
@ -47,7 +62,7 @@ const NewRoute: React.FC<Props> = (props: Props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<React.Fragment key={router.asPath}>
|
||||
<Head>
|
||||
{/* HTML */}
|
||||
<title>{t('page.titles.new')}</title>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ const PartyRoute: React.FC<Props> = (props: Props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<React.Fragment key={router.asPath}>
|
||||
<Party
|
||||
team={props.party}
|
||||
raids={props.sortedRaids}
|
||||
|
|
|
|||
4
public/icons/Remix.svg
Normal file
4
public/icons/Remix.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.50019 2.00047C5.18025 2.00132 4.83617 2.00371 4.51209 2.01042C3.93815 2.0223 3.42693 2.04775 3.22357 2.10224C2.1883 2.37964 1.37965 3.18828 1.10225 4.22356C1.04775 4.42694 1.02231 4.93829 1.01043 5.51233C1.00003 6.01504 1.00003 6.56583 1.00003 7.00001C1.00003 7.43419 1.00003 7.98498 1.01043 8.48769C1.02231 9.06174 1.04775 9.57308 1.10225 9.77647C1.36232 10.747 2.08929 11.5184 3.03219 11.8396C3.09505 11.861 3.15887 11.8805 3.22357 11.8978C3.42692 11.9523 3.9381 11.9777 4.51201 11.9896C4.7811 11.9952 5.06399 11.9978 5.33539 11.999L4.73226 12.6021C4.537 12.7974 4.537 13.1139 4.73226 13.3092C4.92752 13.5045 5.24411 13.5045 5.43937 13.3092L6.85358 11.895C6.95385 11.7947 7.00263 11.6625 6.99992 11.5311C7.00263 11.3997 6.95385 11.2674 6.85358 11.1672L5.43937 9.75295C5.24411 9.55769 4.92752 9.55769 4.73226 9.75295C4.537 9.94822 4.537 10.2648 4.73226 10.4601L5.27027 10.9981C5.01654 10.9966 4.7638 10.994 4.53092 10.9898C4.01686 10.9805 3.59961 10.9633 3.48239 10.9319C2.83534 10.7585 2.32109 10.2738 2.10695 9.64524C2.09268 9.60333 2.07974 9.56079 2.06818 9.51765C2.03675 9.40038 2.01952 8.98285 2.01025 8.4685C2.00195 8.00794 2.00003 7.46976 2.00003 7.00001C2.00003 6.53027 2.00195 5.99208 2.01025 5.53152C2.01952 5.01718 2.03676 4.59965 2.06818 4.48238C2.25311 3.79219 2.79221 3.2531 3.48239 3.06816C3.59963 3.03675 4.01694 3.01951 4.53106 3.01024C4.83429 3.00477 5.17118 3.00206 5.50006 3.00086C5.7762 2.99984 6.00003 2.77615 6.00003 2.50001C6.00003 2.22386 5.77633 1.99974 5.50019 2.00047Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.7765 11.8999C10.462 11.9842 9.40436 11.9981 8.52156 12.0003L8.4928 12.0004L8.48903 12.0004C8.21325 12.001 7.98986 11.7772 7.98986 11.5015C7.98986 11.2265 8.21208 11.0036 8.48675 11.0013L8.48916 11.0013L8.51572 11.0012C9.39725 10.9977 10.333 10.9834 10.5176 10.934C11.2078 10.749 11.7469 10.2099 11.9319 9.51976C11.9633 9.40248 11.9805 8.98495 11.9898 8.47061C11.9981 8.01005 12 7.47186 12 7.00212C12 6.53237 11.9981 5.99419 11.9898 5.53363C11.9805 5.01928 11.9633 4.60175 11.9319 4.48448C11.9203 4.44134 11.9074 4.3988 11.8931 4.35689C11.6789 3.72829 11.1647 3.24364 10.5176 3.07026C10.4004 3.03886 9.98317 3.02162 9.46911 3.01235C9.23664 3.00815 8.98438 3.00559 8.7311 3.00407L9.26777 3.54074C9.46303 3.736 9.46303 4.05258 9.26777 4.24784C9.0725 4.44311 8.75592 4.44311 8.56066 4.24784L7.14645 2.83363C7.04618 2.73336 6.9974 2.60111 7.00011 2.46972C6.9974 2.33833 7.04618 2.20607 7.14645 2.1058L8.56066 0.69159C8.75592 0.496328 9.0725 0.496328 9.26777 0.69159C9.46303 0.886852 9.46303 1.20343 9.26777 1.3987L8.66331 2.00316C8.93512 2.00436 9.21849 2.00695 9.48802 2.01253C10.0619 2.02441 10.5731 2.04985 10.7765 2.10434C10.8412 2.12168 10.905 2.14109 10.9678 2.1625C11.9107 2.48371 12.6377 3.25509 12.8978 4.22566C12.9523 4.42905 12.9777 4.94039 12.9896 5.51444C13 6.01715 13 6.56794 13 7.00212C13 7.43629 13 7.98709 12.9896 8.48979C12.9777 9.06384 12.9523 9.57519 12.8978 9.77857C12.6204 10.8139 11.8117 11.6225 10.7765 11.8999Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3 KiB |
|
|
@ -39,6 +39,9 @@
|
|||
"show_info": "Edit info",
|
||||
"hide_info": "Hide info",
|
||||
"save_info": "Save info",
|
||||
"remix": "Remix",
|
||||
"save": "Save",
|
||||
"saved": "Saved",
|
||||
"menu": "Menu",
|
||||
"new": "New",
|
||||
"wiki": "View more on gbf.wiki"
|
||||
|
|
@ -384,6 +387,14 @@
|
|||
"no_skill": "No skill"
|
||||
}
|
||||
},
|
||||
"toasts": {
|
||||
"copied": "This party's URL was copied to your clipboard"
|
||||
},
|
||||
"tooltips": {
|
||||
"remix": "Make a copy of this team",
|
||||
"save": "Save this team to your account",
|
||||
"source": "Go to original team"
|
||||
},
|
||||
"extra_weapons": "Additional Weapons",
|
||||
"equipped": "Equipped",
|
||||
"coming_soon": "Coming Soon",
|
||||
|
|
@ -394,5 +405,6 @@
|
|||
"no_user": "Anonymous",
|
||||
"no_job": "No class",
|
||||
"no_value": "No value",
|
||||
"remixes": "Remixes",
|
||||
"level": "Level"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@
|
|||
"show_info": "詳細を編集",
|
||||
"save_info": "詳細を保存",
|
||||
"hide_info": "詳細を非表示",
|
||||
"remix": "リミックス",
|
||||
"save": "保存する",
|
||||
"saved": "保存",
|
||||
"menu": "メニュー",
|
||||
"new": "作成",
|
||||
"wiki": "gbf.wikiで詳しく見る"
|
||||
|
|
@ -385,6 +388,14 @@
|
|||
"no_skill": "設定されていません"
|
||||
}
|
||||
},
|
||||
"toasts": {
|
||||
"copied": "この編成のURLはクリップボードにコピーされました"
|
||||
},
|
||||
"tooltips": {
|
||||
"remix": "この編成をコピーする",
|
||||
"save": "この編成をアカウントに保存する",
|
||||
"source": "オリジナルの編成へ"
|
||||
},
|
||||
"equipped": "装備した",
|
||||
"extra_weapons": "Additional Weapons",
|
||||
"coming_soon": "開発中",
|
||||
|
|
@ -395,5 +406,6 @@
|
|||
"no_user": "無名",
|
||||
"no_job": "ジョブなし",
|
||||
"no_value": "値なし",
|
||||
"remixes": "リミックスされた編成",
|
||||
"level": "レベル"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,8 +153,8 @@ $dialog--bg--dark: $grey-25;
|
|||
// Color Definitions: Menu
|
||||
$menu--bg--light: $grey-100;
|
||||
$menu--bg--dark: $grey-10;
|
||||
$menu--text--light: $grey-90;
|
||||
$menu--text--dark: $grey-50;
|
||||
$menu--text--light: $grey-50;
|
||||
$menu--text--dark: $grey-60;
|
||||
$menu--separator--light: $grey-90;
|
||||
$menu--separator--dark: $grey-05;
|
||||
$menu--item--bg--light--hover: $grey-85;
|
||||
|
|
|
|||
3
types/Party.d.ts
vendored
3
types/Party.d.ts
vendored
|
|
@ -18,6 +18,7 @@ interface Party {
|
|||
button_count?: number
|
||||
turn_count?: number
|
||||
chain_count?: number
|
||||
source_party?: Party
|
||||
job: Job
|
||||
job_skills: JobSkillObject
|
||||
accessory: JobAccessory
|
||||
|
|
@ -28,6 +29,8 @@ interface Party {
|
|||
weapons: Array<GridWeapon>
|
||||
summons: Array<GridSummon>
|
||||
user: User
|
||||
remix: boolean
|
||||
remixes: Party[]
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,11 @@ class Api {
|
|||
return axios.get(resourceUrl, params)
|
||||
}
|
||||
|
||||
remix(shortcode: string, params?: {}) {
|
||||
const resourceUrl = `${this.url}/parties/${shortcode}/remix`
|
||||
return axios.post(resourceUrl, params)
|
||||
}
|
||||
|
||||
savedTeams(params: {}) {
|
||||
const resourceUrl = `${this.url}/parties/favorites`
|
||||
return axios.get(resourceUrl, params)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ interface AppState {
|
|||
|
||||
party: {
|
||||
id: string | undefined
|
||||
shortcode: string | undefined
|
||||
editable: boolean
|
||||
detailsVisible: boolean
|
||||
name: string | undefined
|
||||
|
|
@ -55,6 +56,9 @@ interface AppState {
|
|||
extra: boolean
|
||||
user: User | undefined
|
||||
favorited: boolean
|
||||
remix: boolean
|
||||
remixes: Party[]
|
||||
sourceParty?: Party
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
|
@ -87,6 +91,7 @@ interface AppState {
|
|||
export const initialAppState: AppState = {
|
||||
party: {
|
||||
id: undefined,
|
||||
shortcode: '',
|
||||
editable: false,
|
||||
detailsVisible: false,
|
||||
name: undefined,
|
||||
|
|
@ -111,6 +116,9 @@ export const initialAppState: AppState = {
|
|||
extra: false,
|
||||
user: undefined,
|
||||
favorited: false,
|
||||
remix: false,
|
||||
remixes: [],
|
||||
sourceParty: undefined,
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue