mirror of
https://github.com/Steffo99/todocolors.git
synced 2024-11-24 17:24:18 +00:00
Implement Connect
and Disconnect
BoardChange
s on Todoblue
This commit is contained in:
parent
5ca640be39
commit
33cb947da3
16 changed files with 107 additions and 7 deletions
|
@ -1,5 +1,7 @@
|
|||
import {ConnectBoardSignal} from "@/app/[lang]/board/[board]/(api)/(signal)/ConnectBoardSignal"
|
||||
import {DisconnectBoardSignal} from "@/app/[lang]/board/[board]/(api)/(signal)/DisconnectBoardSignal"
|
||||
import {TaskBoardSignal} from "@/app/[lang]/board/[board]/(api)/(signal)/TaskBoardSignal"
|
||||
import {TitleBoardSignal} from "@/app/[lang]/board/[board]/(api)/(signal)/TitleBoardSignal"
|
||||
|
||||
|
||||
export type BoardSignal = TaskBoardSignal | TitleBoardSignal;
|
||||
export type BoardSignal = TaskBoardSignal | TitleBoardSignal | ConnectBoardSignal | DisconnectBoardSignal;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* **Object** signaling the connection of a new client to the board.
|
||||
*/
|
||||
export type ConnectBoardSignal = {
|
||||
"Connect": string,
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* **Object** signaling the disconnection of a client from the board.
|
||||
*/
|
||||
export type DisconnectBoardSignal = {
|
||||
"Disconnect": string,
|
||||
}
|
|
@ -7,4 +7,5 @@ import {Task} from "@/app/[lang]/board/[board]/(api)/(task)"
|
|||
export type BoardState = {
|
||||
title: string,
|
||||
tasksById: {[key: string]: Task},
|
||||
connectedClients: string[],
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import {BoardSignal, DeleteTaskBoardSignal, TaskBoardSignal, TitleBoardSignal, UpdateTaskBoardSignal} from "@/app/[lang]/board/[board]/(api)/(signal)"
|
||||
import {ConnectBoardSignal} from "@/app/[lang]/board/[board]/(api)/(signal)/ConnectBoardSignal"
|
||||
import {DisconnectBoardSignal} from "@/app/[lang]/board/[board]/(api)/(signal)/DisconnectBoardSignal"
|
||||
import {BoardState} from "@/app/[lang]/board/[board]/(api)/(state)/BoardState"
|
||||
import {DEFAULT_BOARD_STATE} from "@/app/[lang]/board/[board]/(api)/(state)/defaultBoardState"
|
||||
|
||||
|
@ -41,6 +43,28 @@ export function boardReducer(state: BoardState, action: BoardSignal | null) {
|
|||
}
|
||||
return {...state, tasksById}
|
||||
}
|
||||
else if("Connect" in action) {
|
||||
const connectAction = action as ConnectBoardSignal;
|
||||
const id = connectAction["Connect"];
|
||||
const connectedClients = [...state.connectedClients, id]
|
||||
console.debug("[boardReducer] Adding new client:", id)
|
||||
return {...state, connectedClients}
|
||||
}
|
||||
else if("Disconnect" in action) {
|
||||
const disconnectAction = action as DisconnectBoardSignal;
|
||||
const id = disconnectAction["Disconnect"];
|
||||
const connectedClients = [...state.connectedClients]
|
||||
const clientIndex = connectedClients.indexOf(id)
|
||||
if(clientIndex !== -1) {
|
||||
connectedClients.splice(clientIndex, 1);
|
||||
console.debug("[boardReducer] Removing client:", id)
|
||||
return {...state, connectedClients}
|
||||
}
|
||||
else {
|
||||
console.warn("[boardReducer] Received DisconnectBoardSignal without the client being connected in first place.")
|
||||
return state
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.warn("[boardReducer] Received unknown signal, ignoring:", action)
|
||||
return state
|
||||
|
|
|
@ -4,4 +4,4 @@ import {BoardState} from "@/app/[lang]/board/[board]/(api)/(state)/BoardState"
|
|||
/**
|
||||
* **Object** denoting the {@link BoardState} of a board where no {@link BoardAction}s have been performed.
|
||||
*/
|
||||
export const DEFAULT_BOARD_STATE: BoardState = {title: "", tasksById: {}}
|
||||
export const DEFAULT_BOARD_STATE: BoardState = {title: "", tasksById: {}, connectedClients: []}
|
||||
|
|
|
@ -58,9 +58,6 @@
|
|||
var(--bhsl-background-lightness),
|
||||
100%
|
||||
);
|
||||
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.leftButtonsArea {
|
||||
|
|
|
@ -3,6 +3,7 @@ import {CycleColumningButton} from "@/app/[lang]/board/[board]/(page)/(header)/C
|
|||
import {CycleGroupingButton} from "@/app/[lang]/board/[board]/(page)/(header)/CycleGroupingButton"
|
||||
import {CycleSortingButton} from "@/app/[lang]/board/[board]/(page)/(header)/CycleSortingButton"
|
||||
import {NavigateHomeButton} from "@/app/[lang]/board/[board]/(page)/(header)/NavigateHomeButton"
|
||||
import {ConnectedClientsButton} from "@/app/[lang]/board/[board]/(page)/(header)/ConnectedClientsButton"
|
||||
import {ToggleEditingButton} from "@/app/[lang]/board/[board]/(page)/(header)/ToggleEditingButton"
|
||||
import {ToggleStarredButton} from "@/app/[lang]/board/[board]/(page)/(header)/ToggleStarredButton"
|
||||
import {useBoardLayoutEditor} from "@/app/[lang]/board/[board]/(page)/useBoardLayoutEditor"
|
||||
|
@ -25,7 +26,7 @@ export function BoardHeader({lang, className, metadataHook, layoutHook: {columni
|
|||
<div className={cn(style.buttonsArea, style.leftButtonsArea)}>
|
||||
<NavigateHomeButton lang={lang}/>
|
||||
<ToggleStarredButton lang={lang}/>
|
||||
<ToggleEditingButton lang={lang} metadataHook={metadataHook}/>
|
||||
<ConnectedClientsButton lang={lang}/>
|
||||
</div>
|
||||
<BoardHeaderTitle
|
||||
lang={lang}
|
||||
|
@ -33,6 +34,7 @@ export function BoardHeader({lang, className, metadataHook, layoutHook: {columni
|
|||
editorHook={metadataHook}
|
||||
/>
|
||||
<div className={cn(style.buttonsArea, style.rightButtonsArea)}>
|
||||
<ToggleEditingButton lang={lang} metadataHook={metadataHook}/>
|
||||
<CycleColumningButton lang={lang} value={columningHook.value} next={columningHook.next}/>
|
||||
<CycleGroupingButton lang={lang} next={groupingHook.next}/>
|
||||
<CycleSortingButton lang={lang} next={sortingHook.next}/>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
.block {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
height: 36px;
|
||||
padding: 0.125em 0.75ex;
|
||||
}
|
||||
|
||||
.singleBlock {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.doubleBlock {
|
||||
width: 76px;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import {useClientTranslation} from "@/app/(i18n)/client"
|
||||
import {useBoardConsumer} from "@/app/[lang]/board/[board]/(layout)/(contextBoard)"
|
||||
import style from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeaderButtons.module.css"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import {faUsers} from "@fortawesome/free-solid-svg-icons"
|
||||
import cn from "classnames"
|
||||
|
||||
|
||||
export function ConnectedClientsButton({lang}: {lang: string}) {
|
||||
const {isReady, boardState: {connectedClients}} = useBoardConsumer()
|
||||
const {t} = useClientTranslation(lang, "board")
|
||||
|
||||
if(!isReady) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
title={t("privacyButtonTitle")}
|
||||
className={cn(style.block, style.doubleBlock)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faUsers}/>
|
||||
|
||||
{connectedClients.length}
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import {useClientTranslation} from "@/app/(i18n)/client"
|
||||
import {useBoardConsumer} from "@/app/[lang]/board/[board]/(layout)/(contextBoard)"
|
||||
import style from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeaderButtons.module.css"
|
||||
import cn from "classnames"
|
||||
import {COLUMNING_MODE_TO_ICON, ColumningMode} from "../(view)/(columning)"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
|
||||
|
@ -14,6 +16,7 @@ export function CycleColumningButton({lang, value, next}: {lang: string, value:
|
|||
<button
|
||||
title={t("cycleColumningButtonTitle")}
|
||||
onClick={next}
|
||||
className={cn(style.block, style.singleBlock)}
|
||||
>
|
||||
<FontAwesomeIcon icon={COLUMNING_MODE_TO_ICON[value]}/>
|
||||
</button>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import {useClientTranslation} from "@/app/(i18n)/client"
|
||||
import {useBoardConsumer} from "@/app/[lang]/board/[board]/(layout)/(contextBoard)"
|
||||
import style from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeaderButtons.module.css"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import {faObjectGroup} from "@fortawesome/free-solid-svg-icons"
|
||||
import cn from "classnames"
|
||||
|
||||
|
||||
export function CycleGroupingButton({lang, next}: {lang: string, next: () => void}) {
|
||||
|
@ -14,6 +16,7 @@ export function CycleGroupingButton({lang, next}: {lang: string, next: () => voi
|
|||
<button
|
||||
title={t("cycleGroupingButtonTitle")}
|
||||
onClick={next}
|
||||
className={cn(style.block, style.singleBlock)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faObjectGroup}/>
|
||||
</button>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import {useClientTranslation} from "@/app/(i18n)/client"
|
||||
import {useBoardConsumer} from "@/app/[lang]/board/[board]/(layout)/(contextBoard)"
|
||||
import style from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeaderButtons.module.css"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import {faArrowDownWideShort} from "@fortawesome/free-solid-svg-icons"
|
||||
import cn from "classnames"
|
||||
|
||||
|
||||
export function CycleSortingButton({lang, next}: {lang: string, next: () => void}) {
|
||||
|
@ -14,6 +16,7 @@ export function CycleSortingButton({lang, next}: {lang: string, next: () => void
|
|||
<button
|
||||
title={t("cycleSortingButtonTitle")}
|
||||
onClick={next}
|
||||
className={cn(style.block, style.singleBlock)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowDownWideShort}/>
|
||||
</button>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {useClientTranslation} from "@/app/(i18n)/client"
|
||||
import style from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeaderButtons.module.css"
|
||||
import {faHouse} from "@fortawesome/free-solid-svg-icons"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import cn from "classnames"
|
||||
import {useRouter} from "next/navigation"
|
||||
import {useCallback} from "react"
|
||||
|
||||
|
@ -11,7 +13,11 @@ export function NavigateHomeButton({lang}: {lang: string}) {
|
|||
const goHome = useCallback(() => router.push("/"), [router])
|
||||
|
||||
return (
|
||||
<button title={t("navigateHomeButtonTitle")} onClick={goHome}>
|
||||
<button
|
||||
title={t("navigateHomeButtonTitle")}
|
||||
onClick={goHome}
|
||||
className={cn(style.block, style.singleBlock)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faHouse}/>
|
||||
</button>
|
||||
)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import {useClientTranslation} from "@/app/(i18n)/client"
|
||||
import {useBoardConsumer} from "@/app/[lang]/board/[board]/(layout)/(contextBoard)"
|
||||
import style from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeaderButtons.module.css"
|
||||
import {useBoardMetadataEditor} from "@/app/[lang]/board/[board]/(page)/useBoardMetadataEditor"
|
||||
import {faFloppyDisk, faPencil} from "@fortawesome/free-solid-svg-icons"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import cn from "classnames"
|
||||
|
||||
|
||||
export function ToggleEditingButton({lang, metadataHook}: {lang: string, metadataHook: ReturnType<typeof useBoardMetadataEditor>}) {
|
||||
|
@ -15,6 +17,7 @@ export function ToggleEditingButton({lang, metadataHook}: {lang: string, metadat
|
|||
<button
|
||||
title={metadataHook.isEditingMetadata ? t("stopEditingButtonTitle") : t("startEditingButtonTitle")}
|
||||
onClick={metadataHook.toggleEditingMetadata}
|
||||
className={cn(style.block, style.singleBlock)}
|
||||
>
|
||||
<FontAwesomeIcon icon={metadataHook.isEditingMetadata ? faFloppyDisk : faPencil}/>
|
||||
</button>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import {useClientTranslation} from "@/app/(i18n)/client"
|
||||
import {useStarredConsumer} from "@/app/[lang]/(layout)/(contextStarred)"
|
||||
import {useBoardConsumer} from "@/app/[lang]/board/[board]/(layout)/(contextBoard)"
|
||||
import style from "@/app/[lang]/board/[board]/(page)/(header)/BoardHeaderButtons.module.css"
|
||||
import {faStar as faStarRegular} from "@fortawesome/free-regular-svg-icons"
|
||||
import {faStar as faStarSolid} from "@fortawesome/free-solid-svg-icons"
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
|
||||
import cn from "classnames"
|
||||
|
||||
|
||||
export function ToggleStarredButton({lang}: {lang: string}) {
|
||||
|
@ -16,6 +18,7 @@ export function ToggleStarredButton({lang}: {lang: string}) {
|
|||
<button
|
||||
title={thisIsStarred ? t("removeStarredButtonTitle") : t("addStarredButtonTitle")}
|
||||
onClick={() => toggleStarred(boardName)}
|
||||
className={cn(style.block, style.singleBlock)}
|
||||
>
|
||||
<FontAwesomeIcon icon={thisIsStarred ? faStarSolid : faStarRegular}/>
|
||||
</button>
|
||||
|
|
Loading…
Reference in a new issue