Skip to content

Commit

Permalink
Simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
raimohanska committed Oct 16, 2023
1 parent 07c8b82 commit 9d14834
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 85 deletions.
1 change: 0 additions & 1 deletion frontend/src/board/BoardView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ export const BoardView = ({
board,
focus,
viewRect,
permissions: boardStore.permissionsProvider,
}}
/>
<ConnectionsView {...{ board, zoom, dispatch, focus, coordinateHelper }} />
Expand Down
76 changes: 26 additions & 50 deletions frontend/src/board/board-permissions.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,39 @@
import * as L from "lonna"
import { AccessLevel, Connection, Id, Item } from "../../../common/src/domain"
import { componentScope } from "harmaja"
import { Connection, Item } from "../../../common/src/domain"

export interface ItemPermissions {
canChangeFont: boolean
canChangeShapeAndColor: boolean
canChangeTextAlign: boolean
canMove: boolean
canLock: boolean
}
export interface ConnectionPermissions {
canMove: boolean
canChangeShapeAndColor: boolean
canLock: boolean
}
export type BoardPermissionsProvider = ReturnType<typeof BoardPermissionsProvider>
export function BoardPermissionsProvider(accessLevel: L.Property<AccessLevel>) {
const api = {
accessLevel,
getItemPermissions(itemId: Id): L.Property<ItemPermissions> {
return L.constant({
canChangeFont: false,
canChangeShapeAndColor: false,
canChangeTextAlign: false,
canMove: false,
})
},
getConnectionPermissions(connectionId: Id): L.Property<ConnectionPermissions> {
return L.constant({
canMove: false,
canChangeShapeAndColor: false,
})
},
everyItemHasPermission<I extends Item>(
p: L.Property<I[]>,
fn: (p: ItemPermissions) => boolean,
scope: L.Scope,
): L.Property<boolean> {
return p
.pipe(
L.flatMapLatest((items) =>
L.view(L.combineAsArray(items.map((i) => api.getItemPermissions(i.id))), (xs) => xs.every(fn)),
),
)
.applyScope(scope)
},
everyConnectionHasPermission(
p: L.Property<Connection[]>,
fn: (p: ConnectionPermissions) => boolean,
scope: L.Scope,
): L.Property<boolean> {
return p
.pipe(
L.flatMapLatest((items) =>
L.view(L.combineAsArray(items.map((i) => api.getConnectionPermissions(i.id))), (xs) =>
xs.every(fn),
),
),
)
.applyScope(scope)
},

export function anyItemHasPermission(items: Item[], f: (p: ItemPermissions) => boolean): boolean {
return items.some((i) => f(getItemPermissions(i)))
}

export function anyConnectionHasPermission(items: Connection[], f: (p: ConnectionPermissions) => boolean): boolean {
return items.some((i) => f(getConnectionPermissions(i)))
}

export function getItemPermissions(item: Item): ItemPermissions {
return {
canChangeFont: false,
canChangeShapeAndColor: false,
canChangeTextAlign: false,
canMove: false,
canLock: true,
}
}
export function getConnectionPermissions(connection: Connection): ConnectionPermissions {
return {
canMove: false,
canChangeShapeAndColor: false,
canLock: true,
}
return api
}
6 changes: 1 addition & 5 deletions frontend/src/board/contextmenu/ContextMenuView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ import { resolveEndpoint } from "../../../../common/src/connection-utils"
import { connectionEndsMenu } from "./connection-ends"
import { textAlignmentsMenu } from "./textAlignments"
import { lockMenu } from "./lock"
import { BoardPermissionsProvider } from "../board-permissions"

export type SubmenuProps = {
focusedItems: L.Property<{ items: Item[]; connections: Connection[] }>
board: L.Property<Board>
dispatch: Dispatch
submenu: L.Atom<SubMenuCreator | null>
permissions: BoardPermissionsProvider
}

export type SubMenuCreator = (props: SubmenuProps) => HarmajaOutput
Expand Down Expand Up @@ -76,13 +74,11 @@ export const ContextMenuView = ({
board,
focus,
viewRect,
permissions,
}: {
dispatch: Dispatch
board: L.Property<Board>
focus: L.Property<BoardFocus>
viewRect: L.Property<Rect>
permissions: BoardPermissionsProvider
}) => {
const focusedItems = L.view(focus, board, (f, b) => {
if (f.status === "dragging" || f.status === "connection-adding" || f.status === "adding")
Expand All @@ -97,7 +93,7 @@ export const ContextMenuView = ({
(i) => i?.id,
).forEach(() => submenu.set(null))

const props = { board, focusedItems, dispatch, submenu, permissions }
const props = { board, focusedItems, dispatch, submenu }
const widgetCreators = [
alignmentsMenu("x", props),
alignmentsMenu("y", props),
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/board/contextmenu/alignments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
VerticalDistributeIcon,
} from "../../components/Icons"
import { SubmenuProps } from "./ContextMenuView"
import { anyItemHasPermission } from "../board-permissions"

const createSubMenuByAxis = (axis: Axis) => (props: SubmenuProps) => {
return <div className={`submenu alignment ${axis}`}>{alignmentsSubMenu(axis, props)}</div>
Expand All @@ -22,11 +23,7 @@ export function alignmentsMenu(axis: Axis, props: SubmenuProps) {
const hasItemsToAlign = L.view(props.focusedItems, (items) => items.items.length > 1)
const hasItemsToDistribute = L.view(props.focusedItems, (items) => items.items.length > 2)
const createSubmenu = createSubMenuByAxis(axis)
const enabled = props.permissions.everyItemHasPermission(
props.focusedItems.pipe(L.map((i) => i.items)),
(p) => p.canMove,
componentScope(),
)
const enabled = L.view(props.focusedItems, (items) => anyItemHasPermission(items.items, (p) => p.canMove))

return L.combine(hasItemsToAlign, hasItemsToDistribute, (hasItemsToAlign, hasItemsToDistribute) => {
return !hasItemsToAlign && !hasItemsToDistribute
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/board/contextmenu/areaTiling.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { componentScope, h } from "harmaja"
import { h } from "harmaja"
import * as L from "lonna"
import { Board, Container, findItem, isContainer, Item } from "../../../../common/src/domain"
import { Board, Container, Item, findItem, isContainer } from "../../../../common/src/domain"
import { TileIcon } from "../../components/Icons"
import { Dispatch } from "../../store/board-store"
import { anyItemHasPermission } from "../board-permissions"
import { contentRect, organizeItems, packableItems } from "../item-organizer"
import { packItems } from "../item-packer"
import { SubmenuProps } from "./ContextMenuView"

export function areaTilingMenu({ board, focusedItems, dispatch, permissions }: SubmenuProps) {
export function areaTilingMenu({ board, focusedItems, dispatch }: SubmenuProps) {
const packables = L.view(focusedItems, (items) => {
if (items.items.length === 1) {
if (isContainer(items.items[0])) return items.items
Expand All @@ -18,7 +18,7 @@ export function areaTilingMenu({ board, focusedItems, dispatch, permissions }: S
}
return []
})
const enabled = permissions.everyItemHasPermission(packables, (p) => p.canMove, componentScope())
const enabled = L.view(packables, (items) => anyItemHasPermission(items, (p) => p.canMove))
const className = enabled.pipe(L.map((e) => (e ? "icon" : "icon disabled")))

return L.view(
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/board/contextmenu/colorsAndShapes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { colorsSubMenu } from "./colors"
import { SubmenuProps } from "./ContextMenuView"
import { getShapeIcon, shapesSubMenu } from "./shapes"
import { disabledColor } from "../../components/UIColors"
import { anyItemHasPermission } from "../board-permissions"

function createSubMenu(props: SubmenuProps) {
return (
Expand All @@ -19,11 +20,7 @@ function createSubMenu(props: SubmenuProps) {
export function colorsAndShapesMenu(props: SubmenuProps) {
const coloredItems = L.view(props.focusedItems, (items) => items.items.filter(isColoredItem))
const representativeColoredItem: L.Property<ColoredItem | null> = L.view(coloredItems, (items) => items[0] || null)
const enabled = props.permissions.everyItemHasPermission(
coloredItems,
(p) => p.canChangeShapeAndColor,
componentScope(),
)
const enabled = L.view(coloredItems, (items) => anyItemHasPermission(items, (p) => p.canChangeShapeAndColor))
return L.view(representativeColoredItem, enabled, (item, enabled) => {
if (!item) return []
const color = NOTE_COLORS.find((c) => c.color === item.color) || NOTE_COLORS[0]
Expand Down
9 changes: 4 additions & 5 deletions frontend/src/board/contextmenu/connection-ends.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,21 @@ import {
ConnectionRightDotIcon,
} from "../../components/Icons"
import { SubmenuProps } from "./ContextMenuView"
import { anyConnectionHasPermission } from "../board-permissions"

const styles: ConnectionEndStyle[] = ["arrow", "black-dot", "none"]
function nextStyle(style: ConnectionEndStyle) {
const i = styles.indexOf(style)
return styles[(i + 1) % styles.length]
}

export function connectionEndsMenu({ board, focusedItems, dispatch, permissions }: SubmenuProps) {
export function connectionEndsMenu({ board, focusedItems, dispatch }: SubmenuProps) {
const connections = L.view(focusedItems, (items) => items.connections)
const singleConnection = L.view(connections, (connections) =>
connections.length === 1 && connections[0].action === "connect" ? connections[0] : null,
)
const enabled = permissions.everyConnectionHasPermission(
connections,
(p) => p.canChangeShapeAndColor,
componentScope(),
const enabled = L.view(connections, (connections) =>
anyConnectionHasPermission(connections, (p) => p.canChangeShapeAndColor),
)
const className = enabled.pipe(L.map((e) => (e ? "icon" : "icon disabled")))

Expand Down
5 changes: 3 additions & 2 deletions frontend/src/board/contextmenu/fontSizes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { isTextItem } from "../../../../common/src/domain"
import { DecreaseFontSizeIcon, IncreaseFontSizeIcon } from "../../components/Icons"
import { SubmenuProps } from "./ContextMenuView"
import { black, disabledColor } from "../../components/UIColors"
import { anyItemHasPermission } from "../board-permissions"

type MenuIconProps = {
onClick: () => void
Expand All @@ -24,10 +25,10 @@ export const MenuIcon = (props: MenuIconProps) => {
)
}

export function fontSizesMenu({ board, focusedItems, dispatch, permissions }: SubmenuProps) {
export function fontSizesMenu({ board, focusedItems, dispatch }: SubmenuProps) {
const textItems = L.view(focusedItems, (items) => items.items.filter(isTextItem))
const anyText = L.view(textItems, (items) => items.length > 0)
const enabled = permissions.everyItemHasPermission(textItems, (p) => p.canChangeFont, componentScope())
const enabled = L.view(textItems, (items) => anyItemHasPermission(items, (p) => p.canChangeFont))
const className = enabled.pipe(L.map((e) => (e ? "icon" : "icon disabled")))

return L.view(anyText, (any) =>
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/board/contextmenu/lock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ import { h } from "harmaja"
import * as L from "lonna"
import { LockIcon } from "../../components/Icons"
import { SubmenuProps } from "./ContextMenuView"
import { anyConnectionHasPermission, anyItemHasPermission } from "../board-permissions"

export function lockMenu({ board, focusedItems, dispatch }: SubmenuProps) {
const enabled = L.view(
focusedItems,
(items) =>
anyItemHasPermission(items.items, (p) => p.canLock) ||
anyConnectionHasPermission(items.connections, (p) => p.canLock),
)
return L.view(
focusedItems,
(ps) => ps.connections.length > 0 || ps.items.length > 0,
Expand All @@ -12,7 +19,11 @@ export function lockMenu({ board, focusedItems, dispatch }: SubmenuProps) {
return show
? [
<div className="icon-group">
<span className="icon" title="Lock item(s)" onClick={() => console.log("TODO")}>
<span
className={L.view(enabled, (e) => (e ? "icon" : "icon disabled"))}
title="Lock item(s)"
onClick={() => console.log("TODO")}
>
<LockIcon />
</span>
</div>,
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/board/contextmenu/textAlignments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import {
} from "../../components/Icons"
import { black, disabledColor } from "../../components/UIColors"
import { SubmenuProps } from "./ContextMenuView"
import { anyItemHasPermission } from "../board-permissions"

export function textAlignmentsMenu({ board, focusedItems, dispatch, permissions }: SubmenuProps) {
export function textAlignmentsMenu({ board, focusedItems, dispatch }: SubmenuProps) {
const textItems = L.view(focusedItems, (items) => items.items.filter(isTextItem))
const allText = L.view(focusedItems, textItems, (f, t) => f.items.length > 0 && t.length === f.items.length)

Expand All @@ -48,7 +49,8 @@ export function textAlignmentsMenu({ board, focusedItems, dispatch, permissions
dispatch({ action: "item.update", boardId: b.id, items: updated })
}

const enabled = permissions.everyItemHasPermission(textItems, (p) => p.canChangeTextAlign, componentScope())
const enabled = L.view(textItems, (items) => anyItemHasPermission(items, (p) => p.canChangeTextAlign))

const className = enabled.pipe(L.map((e) => (e ? "icon" : "icon disabled")))

return L.view(allText, currentHAlign, currentVAlign, (all, ha, va) => {
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/store/board-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { mkBootStrapEvent } from "../../../common/src/migration"
import { BoardLocalStore, LocalStorageBoard } from "./board-local-store"
import { ServerConnection } from "./server-connection"
import { isLoginInProgress, UserSessionState } from "./user-session-store"
import { BoardPermissionsProvider, ItemPermissions, ConnectionPermissions } from "../board/board-permissions"
export type Dispatch = (e: UIEvent) => void
export type BoardStore = ReturnType<typeof BoardStore>
export type BoardAccessStatus =
Expand Down Expand Up @@ -503,16 +502,13 @@ export function BoardStore(
})
}

const permissionsProvider: BoardPermissionsProvider = BoardPermissionsProvider(L.view(state, "accessLevel"))

return {
state,
events,
eventsFromServer: connection.bufferedServerEvents,
dispatch,
canUndo: undoStack.canPop,
canRedo: redoStack.canPop,
permissionsProvider,
}
}

Expand Down

0 comments on commit 9d14834

Please sign in to comment.