import { Enc, Path, Patp, PatpNoSig, Poke, Thread } from '../lib/types'; import { Group, GroupPolicy, GroupPolicyDiff, GroupUpdateAddMembers, GroupUpdateAddTag, GroupUpdateChangePolicy, GroupUpdateRemoveGroup, GroupUpdateRemoveMembers, GroupUpdateRemoveTag, Resource, RoleTags, Tag } from './types'; import { GroupUpdate } from './update'; export const GROUP_UPDATE_VERSION = 0; export const proxyAction = (data: T, version: number = GROUP_UPDATE_VERSION): Poke => ({ app: 'group-push-hook', mark: `group-update-${version}`, json: data }); const storeAction = (data: T, version: number = GROUP_UPDATE_VERSION): Poke => ({ app: 'group-store', mark: `group-update-${version}`, json: data }); export { storeAction as groupStoreAction }; const viewAction = (data: T): Poke => ({ app: 'group-view', mark: 'group-view-action', json: data }); export { viewAction as groupViewAction }; export const viewThread = (thread: string, action: T): Thread => ({ inputMark: 'group-view-action', outputMark: 'json', threadName: thread, body: action }); export const removeMembers = ( resource: Resource, ships: PatpNoSig[] ): Poke => proxyAction({ removeMembers: { resource, ships } }); export const addTag = ( resource: Resource, tag: Tag, ships: Patp[] ): Poke => proxyAction({ addTag: { resource, tag, ships } }); export const removeTag = ( tag: Tag, resource: Resource, ships: PatpNoSig[] ): Poke => proxyAction({ removeTag: { tag, resource, ships } }); export const addMembers = ( resource: Resource, ships: PatpNoSig[] ): Poke => proxyAction({ addMembers: { resource, ships } }); export const removeGroup = ( resource: Resource ): Poke => storeAction({ removeGroup: { resource } }); export const changePolicy = ( resource: Resource, diff: Enc ): Poke> => proxyAction({ changePolicy: { resource, diff } }); export const makeResource = (ship: string, name: string) => { return { ship, name }; }; export const join = ( ship: string, name: string ): Poke => viewAction({ join: { resource: makeResource(ship, name), ship } }); export const createGroup = ( name: string, policy: Enc, title: string, description: string ): Thread => viewThread('group-create', { create: { name, policy, title, description } }); export const deleteGroup = ( ship: string, name: string ): Thread => viewThread('group-delete', { remove: makeResource(ship, name) }); export const leaveGroup = ( ship: string, name: string ): Thread => viewThread('group-leave', { leave: makeResource(ship, name) }); export const invite = ( ship: string, name: string, ships: Patp[], description: string ): Thread => viewThread('group-invite', { invite: { resource: makeResource(ship, name), ships, description } }); export const hideGroup = ( resource: string ): Poke => viewAction({ hide: resource }); export const roleTags = ['janitor', 'moderator', 'admin']; // TODO make this type better? export const groupBunts = { group: (): Group => ({ members: new Set(), tags: { role: {} }, hidden: false, policy: groupBunts.policy() }), policy: (): GroupPolicy => ({ open: { banned: new Set(), banRanks: new Set() } }) }; export const joinError = ['no-perms', 'strange'] as const; export const joinResult = ['done', ...joinError] as const; export const joinProgress = ['start', 'added', ...joinResult] as const; export const roleForShip = ( group: Group, ship: PatpNoSig ): RoleTags | undefined => { return roleTags.reduce((currRole, role) => { const roleShips = group?.tags?.role?.[role]; return roleShips && roleShips.has(ship) ? role : currRole; }, undefined as RoleTags | undefined); }; export const resourceFromPath = (path: Path): Resource => { const [, , ship, name] = path.split('/'); return { ship, name }; }; export const isWriter = (group: Group, resource: string, ship: string) => { const graph = group.tags?.graph; const writers: Set | undefined = graph && (graph[resource] as any)?.writers; const admins = group?.tags?.role?.admin ?? new Set(); if (typeof writers === 'undefined') { return true; } else { return writers.has(ship) || admins.has(ship); } }; export const isHost = ( resource: string, ship: string ): boolean => { const [, , host] = resource.split('/'); return ship === host; }; export const isChannelAdmin = ( group: Group, resource: string, ship: string ): boolean => { const role = roleForShip(group, ship.slice(1)); return ( isHost(resource, ship) || role === 'admin' || role === 'moderator' ); };