import _ from 'lodash'; 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 join = ( ship: string, name: string, app: "groups" | "graph", autojoin: boolean, share: boolean ): Poke => viewAction({ join: { resource: makeResource(ship, name), ship, shareContact: share || false, app, autojoin } }); 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 abortJoin = ( resource: string ): Poke => viewAction({ abort: 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 joinLoad = ['start', 'added', 'metadata'] as const; export const joinProgress = [...joinLoad, ...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 makeResource = (ship: string, name: string) => { return { ship, name }; } export const isWriter = (group: Group, resource: string, ship: string) => { const writers: Set | undefined = _.get( group, ['tags', 'graph', resource, 'writers'], undefined ); const admins = group?.tags?.role?.admin ?? new Set(); if (_.isUndefined(writers)) { return true; } else { return writers.has(ship) || admins.has(ship); } } 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' ); } export const isHost = ( resource: string, ship: string ): boolean => { const [, , host] = resource.split('/'); return ship === host; }