Implement dialog and notices
This implements the PartyVisibilityDialog in PartyHeader, lets the user invoke it from PartyDropdown, and adds notices so the user knows if a party is private or unlisted.
This commit is contained in:
parent
6e0973d406
commit
2cf8b028d1
4 changed files with 143 additions and 1 deletions
|
|
@ -46,6 +46,10 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.no-shrink {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.blended {
|
&.blended {
|
||||||
background: transparent;
|
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 {
|
&.destructive {
|
||||||
background: $error;
|
background: $error;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Libraries
|
// Libraries
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
@ -33,12 +33,14 @@ interface Props {
|
||||||
editable: boolean
|
editable: boolean
|
||||||
deleteTeamCallback: () => void
|
deleteTeamCallback: () => void
|
||||||
remixTeamCallback: () => void
|
remixTeamCallback: () => void
|
||||||
|
teamVisibilityCallback: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PartyDropdown = ({
|
const PartyDropdown = ({
|
||||||
editable,
|
editable,
|
||||||
deleteTeamCallback,
|
deleteTeamCallback,
|
||||||
remixTeamCallback,
|
remixTeamCallback,
|
||||||
|
teamVisibilityCallback,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
// Localization
|
// Localization
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
@ -81,6 +83,11 @@ const PartyDropdown = ({
|
||||||
|
|
||||||
// Methods: Event handlers
|
// Methods: Event handlers
|
||||||
|
|
||||||
|
// Dialogs / Visibility
|
||||||
|
function visibilityCallback() {
|
||||||
|
teamVisibilityCallback()
|
||||||
|
}
|
||||||
|
|
||||||
// Alerts / Delete team
|
// Alerts / Delete team
|
||||||
function openDeleteTeamAlert() {
|
function openDeleteTeamAlert() {
|
||||||
setDeleteAlertOpen(true)
|
setDeleteAlertOpen(true)
|
||||||
|
|
@ -125,6 +132,9 @@ const PartyDropdown = ({
|
||||||
const items = (
|
const items = (
|
||||||
<>
|
<>
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem onClick={visibilityCallback}>
|
||||||
|
<span>{t('dropdown.party.visibility')}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={copyToClipboard}>
|
<DropdownMenuItem onClick={copyToClipboard}>
|
||||||
<span>{t('dropdown.party.copy')}</span>
|
<span>{t('dropdown.party.copy')}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notice {
|
||||||
|
align-items: center;
|
||||||
|
background: var(--notice-bg);
|
||||||
|
border-radius: $card-corner;
|
||||||
|
display: flex;
|
||||||
|
gap: $unit-2x;
|
||||||
|
font-size: $font-regular;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: $unit-4x;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
@include breakpoint(small-tablet) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $unit;
|
||||||
|
padding: $unit-2x;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--notice-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: $unit;
|
||||||
|
|
||||||
|
@include breakpoint(small-tablet) {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ import SaveIcon from '~public/icons/Save.svg'
|
||||||
import type { DetailsObject } from 'types'
|
import type { DetailsObject } from 'types'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
import PartyVisibilityDialog from '../PartyVisibilityDialog'
|
||||||
|
import UrlCopiedToast from '~components/toasts/UrlCopiedToast'
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -50,8 +52,10 @@ const PartyHeader = (props: Props) => {
|
||||||
|
|
||||||
// State: Component
|
// State: Component
|
||||||
const [detailsOpen, setDetailsOpen] = useState(false)
|
const [detailsOpen, setDetailsOpen] = useState(false)
|
||||||
|
const [copyToastOpen, setCopyToastOpen] = useState(false)
|
||||||
const [remixAlertOpen, setRemixAlertOpen] = useState(false)
|
const [remixAlertOpen, setRemixAlertOpen] = useState(false)
|
||||||
const [remixToastOpen, setRemixToastOpen] = useState(false)
|
const [remixToastOpen, setRemixToastOpen] = useState(false)
|
||||||
|
const [visibilityDialogOpen, setVisibilityDialogOpen] = useState(false)
|
||||||
|
|
||||||
const userClass = classNames({
|
const userClass = classNames({
|
||||||
[styles.user]: true,
|
[styles.user]: true,
|
||||||
|
|
@ -122,12 +126,29 @@ const PartyHeader = (props: Props) => {
|
||||||
setDetailsOpen(open)
|
setDetailsOpen(open)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dialogs: Visibility
|
||||||
|
function visibilityDialogCallback() {
|
||||||
|
setVisibilityDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleVisibilityDialogChange(open: boolean) {
|
||||||
|
setVisibilityDialogOpen(open)
|
||||||
|
}
|
||||||
|
|
||||||
// Actions: Remix team
|
// Actions: Remix team
|
||||||
function remixTeamCallback() {
|
function remixTeamCallback() {
|
||||||
setRemixToastOpen(true)
|
setRemixToastOpen(true)
|
||||||
props.remixCallback()
|
props.remixCallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actions: Copy URL
|
||||||
|
function copyToClipboard() {
|
||||||
|
if (router.asPath.split('/')[1] === 'p') {
|
||||||
|
navigator.clipboard.writeText(window.location.href)
|
||||||
|
setCopyToastOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Alerts: Remix team
|
// Alerts: Remix team
|
||||||
function openRemixTeamAlert() {
|
function openRemixTeamAlert() {
|
||||||
setRemixAlertOpen(true)
|
setRemixAlertOpen(true)
|
||||||
|
|
@ -146,6 +167,15 @@ const PartyHeader = (props: Props) => {
|
||||||
setRemixToastOpen(false)
|
setRemixToastOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toasts / Copy URL
|
||||||
|
function handleCopyToastOpenChanged(open: boolean) {
|
||||||
|
setCopyToastOpen(!open)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCopyToastCloseClicked() {
|
||||||
|
setCopyToastOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
// Rendering
|
// Rendering
|
||||||
|
|
||||||
const userBlock = (username?: string, picture?: string, element?: string) => {
|
const userBlock = (username?: string, picture?: string, element?: string) => {
|
||||||
|
|
@ -298,6 +328,44 @@ const PartyHeader = (props: Props) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render: Notice
|
||||||
|
const unlistedNotice = (
|
||||||
|
<div className={styles.notice}>
|
||||||
|
<p>{t('party.notices.unlisted')}</p>
|
||||||
|
<div className={styles.buttons}>
|
||||||
|
<Button
|
||||||
|
bound={true}
|
||||||
|
className="notice no-shrink"
|
||||||
|
key="copy_link"
|
||||||
|
text={t('party.notices.buttons.copy_link')}
|
||||||
|
onClick={copyToClipboard}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
bound={true}
|
||||||
|
className="notice no-shrink"
|
||||||
|
key="change_visibility"
|
||||||
|
text={t('party.notices.buttons.change_visibility')}
|
||||||
|
onClick={() => handleVisibilityDialogChange(true)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const privateNotice = (
|
||||||
|
<div className={styles.notice}>
|
||||||
|
<p>{t('party.notices.private')}</p>
|
||||||
|
<div className={styles.buttons}>
|
||||||
|
<Button
|
||||||
|
bound={true}
|
||||||
|
className="notice"
|
||||||
|
key="change_visibility"
|
||||||
|
text={t('party.notices.buttons.change_visibility')}
|
||||||
|
onClick={() => handleVisibilityDialogChange(true)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
// Render: Buttons
|
// Render: Buttons
|
||||||
const saveButton = () => {
|
const saveButton = () => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -358,6 +426,8 @@ const PartyHeader = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className={styles.wrapper}>
|
<header className={styles.wrapper}>
|
||||||
|
{party.visibility == 2 && unlistedNotice}
|
||||||
|
{party.visibility == 3 && privateNotice}
|
||||||
<section className={styles.info}>
|
<section className={styles.info}>
|
||||||
<div className={styles.left}>
|
<div className={styles.left}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
|
|
@ -399,6 +469,7 @@ const PartyHeader = (props: Props) => {
|
||||||
editable={props.editable}
|
editable={props.editable}
|
||||||
deleteTeamCallback={props.deleteCallback}
|
deleteTeamCallback={props.deleteCallback}
|
||||||
remixTeamCallback={props.remixCallback}
|
remixTeamCallback={props.remixCallback}
|
||||||
|
teamVisibilityCallback={visibilityDialogCallback}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -412,6 +483,13 @@ const PartyHeader = (props: Props) => {
|
||||||
<section className={styles.tokens}>{renderTokens()}</section>
|
<section className={styles.tokens}>{renderTokens()}</section>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<PartyVisibilityDialog
|
||||||
|
open={visibilityDialogOpen}
|
||||||
|
value={party.visibility as 1 | 2 | 3}
|
||||||
|
onOpenChange={handleVisibilityDialogChange}
|
||||||
|
updateParty={props.updateCallback}
|
||||||
|
/>
|
||||||
|
|
||||||
<RemixTeamAlert
|
<RemixTeamAlert
|
||||||
creator={props.editable}
|
creator={props.editable}
|
||||||
name={partySnapshot.name ? partySnapshot.name : t('no_title')}
|
name={partySnapshot.name ? partySnapshot.name : t('no_title')}
|
||||||
|
|
@ -426,6 +504,12 @@ const PartyHeader = (props: Props) => {
|
||||||
onOpenChange={handleRemixToastOpenChanged}
|
onOpenChange={handleRemixToastOpenChanged}
|
||||||
onCloseClick={handleRemixToastCloseClicked}
|
onCloseClick={handleRemixToastCloseClicked}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<UrlCopiedToast
|
||||||
|
open={copyToastOpen}
|
||||||
|
onOpenChange={handleCopyToastOpenChanged}
|
||||||
|
onCloseClick={handleCopyToastCloseClicked}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue