1
Fork 0
mirror of https://github.com/Steffo99/todocolors.git synced 2024-11-22 08:14:18 +00:00

Add starring mechanism

This commit is contained in:
Steffo 2023-08-07 18:40:40 +02:00
parent d750d7949b
commit 567818e95e
Signed by: steffo
GPG key ID: 2A24051445686895
8 changed files with 149 additions and 5 deletions

View file

@ -23,6 +23,7 @@
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"server-only": "^0.0.1", "server-only": "^0.0.1",
"typescript": "5.1.6" "typescript": "5.1.6",
"use-local-storage": "^3.0.0"
} }
} }

View file

@ -1,6 +1,7 @@
import {CreatePrivateBoardPanel} from "@/app/CreatePrivateBoardPanel" import {CreatePrivateBoardPanel} from "@/app/CreatePrivateBoardPanel"
import {CreatePublicBoardPanel} from "@/app/CreatePublicBoardPanel" import {CreatePublicBoardPanel} from "@/app/CreatePublicBoardPanel"
import style from "@/app/page.module.css" import style from "@/app/page.module.css"
import {StarredBoardsPanel} from "@/app/StarredBoardsPanel"
import {default as React} from "react" import {default as React} from "react"
@ -14,6 +15,12 @@ export function RootMain() {
<CreatePublicBoardPanel/> <CreatePublicBoardPanel/>
<CreatePrivateBoardPanel/> <CreatePrivateBoardPanel/>
</div> </div>
<div className={"chapter-1"}>
<h2>
Usa un tabellone già esistente
</h2>
<StarredBoardsPanel/>
</div>
</main> </main>
) )
} }

View file

@ -0,0 +1,57 @@
"use client";
import {createContext, Dispatch, ReactNode, SetStateAction, useCallback, useContext} from "react"
import useLocalStorage from "use-local-storage"
export interface StarContextData {
starred: string[],
setStarred: Dispatch<SetStateAction<string[] | undefined>>
addStarred: (key: string) => void,
removeStarred: (key: string) => void,
}
const StarContext = createContext<StarContextData | null>(null)
export function StarredManager({children}: {children: ReactNode}) {
const [starred, setStarred] = useLocalStorage<string[]>("TODOBLUE_STARRED", [])
const addStarred = useCallback((value: string) => {
setStarred(prev => {
if(!prev) {
return [value]
}
else {
return [...prev, value]
}
})
}, [])
const removeStarred = useCallback((value: string) => {
setStarred(prev => {
if(!prev) {
return []
}
else {
const result = [...prev]
delete result[result.indexOf(value)]
return result
}
})
}, [])
return (
<StarContext.Provider value={{starred, setStarred, addStarred, removeStarred}}>
{children}
</StarContext.Provider>
)
}
export function useManagedStarred(): StarContextData {
const context = useContext(StarContext)
if(context === null) {
throw new Error("useStarContext used outside a StarContext.")
}
return context
}

View file

@ -0,0 +1,52 @@
"use client";
import {useManagedStarred} from "@/app/StarContext"
import cn from "classnames"
import Link from "next/link"
import {useEffect, useState} from "react"
export function StarredBoardsPanel() {
const [isClient, setIsClient] = useState<true | null>(null);
const {starred} = useManagedStarred()
useEffect(() => setIsClient(true), [])
let content;
if(!isClient) {
content = <>
<p>
Sto recuperando i dati salvati sul tuo browser...
</p>
</>
}
else {
content = <>
<p>
Puoi stellare un tabellone cliccando sulla stellina una volta che ci sei dentro.
</p>
{starred.length > 0 ?
<ul>
{starred.map(s => <li key={s}><Link href={`/board/${s}`}><code>{s}</code></Link></li>)}
</ul>
:
<p className={"fade"}>
Non hai ancora stellato nessun tabellone.
</p>
}
</>
}
return (
<div className={cn({
"panel": true,
"box": true,
"fade": !isClient,
})}>
<h3>
Tabelloni stellati
</h3>
{content}
</div>
)
}

View file

