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

219 lines
4.8 KiB
TypeScript
Raw Normal View History

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 makeResource = (ship: string, name: string) => {
return { ship, name };
};
2021-02-25 06:54:00 +03:00
export const join = (
ship: string,
name: string
): Poke<any> => viewAction({
join: {
resource: makeResource(ship, name),
ship
}
});
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-06-09 04:42:27 +03:00
export const hideGroup = (
2021-04-22 17:17:39 +03:00
resource: string
): Poke<any> => viewAction({
hide: resource
});
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: 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);
};
2021-02-25 06:54:00 +03:00
export const resourceFromPath = (path: Path): Resource => {
const [, , ship, name] = path.split('/');
return { ship, name };
};
2021-02-25 06:54:00 +03:00
export const isWriter = (group: Group, resource: string, ship: string) => {
const graph = group.tags?.graph;
const writers: Set<string> | undefined = graph && (graph[resource] as any)?.writers;
2021-02-25 06:54:00 +03:00
const admins = group?.tags?.role?.admin ?? new Set();
if (typeof writers === 'undefined') {
2021-02-25 06:54:00 +03:00
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;
};
2021-02-25 06:54:00 +03:00
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'
);
};