urbit/pkg/npm/api/groups/lib.ts

227 lines
5.0 KiB
TypeScript
Raw Normal View History

import { deSig } from '../index';
2021-02-25 06:54:00 +03:00
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';
2021-04-22 17:17:39 +03:00
export const GROUP_UPDATE_VERSION = 0;
export const proxyAction = <T>(data: T, version: number = GROUP_UPDATE_VERSION): Poke<T> => ({
2021-02-25 06:54:00 +03:00
app: 'group-push-hook',
2021-04-22 17:17:39 +03:00
mark: `group-update-${version}`,
2021-02-25 06:54:00 +03:00
json: data
});
2021-04-22 17:17:39 +03:00
const storeAction = <T extends GroupUpdate>(data: T, version: number = GROUP_UPDATE_VERSION): Poke<T> => ({
2021-02-25 06:54:00 +03:00
app: 'group-store',
2021-04-22 17:17:39 +03:00
mark: `group-update-${version}`,
2021-02-25 06:54:00 +03:00
json: data
});
export { storeAction as groupStoreAction };
const viewAction = <T>(data: T): Poke<T> => ({
app: 'group-view',
mark: 'group-view-action',
json: data
});
export { viewAction as groupViewAction };
export const viewThread = <T>(thread: string, action: T): Thread<T> => ({
inputMark: 'group-view-action',
outputMark: 'json',
threadName: thread,
body: action
});
export const removeMembers = (
resource: Resource,
ships: PatpNoSig[]
): Poke<GroupUpdateRemoveMembers> => proxyAction({
removeMembers: {
resource,
ships
}
});
export const addTag = (
resource: Resource,
tag: Tag,
ships: Patp[]
): Poke<GroupUpdateAddTag> => proxyAction({
addTag: {
resource,
tag,
ships
}
});
export const removeTag = (
tag: Tag,
resource: Resource,
ships: PatpNoSig[]
): Poke<GroupUpdateRemoveTag> => proxyAction({
removeTag: {
tag,
resource,
ships
}
});
export const addMembers = (
resource: Resource,
ships: PatpNoSig[]
): Poke<GroupUpdateAddMembers> => proxyAction({
addMembers: {
resource,
ships
}
});
export const removeGroup = (
resource: Resource
): Poke<GroupUpdateRemoveGroup> => storeAction({
removeGroup: {
resource
}
});
export const changePolicy = (
resource: Resource,
2021-06-09 04:42:27 +03:00
diff: Enc<GroupPolicyDiff>
): Poke<Enc<GroupUpdateChangePolicy>> => proxyAction({
2021-02-25 06:54:00 +03:00
changePolicy: {
resource,
diff
}
});
export const join = (
ship: string,
2021-11-18 00:25:24 +03:00
name: string,
app: "groups" | "graph",
autojoin: boolean,
share: boolean
2021-02-25 06:54:00 +03:00
): Poke<any> => viewAction({
join: {
resource: makeResource(ship, name),
2021-11-18 00:25:24 +03:00
ship,
shareContact: share || false,
app,
autojoin
2021-02-25 06:54:00 +03:00
}
});
export const createGroup = (
name: string,
policy: Enc<GroupPolicy>,
title: string,
description: string
): Thread<any> => viewThread('group-create', {
create: {
name,
policy,
title,
description
}
});
export const deleteGroup = (
ship: string,
name: string
): Thread<any> => viewThread('group-delete', {
remove: makeResource(ship, name)
});
export const leaveGroup = (
ship: string,
name: string
): Thread<any> => viewThread('group-leave', {
leave: makeResource(ship, name)
});
export const invite = (
ship: string,
name: string,
ships: Patp[],
description: string
): Thread<any> => viewThread('group-invite', {
invite: {
resource: makeResource(ship, name),
ships,
description
}
});
2021-11-18 00:25:24 +03:00
export const abortJoin = (
2021-04-22 17:17:39 +03:00
resource: string
): Poke<any> => viewAction({
2021-11-18 00:25:24 +03:00
abort: resource
2021-04-22 17:17:39 +03:00
});
2021-02-25 06:54:00 +03:00
export const roleTags = ['janitor', 'moderator', 'admin'];
// TODO make this type better?
export const groupBunts = {
group: (): Group => ({ members: [], tags: { role: {} }, hidden: false, policy: groupBunts.policy() }),
policy: (): GroupPolicy => ({ open: { banned: [], banRanks: [] } })
2021-02-25 06:54:00 +03:00
};
2021-12-07 19:02:23 +03:00
export const joinError = ['no-perms', 'strange', 'abort'] as const;
2021-02-25 06:54:00 +03:00
export const joinResult = ['done', ...joinError] as const;
2021-11-18 00:25:24 +03:00
export const joinLoad = ['start', 'added', 'metadata'] as const;
export const joinProgress = [...joinLoad, ...joinResult] as const;
2021-02-25 06:54:00 +03:00
export function roleForShip(
2021-02-25 06:54:00 +03:00
group: Group,
ship: PatpNoSig
): RoleTags | undefined {
2021-02-25 06:54:00 +03:00
return roleTags.reduce((currRole, role) => {
const roleShips = group?.tags?.role?.[role];
return roleShips && roleShips.includes(ship) ? role : currRole;
2021-02-25 06:54:00 +03:00
}, undefined as RoleTags | undefined);
};
2021-02-25 06:54:00 +03:00
export function resourceFromPath(path: Path): Resource {
2021-02-25 06:54:00 +03:00
const [, , ship, name] = path.split('/');
return { ship, name };
}
export function makeResource(ship: string, name: string) {
2021-02-25 06:54:00 +03:00
return { ship, name };
}
export const isWriter = (group: Group, resource: string, ship: string) => {
const graph = group.tags?.graph;
const writers: string[] | undefined = graph && (graph[resource] as any)?.writers;
const admins = group?.tags?.role?.admin ?? [];
if (typeof writers === 'undefined') {
2021-02-25 06:54:00 +03:00
return true;
} else {
return writers.includes(ship) || admins.includes(ship);
2021-02-25 06:54:00 +03:00
}
};
2021-02-25 06:54:00 +03:00
export function isChannelAdmin(
2021-02-25 06:54:00 +03:00
group: Group,
resource: string,
ship: string
): boolean {
const role = roleForShip(group, deSig(ship));
2021-02-25 06:54:00 +03:00
return (
isHost(resource, ship) ||
role === 'admin' ||
role === 'moderator'
);
}
export function isHost(
2021-02-25 06:54:00 +03:00
resource: string,
ship: string
): boolean {
2021-02-25 06:54:00 +03:00
const [, , host] = resource.split('/');
return ship === host;
}