@ -1,13 +1,15 @@
import {useManagedStarred} from "@/app/StarContext"
import {useRouter} from "next/navigation" import {useRouter} from "next/navigation"
import {ReactNode, useCallback} from "react" import {ReactNode, useCallback} from "react"
import style from "./BoardHeader.module.css" import style from "./BoardHeader.module.css"
import {useManagedBoard} from "@/app/board/[board]/BoardManager" import {useManagedBoard} from "@/app/board/[board]/BoardManager"
import {faArrowDownWideShort, faHouse, faPencil, faObjectGroup, faTableColumns} from "@fortawesome/free-solid-svg-icons" import {faArrowDownWideShort, faHouse, faPencil, faObjectGroup, faTableColumns, faStar as faStarSolid} from "@fortawesome/free-solid-svg-icons"
import {faStar as faStarRegular} from "@fortawesome/free-regular-svg-icons"
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome" import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"
import cn from "classnames" import cn from "classnames"
export function BoardHeader({className}: {className?: string}) { export function BoardHeader({className}: {boardName: string, className?: string}) {
const {isEditingTitle} = useManagedBoard(); const {isEditingTitle} = useManagedBoard();
return ( return (
@ -17,6 +19,7 @@ export function BoardHeader({className}: {className?: string}) {
</TitleArea> </TitleArea>
<LeftButtonsArea> <LeftButtonsArea>
<HomeButton/> <HomeButton/>
<StarButton/>
<EditTitleButton/> <EditTitleButton/>
</LeftButtonsArea> </LeftButtonsArea>
<RightButtonsArea> <RightButtonsArea>
@ -93,6 +96,20 @@ function HomeButton() {
) )
} }
function StarButton() {
const {name} = useManagedBoard()
const {starred, addStarred, removeStarred} = useManagedStarred()
const isStarred = starred.indexOf(name) >= 0
const toggleStarred = useCallback(() => isStarred ? removeStarred(name) : addStarred(name), [name, isStarred, addStarred, removeStarred])
return (
<button title={"Stella tabellone"} onClick={toggleStarred}>
<FontAwesomeIcon icon={isStarred ? faStarSolid : faStarRegular}/>
</button>
)
}
function EditTitleButton() { function EditTitleButton() {
const {webSocketState, toggleEditingTitle} = useManagedBoard() const {webSocketState, toggleEditingTitle} = useManagedBoard()
@ -116,7 +133,7 @@ function RightButtonsArea({children}: {children: ReactNode}) {
} }
function ToggleSingleColumnButton() { function ToggleSingleColumnButton() {
const {webSocketState, isSingleColumn, setSingleColumn} = useManagedBoard() const {webSocketState, setSingleColumn} = useManagedBoard()
if(webSocketState != WebSocket.OPEN) { if(webSocketState != WebSocket.OPEN) {
return null; return null;

View file

@ -11,6 +11,7 @@ import {useCycleState} from "@/app/useCycleState"
import {Dispatch, SetStateAction, useState} from "react" import {Dispatch, SetStateAction, useState} from "react"
export interface UseBoardReturns { export interface UseBoardReturns {
name: string,
title: string, title: string,
tasksById: {[id: string]: Task}, tasksById: {[id: string]: Task},
taskGroups: TaskGroup[], taskGroups: TaskGroup[],
@ -50,6 +51,7 @@ export function useBoard(name: string): UseBoardReturns {
const [isSingleColumn, setSingleColumn] = useState<boolean>(false) const [isSingleColumn, setSingleColumn] = useState<boolean>(false)
return { return {
name,
title, title,
tasksById, tasksById,
taskGroups, taskGroups,

View file

@ -2,6 +2,7 @@
import "./layout.css"; import "./layout.css";
import {AppBody} from "@/app/AppBody" import {AppBody} from "@/app/AppBody"
import {StarredManager} from "@/app/StarContext"
import type {Metadata as NextMetadata} from "next" import type {Metadata as NextMetadata} from "next"
import {default as React, ReactNode} from "react" import {default as React, ReactNode} from "react"
@ -24,7 +25,9 @@ export default function layout({children}: { children: ReactNode }) {
return ( return (
<html lang="en"> <html lang="en">
<AppBody> <AppBody>
{children} <StarredManager>
{children}
</StarredManager>
</AppBody> </AppBody>
</html> </html>
) )

View file

@ -302,6 +302,11 @@ typescript@5.1.6:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
use-local-storage@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/use-local-storage/-/use-local-storage-3.0.0.tgz#ecf90952374150f0c65baf027eaaf2062a4c20c6"
integrity sha512-wlPNnBCG3ULIJMr5A+dvWqLiPWCfsN1Kwijq+sAhT5yV4ex0u6XmZuNwP+RerIOfzBuz1pwSZuzhZMiluGQHfQ==
watchpack@2.4.0: watchpack@2.4.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"