@things-factory/announcement 10.1.248 → 10.1.250
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/announcement-watch.d.ts +6 -0
- package/dist-client/announcement-watch.js +105 -0
- package/dist-client/announcement-watch.js.map +1 -0
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +8 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/components/announcement-popup.d.ts +20 -0
- package/dist-client/components/announcement-popup.js +98 -0
- package/dist-client/components/announcement-popup.js.map +1 -0
- package/dist-client/components/announcement-receipts.d.ts +30 -0
- package/dist-client/components/announcement-receipts.js +220 -0
- package/dist-client/components/announcement-receipts.js.map +1 -0
- package/dist-client/components/announcement-sheet.d.ts +17 -0
- package/dist-client/components/announcement-sheet.js +224 -0
- package/dist-client/components/announcement-sheet.js.map +1 -0
- package/dist-client/components/audience-picker.d.ts +32 -0
- package/dist-client/components/audience-picker.js +229 -0
- package/dist-client/components/audience-picker.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/announcement-editor.d.ts +59 -0
- package/dist-client/pages/announcement-editor.js +523 -0
- package/dist-client/pages/announcement-editor.js.map +1 -0
- package/dist-client/pages/announcement-list.d.ts +54 -0
- package/dist-client/pages/announcement-list.js +209 -0
- package/dist-client/pages/announcement-list.js.map +1 -0
- package/dist-client/pages/announcement-manage-list.d.ts +46 -0
- package/dist-client/pages/announcement-manage-list.js +115 -0
- package/dist-client/pages/announcement-manage-list.js.map +1 -0
- package/dist-client/pages/announcement-view.d.ts +30 -0
- package/dist-client/pages/announcement-view.js +182 -0
- package/dist-client/pages/announcement-view.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +18 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/utils/announcement-api.d.ts +149 -0
- package/dist-client/utils/announcement-api.js +52 -0
- package/dist-client/utils/announcement-api.js.map +1 -0
- package/dist-client/utils/announcement-badge.d.ts +4 -0
- package/dist-client/utils/announcement-badge.js +47 -0
- package/dist-client/utils/announcement-badge.js.map +1 -0
- package/dist-client/utils/announcement-words.d.ts +11 -0
- package/dist-client/utils/announcement-words.js +23 -0
- package/dist-client/utils/announcement-words.js.map +1 -0
- package/dist-server/announcement-read.d.ts +6 -0
- package/dist-server/announcement-read.js +45 -0
- package/dist-server/announcement-read.js.map +1 -0
- package/dist-server/attachment-reader.d.ts +1 -0
- package/dist-server/attachment-reader.js +13 -0
- package/dist-server/attachment-reader.js.map +1 -0
- package/dist-server/index.d.ts +2 -0
- package/dist-server/index.js +20 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +13 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/service/announcement/announcement-resolver.d.ts +38 -0
- package/dist-server/service/announcement/announcement-resolver.js +664 -0
- package/dist-server/service/announcement/announcement-resolver.js.map +1 -0
- package/dist-server/service/announcement/announcement-rules.d.ts +150 -0
- package/dist-server/service/announcement/announcement-rules.js +227 -0
- package/dist-server/service/announcement/announcement-rules.js.map +1 -0
- package/dist-server/service/announcement/announcement-service.d.ts +54 -0
- package/dist-server/service/announcement/announcement-service.js +248 -0
- package/dist-server/service/announcement/announcement-service.js.map +1 -0
- package/dist-server/service/announcement/announcement-types.d.ts +54 -0
- package/dist-server/service/announcement/announcement-types.js +203 -0
- package/dist-server/service/announcement/announcement-types.js.map +1 -0
- package/dist-server/service/announcement/announcement.d.ts +124 -0
- package/dist-server/service/announcement/announcement.js +462 -0
- package/dist-server/service/announcement/announcement.js.map +1 -0
- package/dist-server/service/announcement/index.d.ts +4 -0
- package/dist-server/service/announcement/index.js +8 -0
- package/dist-server/service/announcement/index.js.map +1 -0
- package/dist-server/service/index.d.ts +7 -0
- package/dist-server/service/index.js +23 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +8 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"announcement-resolver.js","sourceRoot":"","sources":["../../../server/service/announcement/announcement-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The doors of announcements (ADR-0163 decisions 2-5).\n *\n * Reading is not a privilege: an announcement is read by the people it names, so the reader's doors are open to anyone\n * signed in to the domain and each asks the audience itself (announcement-service.ts). Writing, managing and the\n * reading record of others are `announcement:mutation` - the module's task `announcement.writing`.\n */\nimport type { FileUpload } from 'graphql-upload/GraphQLUpload.js'\nimport GraphQLUpload from 'graphql-upload/GraphQLUpload.js'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Int, Mutation, Query, Resolver, Root } from 'type-graphql'\nimport { In, IsNull, MoreThan, Not } from 'typeorm'\n\nimport { Attachment, createAttachment, deleteAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'\nimport { getDomainAssignees, User } from '@things-factory/auth-base'\nimport { audienceTargetRefusal, audienceUserIds, Department, departmentTreeOf, domainRolesOf } from '@things-factory/organization'\nimport { getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'\n\nimport { Announcement, AnnouncementAudience, AnnouncementImportance, AnnouncementReceipt, AnnouncementRevision, AnnouncementStatus } from './announcement'\nimport { inputRefusal, isCurrent, owedReminder, periodOf, popupDue, publishedStatusOf, publishRefusal, rankOf, readerNarrowingOf, readerStateOf } from './announcement-rules'\nimport {\n alignRoster,\n ANNOUNCEMENT_REF_TYPE,\n audiencesOf,\n isWriter,\n keepRevision,\n memberOf,\n reachingIds,\n sendOut,\n tellWhenCommitted,\n visibleAnnouncement\n} from './announcement-service'\nimport {\n AnnouncementAudienceChoices,\n AnnouncementAudienceInput,\n AnnouncementChoice,\n AnnouncementInput,\n AnnouncementList,\n AnnouncementReceiptRow,\n AnnouncementSummary\n} from './announcement-types'\n\nconst WRITE = '@privilege(category: \"announcement\", privilege: \"mutation\", domainOwnerGranted: true)'\n\n/** A refusal said in the reader's words, with a code the screen may read. */\nfunction refuse(context: ResolverContext, code: string, fallback: string): never {\n const message = context.t ? String(context.t(`announcement.refusal.${code}`, { defaultValue: fallback })) : fallback\n const error: any = new Error(message)\n error.extensions = { code: `announcement.${code}` }\n throw error\n}\n\nconst NOT_FOUND = 'there is no such announcement.'\n\n/** The announcement the request may see, refused otherwise. */\nasync function visibleOrRefuse(id: string, context: ResolverContext, tx?: any) {\n const visible = await visibleAnnouncement(id, context, tx)\n if (!visible) refuse(context, 'unknown-announcement', NOT_FOUND)\n return visible\n}\n\n/** One the writer may change: any that is not somebody else's draft, and not withdrawn. */\nasync function changeable(id: string, context: ResolverContext, tx: any): Promise<Announcement> {\n const visible = await visibleOrRefuse(id, context, tx)\n if (!visible.writer) refuse(context, 'unknown-announcement', NOT_FOUND)\n if (visible.announcement.status === AnnouncementStatus.WITHDRAWN) refuse(context, 'withdrawn', 'a withdrawn announcement is not changed.')\n return visible.announcement\n}\n\n/** The fields of the input as stored, checked against the rules. */\nfunction fieldsOf(input: AnnouncementInput, context: ResolverContext) {\n const refusal = inputRefusal(input)\n if (refusal) refuse(context, refusal, `the announcement cannot be kept (${refusal}).`)\n const importance = (input.importance as AnnouncementImportance) || AnnouncementImportance.NORMAL\n const pinned = !!input.pinned\n return {\n title: input.title.trim(),\n body: input.body ?? null,\n category: input.category || 'GENERAL',\n importance,\n startsAt: input.startsAt ?? null,\n endsAt: input.endsAt ?? null,\n pinned,\n rank: rankOf({ pinned, importance }),\n requiresAcknowledgement: !!input.requiresAcknowledgement,\n acknowledgeBy: input.requiresAcknowledgement ? (input.acknowledgeBy ?? null) : null,\n popup: !!input.popup,\n sendEmail: !!input.sendEmail,\n links: (input.links ?? []).map(({ label, url }) => ({ label: label.trim(), url: url.trim() }))\n }\n}\n\n/** Sets the whole audience - each target checked against the domain - and answers whether it changed. */\nasync function setAudiences(announcement: Announcement, wanted: AnnouncementAudienceInput[], context: ResolverContext, tx: any): Promise<boolean> {\n const { domain, user } = context.state\n const scopes = new Set(wanted.map(one => one.scope))\n const departmentIds = scopes.has('DEPARTMENT') ? [...(await departmentTreeOf(domain, tx)).keys()] : []\n const roleIds = scopes.has('ROLE') ? (await domainRolesOf(domain, undefined, tx)).map(one => one.id) : []\n const people = scopes.has('USER') ? new Set((await getDomainAssignees(domain as any, tx)).map(one => one.id)) : new Set<string>()\n const byKey = new Map<string, AnnouncementAudienceInput>()\n for (const target of wanted) {\n const refusal = audienceTargetRefusal(target, { domainId: domain.id, departmentIds, roleIds }) ?? (target.scope === 'USER' && !people.has(target.targetId) ? 'unknown-person' : undefined)\n if (refusal) refuse(context, `audience-${refusal}`, `this target cannot be given (${refusal}).`)\n byKey.set(`${target.scope}:${target.targetId}`, target)\n }\n const repository = getRepository(AnnouncementAudience, tx)\n const current = await audiencesOf(announcement.id, tx)\n const gone = current.filter(one => !byKey.has(`${one.scope}:${one.targetId}`))\n if (gone.length) await repository.softRemove(gone)\n let added = 0\n for (const [key, target] of byKey) {\n if (current.some(one => `${one.scope}:${one.targetId}` === key)) continue\n added++\n await repository.save({ domain, announcement: { id: announcement.id }, scope: target.scope, targetId: target.targetId, targetName: target.targetName?.slice(0, 100), creator: user, updater: user } as any)\n }\n return gone.length + added > 0\n}\n\nasync function attachFiles(announcementId: string, attach: FileUpload[] | undefined, remove: string[] | undefined, context: ResolverContext) {\n for (const id of remove ?? []) {\n const file = await getRepository(Attachment, context.state.tx).findOne({ where: { id, domain: { id: context.state.domain.id }, refType: ANNOUNCEMENT_REF_TYPE, refBy: announcementId } as any })\n if (file) await deleteAttachment(null, { id }, context)\n }\n for (const file of attach ?? []) {\n await createAttachment(null, { attachment: { file, refType: ANNOUNCEMENT_REF_TYPE, refBy: announcementId } } as any, context)\n }\n}\n\n/** The reader's own receipts of `ids`, by announcement. */\nasync function myReceipts(ids: string[], userId: string, tx?: any): Promise<Map<string, AnnouncementReceipt>> {\n if (!ids.length) return new Map()\n const rows = await getRepository(AnnouncementReceipt, tx).find({ where: { announcement: { id: In(ids) }, user: { id: userId } } as any })\n return new Map(rows.map(one => [one.announcementId!, one]))\n}\n\n/** The current announcements addressed to the request's person, with their receipts. */\nasync function currentForReader(context: ResolverContext) {\n const { domain, user } = context.state\n const ids = await reachingIds(await memberOf(context))\n if (!ids.length) return { items: [] as Announcement[], receipts: new Map<string, AnnouncementReceipt>() }\n const now = new Date()\n const items = await getRepository(Announcement).find({\n where: [\n { id: In(ids), domain: { id: domain.id }, status: AnnouncementStatus.PUBLISHED, endsAt: IsNull() },\n { id: In(ids), domain: { id: domain.id }, status: AnnouncementStatus.PUBLISHED, endsAt: MoreThan(now) }\n ] as any,\n order: { rank: 'ASC', publishedAt: 'DESC' } as any\n })\n return { items, receipts: await myReceipts(items.map(one => one.id), user.id) }\n}\n\n@Resolver(Announcement)\nexport class AnnouncementResolver {\n /* ---------------------------------------------------------------- reading - the audience */\n\n @Query(returns => AnnouncementList, {\n description:\n \"The announcements addressed to the request's person that are out. Two filters are the reader's own: `period` (current | past) and `readerState` (unread, read, to-acknowledge, acknowledged - one or several, with `in`). The current ones come pinned, urgent and important first; otherwise the newest first.\"\n })\n async announcements(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<AnnouncementList> {\n const { domain, user } = context.state\n const { rest, period, states, refusal } = readerNarrowingOf(params.filters as any)\n if (refusal) refuse(context, refusal, `the list cannot be narrowed so (${refusal}).`)\n let ids = await reachingIds(await memberOf(context))\n if (states && ids.length) {\n const candidates = await getRepository(Announcement).find({ where: { id: In(ids), status: AnnouncementStatus.PUBLISHED } as any })\n const receipts = await myReceipts(\n candidates.map(one => one.id),\n user.id\n )\n ids = candidates.filter(one => states.includes(readerStateOf(one, receipts.get(one.id)))).map(one => one.id)\n }\n if (!ids.length) return { items: [], total: 0 }\n const now = new Date()\n const qb = getQueryBuilderFromListParams({\n domain,\n params: { ...params, filters: rest as any, sortings: [] },\n repository: getRepository(Announcement),\n alias: 'A',\n searchables: ['title', 'body', 'category']\n })\n .andWhere('A.id IN (:...ids)', { ids })\n .andWhere('A.status = :published', { published: AnnouncementStatus.PUBLISHED })\n if (period === 'past') qb.andWhere('A.endsAt <= :now', { now })\n if (period === 'current') qb.andWhere('(A.endsAt IS NULL OR A.endsAt > :now)', { now }).orderBy('A.rank', 'ASC').addOrderBy('A.publishedAt', 'DESC')\n else qb.orderBy('A.publishedAt', 'DESC')\n const [items, total] = await qb.getManyAndCount()\n const receipts = await myReceipts(\n items.map(one => one.id),\n user.id\n )\n for (const one of items) (one as any).__mine = receipts.get(one.id) ?? null\n return { items, total }\n }\n\n @Query(returns => Announcement, { nullable: true, description: 'One announcement - to its audience once it is out, to a writer whatever its status (not somebody else\\'s draft).' })\n async announcement(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Announcement | undefined> {\n return (await visibleAnnouncement(id, context))?.announcement\n }\n\n @Query(returns => AnnouncementSummary, { description: 'How many current announcements addressed to the person are unread, and how many await their acknowledgement.' })\n async announcementSummary(@Ctx() context: ResolverContext): Promise<AnnouncementSummary> {\n const { items, receipts } = await currentForReader(context)\n let unread = 0\n let toAcknowledge = 0\n let pending = 0\n for (const one of items) {\n const state = readerStateOf(one, receipts.get(one.id))\n if (state === 'unread') unread++\n if (one.requiresAcknowledgement && state !== 'acknowledged') toAcknowledge++\n if (state === 'unread' || state === 'to-acknowledge') pending++\n }\n return { unread, toAcknowledge, pending }\n }\n\n @Query(returns => [Announcement], { description: 'The announcements that pop up for the person now - unread popup ones, and those still to be acknowledged.' })\n async announcementPopups(@Ctx() context: ResolverContext): Promise<Announcement[]> {\n const { items, receipts } = await currentForReader(context)\n const now = new Date()\n return items.filter(one => popupDue(one, receipts.get(one.id), now)).map(one => Object.assign(one, { __mine: receipts.get(one.id) ?? null }))\n }\n\n @Directive('@transaction')\n @Mutation(returns => Announcement, { description: 'Records that the person read the announcement - once; reading it again changes nothing.' })\n async readAnnouncement(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Announcement> {\n return await this.receive(id, context, false)\n }\n\n @Directive('@transaction')\n @Mutation(returns => Announcement, { description: 'Records that the person acknowledged an announcement that asks for it - reading it too.' })\n async acknowledgeAnnouncement(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Announcement> {\n return await this.receive(id, context, true)\n }\n\n private async receive(id: string, context: ResolverContext, acknowledge: boolean): Promise<Announcement> {\n const { domain, user, tx } = context.state as any\n const visible = await visibleOrRefuse(id, context, tx)\n /* Only its audience reads it into the record - a writer looking at one addressed to others leaves no trace. */\n if (!visible.reader) return visible.announcement\n if (acknowledge && !visible.announcement.requiresAcknowledgement) refuse(context, 'no-acknowledgement', 'this announcement does not ask to be acknowledged.')\n const repository = getRepository(AnnouncementReceipt, tx)\n const now = new Date()\n const receipt = await repository.findOne({ where: { announcement: { id }, user: { id: user.id } } as any })\n const patch: any = {}\n if (!receipt?.readAt) patch.readAt = now\n if (acknowledge && !receipt?.acknowledgedAt) patch.acknowledgedAt = now\n if (!receipt) await repository.save({ domain, announcement: { id }, user: { id: user.id }, listed: false, ...patch, creator: user, updater: user } as any)\n else if (Object.keys(patch).length) await repository.update(receipt.id, { ...patch, updater: user })\n return visible.announcement\n }\n\n /* ---------------------------------------------------------------- writing and managing */\n\n @Directive(WRITE)\n @Query(returns => AnnouncementList, { description: \"Every announcement of the domain for its writers - all that are out, scheduled or withdrawn, and the writer's own drafts.\" })\n async managedAnnouncements(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<AnnouncementList> {\n const { domain, user } = context.state\n const qb = getQueryBuilderFromListParams({\n domain,\n params,\n repository: getRepository(Announcement),\n alias: 'A',\n searchables: ['title', 'body', 'category']\n }).andWhere('(A.status != :draft OR A.creator = :me)', { draft: AnnouncementStatus.DRAFT, me: user.id })\n if (!params.sortings?.length) qb.orderBy('A.updatedAt', 'DESC')\n const [items, total] = await qb.getManyAndCount()\n return { items, total }\n }\n\n @Directive(WRITE)\n @Query(returns => [AnnouncementReceiptRow], { description: 'Who on the roster read and acknowledged it, and who came to it later - for its writers.' })\n async announcementReceipts(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<AnnouncementReceiptRow[]> {\n const visible = await visibleOrRefuse(id, context)\n if (!visible.writer) refuse(context, 'unknown-announcement', NOT_FOUND)\n const receipts = await getRepository(AnnouncementReceipt).find({ where: { announcement: { id } } as any, relations: ['user'] })\n return receipts\n .map(one => ({\n userId: one.userId!,\n name: one.user?.name,\n email: one.user?.email,\n listed: one.listed,\n notifiedAt: one.notifiedAt,\n readAt: one.readAt,\n acknowledgedAt: one.acknowledgedAt\n }))\n .sort((a, b) => Number(!!a.readAt) - Number(!!b.readAt) || String(a.name ?? '').localeCompare(String(b.name ?? '')))\n }\n\n @Directive(WRITE)\n @Query(returns => [AnnouncementRevision], { description: 'The versions of an announcement since it went out, newest first - for its writers.' })\n async announcementRevisions(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<AnnouncementRevision[]> {\n const visible = await visibleOrRefuse(id, context)\n if (!visible.writer) refuse(context, 'unknown-announcement', NOT_FOUND)\n return await getRepository(AnnouncementRevision).find({ where: { announcement: { id } } as any, order: { version: 'DESC' } as any, relations: ['creator'] })\n }\n\n @Directive(WRITE)\n @Query(returns => AnnouncementAudienceChoices, { description: 'What an announcement may be addressed to - the domain, its departments and its roles.' })\n async announcementAudienceChoices(@Ctx() context: ResolverContext): Promise<AnnouncementAudienceChoices> {\n const domain: any = context.state.domain\n const tree = await departmentTreeOf(domain)\n const departments = tree.size ? await getRepository(Department).find({ where: { id: In([...tree.keys()]) } as any, order: { name: 'ASC' } as any }) : []\n const nameOf = new Map(departments.map(one => [one.id, one.name]))\n return {\n domainId: domain.id,\n domainName: domain.name ?? domain.subdomain ?? domain.id,\n departments: departments.map(one => ({ id: one.id, name: one.name, note: nameOf.get(tree.get(one.id) ?? '') ?? undefined })),\n roles: (await domainRolesOf(domain)).map(one => ({ id: one.id, name: one.name, note: one.description ?? undefined }))\n }\n }\n\n @Directive(WRITE)\n @Query(returns => [AnnouncementChoice], { description: 'People of the domain an announcement may be addressed to, found by name or e-mail.' })\n async announcementPeople(@Arg('text') text: string, @Ctx() context: ResolverContext): Promise<AnnouncementChoice[]> {\n const words = (text ?? '').trim().toLowerCase()\n if (!words) return []\n return (await getDomainAssignees(context.state.domain as any))\n .filter(one => `${one.name ?? ''} ${one.email ?? ''}`.toLowerCase().includes(words))\n .slice(0, 20)\n .map(one => ({ id: one.id, name: one.name || one.email, note: one.email }))\n }\n\n @Directive(WRITE)\n @Query(returns => Int, { description: 'How many people an audience names now - shown before it is sent.' })\n async announcementAudienceSize(@Arg('audiences', type => [AnnouncementAudienceInput]) audiences: AnnouncementAudienceInput[], @Ctx() context: ResolverContext): Promise<number> {\n return (await audienceUserIds(context.state.domain, audiences ?? [])).length\n }\n\n @Directive(WRITE)\n @Directive('@transaction')\n @Mutation(returns => Announcement, { description: 'Writes a new announcement as a draft, with its files.' })\n async createAnnouncement(\n @Arg('input', type => AnnouncementInput) input: AnnouncementInput,\n @Arg('attach', type => [GraphQLUpload], { nullable: true }) attach: FileUpload[],\n @Ctx() context: ResolverContext\n ): Promise<Announcement> {\n const { domain, user, tx } = context.state as any\n const saved = await getRepository(Announcement, tx).save({ ...fieldsOf(input, context), domain, status: AnnouncementStatus.DRAFT, version: 1, creator: user, updater: user } as any)\n await setAudiences(saved, input.audiences ?? [], context, tx)\n await attachFiles(saved.id, attach, [], context)\n return saved\n }\n\n @Directive(WRITE)\n @Directive('@transaction')\n @Mutation(returns => Announcement, {\n description:\n 'Changes an announcement. Once out, each change is a new version; people the audience now names who were not told are told, and `notifyAgain` tells everyone on the roster again.'\n })\n async updateAnnouncement(\n @Arg('id') id: string,\n @Arg('input', type => AnnouncementInput) input: AnnouncementInput,\n @Arg('attach', type => [GraphQLUpload], { nullable: true }) attach: FileUpload[],\n @Arg('remove', type => [String], { nullable: true, description: 'Ids of its files to take off.' }) remove: string[],\n @Arg('notifyAgain', { nullable: true, description: 'Tell everyone on the roster of the change.' }) notifyAgain: boolean,\n @Ctx() context: ResolverContext\n ): Promise<Announcement> {\n const { domain, user, tx } = context.state as any\n const announcement = await changeable(id, context, tx)\n const fields = fieldsOf(input, context)\n const out = announcement.status === AnnouncementStatus.PUBLISHED\n /* Once out, its start is the moment it went out - a start set later would hide it again. */\n if (out) fields.startsAt = announcement.startsAt\n const repository = getRepository(Announcement, tx)\n await repository.update(id, { ...fields, ...(out ? { version: announcement.version + 1 } : {}), updater: user } as any)\n const audienceChanged = await setAudiences(announcement, input.audiences ?? [], context, tx)\n if (out && !(input.audiences ?? []).length) refuse(context, 'no-audience', 'an announcement that is out names someone.')\n await attachFiles(id, attach, remove, context)\n const fresh = (await repository.findOneBy({ id }))!\n if (!out) return fresh\n await keepRevision(fresh, domain, user, tx)\n const added = audienceChanged ? await alignRoster(fresh, domain, user, tx) : []\n const receivers = notifyAgain\n ? (await getRepository(AnnouncementReceipt, tx).find({ where: { announcement: { id }, listed: true } as any })).map(one => one.userId!)\n : added\n if (notifyAgain) await getRepository(AnnouncementReceipt, tx).update({ announcement: { id }, listed: true } as any, { notifiedAt: new Date() } as any)\n /* Told again, everyone hears it - the channel too; people the audience newly names are told one by one. */\n await tellWhenCommitted(context, fresh, notifyAgain ? 'changed' : 'published', receivers.filter(one => one !== user.id), !!notifyAgain)\n return fresh\n }\n\n @Directive(WRITE)\n @Directive('@transaction')\n @Mutation(returns => Announcement, { description: 'Sends a draft out to its audience - at once, or at its start when that is still to come.' })\n async publishAnnouncement(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Announcement> {\n const { domain, user, tx } = context.state as any\n const announcement = await changeable(id, context, tx)\n const audiences = await audiencesOf(id, tx)\n const refusal = publishRefusal(announcement.status, announcement, audiences.length)\n if (refusal) refuse(context, refusal, `the announcement cannot go out (${refusal}).`)\n const repository = getRepository(Announcement, tx)\n if (publishedStatusOf(announcement.startsAt) === 'SCHEDULED') {\n await repository.update(id, { status: AnnouncementStatus.SCHEDULED, publisher: user, updater: user } as any)\n return (await repository.findOneBy({ id }))!\n }\n const receivers = await sendOut(announcement, domain, user, tx)\n const fresh = (await repository.findOneBy({ id }))!\n await tellWhenCommitted(context, fresh, 'published', receivers, true)\n return fresh\n }\n\n @Directive(WRITE)\n @Directive('@transaction')\n @Mutation(returns => Announcement, { description: 'Takes back an announcement that is out or scheduled, with the reason. Its audience no longer sees it; its record stays.' })\n async withdrawAnnouncement(@Arg('id') id: string, @Arg('reason') reason: string, @Ctx() context: ResolverContext): Promise<Announcement> {\n const { user, tx } = context.state as any\n const announcement = await changeable(id, context, tx)\n if (announcement.status === AnnouncementStatus.DRAFT) refuse(context, 'not-out', 'a draft is deleted, not withdrawn.')\n if (!reason?.trim()) refuse(context, 'no-reason', 'say why it is withdrawn.')\n const repository = getRepository(Announcement, tx)\n await repository.update(id, { status: AnnouncementStatus.WITHDRAWN, withdrawnAt: new Date(), withdrawReason: reason.trim().slice(0, 500), updater: user } as any)\n return (await repository.findOneBy({ id }))!\n }\n\n @Directive(WRITE)\n @Directive('@transaction')\n @Mutation(returns => Boolean, { description: \"Deletes one's own draft and its files. An announcement that went out is withdrawn instead.\" })\n async deleteAnnouncement(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {\n const { tx } = context.state as any\n const announcement = await changeable(id, context, tx)\n if (announcement.status !== AnnouncementStatus.DRAFT) refuse(context, 'not-a-draft', 'an announcement that went out is withdrawn, not deleted.')\n await deleteAttachmentsByRef(null, { refBys: [id], refType: ANNOUNCEMENT_REF_TYPE }, context)\n await getRepository(Announcement, tx).softDelete(id)\n return true\n }\n\n @Directive(WRITE)\n @Directive('@transaction')\n @Mutation(returns => Int, { description: 'Tells again those on the roster who have not read it - or not acknowledged it, when it asks for that. Answers how many were told.' })\n async remindAnnouncement(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<number> {\n const { user, tx } = context.state as any\n const announcement = await changeable(id, context, tx)\n if (!isCurrent(announcement)) refuse(context, 'not-current', 'only a current announcement is reminded.')\n const repository = getRepository(AnnouncementReceipt, tx)\n const owed = (await repository.find({ where: { announcement: { id } } as any })).filter(one => owedReminder(announcement, one))\n if (owed.length) await repository.update({ id: In(owed.map(one => one.id)) } as any, { notifiedAt: new Date(), updater: user } as any)\n await tellWhenCommitted(\n context,\n announcement,\n 'reminder',\n owed.map(one => one.userId!)\n )\n return owed.length\n }\n\n /* ---------------------------------------------------------------- fields */\n\n @FieldResolver(type => [AnnouncementAudience], { description: 'Whom it is addressed to.' })\n async audiences(@Root() one: Announcement): Promise<AnnouncementAudience[]> {\n return await audiencesOf(one.id)\n }\n\n @FieldResolver(type => [Attachment], { description: 'Its files.' })\n async attachments(@Root() one: Announcement, @Ctx() context: ResolverContext): Promise<Attachment[]> {\n return await getRepository(Attachment).find({ where: { domain: { id: context.state.domain.id }, refType: ANNOUNCEMENT_REF_TYPE, refBy: one.id } as any, order: { createdAt: 'ASC' } as any })\n }\n\n @FieldResolver(type => AnnouncementReceipt, { nullable: true, description: \"The request's person's own reading of it.\" })\n async mine(@Root() one: Announcement, @Ctx() context: ResolverContext): Promise<AnnouncementReceipt | null> {\n if ('__mine' in (one as any)) return (one as any).__mine\n return (await myReceipts([one.id], context.state.user.id)).get(one.id) ?? null\n }\n\n @FieldResolver(type => Boolean, { description: \"Whether it is addressed to the request's person and out - the one who reads and acknowledges it.\" })\n async addressed(@Root() one: Announcement, @Ctx() context: ResolverContext): Promise<boolean> {\n return one.status === AnnouncementStatus.PUBLISHED && (await reachingIds(await memberOf(context))).includes(one.id)\n }\n\n @FieldResolver(type => String, { nullable: true, description: 'Whether it is still current or past - for one that is out.' })\n period(@Root() one: Announcement): string | undefined {\n return periodOf(one)\n }\n\n @FieldResolver(type => String, { description: \"Where it stands for the request's person: unread, read, to-acknowledge or acknowledged.\" })\n async readerState(@Root() one: Announcement, @Ctx() context: ResolverContext): Promise<string> {\n return readerStateOf(one, (await this.mine(one, context)) ?? undefined)\n }\n\n @FieldResolver(type => Int, { nullable: true, description: 'How many on the roster read it - for its writers.' })\n async readCount(@Root() one: Announcement, @Ctx() context: ResolverContext): Promise<number | null> {\n if (!(await isWriter(context))) return null\n return await getRepository(AnnouncementReceipt).count({ where: { announcement: { id: one.id }, listed: true, readAt: Not(IsNull()) } as any })\n }\n\n @FieldResolver(type => Int, { nullable: true, description: 'How many on the roster acknowledged it - for its writers.' })\n async acknowledgedCount(@Root() one: Announcement, @Ctx() context: ResolverContext): Promise<number | null> {\n if (!(await isWriter(context))) return null\n return await getRepository(AnnouncementReceipt).count({ where: { announcement: { id: one.id }, listed: true, acknowledgedAt: Not(IsNull()) } as any })\n }\n\n @FieldResolver(type => Boolean, { description: 'Whether the request may change it.' })\n async editable(@Root() one: Announcement, @Ctx() context: ResolverContext): Promise<boolean> {\n if (one.status === AnnouncementStatus.WITHDRAWN) return false\n if (one.status === AnnouncementStatus.DRAFT && one.creatorId !== context.state.user.id) return false\n return await isWriter(context)\n }\n\n @FieldResolver(type => User, { nullable: true, description: 'Who published it.' })\n async publisher(@Root() one: Announcement): Promise<User | null> {\n return one.publisherId ? await getRepository(User).findOneBy({ id: one.publisherId }) : null\n }\n\n @FieldResolver(type => User, { nullable: true, description: 'Who wrote it.' })\n async creator(@Root() one: Announcement): Promise<User | null> {\n return one.creatorId ? await getRepository(User).findOneBy({ id: one.creatorId }) : null\n }\n}\n"]}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/** The kinds of announcement - declared by this module, named in its translations (`announcement.category.<code>`). */
|
|
2
|
+
export declare const ANNOUNCEMENT_CATEGORIES: readonly ["GENERAL", "HR", "WELFARE", "EVENT", "SYSTEM", "SECURITY"];
|
|
3
|
+
export declare const IMPORTANCES: readonly ["NORMAL", "IMPORTANT", "URGENT"];
|
|
4
|
+
export declare const MAX_TITLE = 200;
|
|
5
|
+
export declare const MAX_LINKS = 10;
|
|
6
|
+
export declare const MAX_LINK_LABEL = 100;
|
|
7
|
+
export interface AnnouncementFacts {
|
|
8
|
+
status: string;
|
|
9
|
+
importance?: string;
|
|
10
|
+
pinned?: boolean;
|
|
11
|
+
popup?: boolean;
|
|
12
|
+
requiresAcknowledgement?: boolean;
|
|
13
|
+
startsAt?: Date | string | null;
|
|
14
|
+
endsAt?: Date | string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface ReceiptFacts {
|
|
17
|
+
readAt?: Date | string | null;
|
|
18
|
+
acknowledgedAt?: Date | string | null;
|
|
19
|
+
}
|
|
20
|
+
/** Out to its audience and not past its end. */
|
|
21
|
+
export declare function isCurrent(one: AnnouncementFacts, now?: Date): boolean;
|
|
22
|
+
/** Whether an announcement that is out is still current or past - a reader's list and a dashboard card ask it. */
|
|
23
|
+
export declare function periodOf(one: AnnouncementFacts, now?: Date): 'current' | 'past' | undefined;
|
|
24
|
+
export declare const PERIODS: readonly ["current", "past"];
|
|
25
|
+
export declare const READER_STATES: readonly ["unread", "read", "to-acknowledge", "acknowledged"];
|
|
26
|
+
type ListFilter = {
|
|
27
|
+
name: string;
|
|
28
|
+
operator?: string;
|
|
29
|
+
value?: unknown;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The reader's own narrowing in a list's filters - `period` (current | past) and `readerState` (unread, read,
|
|
33
|
+
* to-acknowledge, acknowledged; one or several) - taken out of the column filters, so the screen and the assistant
|
|
34
|
+
* narrow the one list the same way. Absent, a list is not narrowed by them. An unknown value is refused, not ignored.
|
|
35
|
+
*/
|
|
36
|
+
export declare function readerNarrowingOf(filters: ListFilter[] | null | undefined): {
|
|
37
|
+
rest: ListFilter[];
|
|
38
|
+
period?: 'current' | 'past';
|
|
39
|
+
states?: string[];
|
|
40
|
+
refusal?: string;
|
|
41
|
+
};
|
|
42
|
+
/** Publishing now goes out at once, or waits for a start still to come. */
|
|
43
|
+
export declare function publishedStatusOf(startsAt: Date | string | null | undefined, now?: Date): 'PUBLISHED' | 'SCHEDULED';
|
|
44
|
+
/**
|
|
45
|
+
* Where it stands for one reader. Reading and acknowledging are apart: one that asks to be acknowledged stays
|
|
46
|
+
* 'to-acknowledge' after it was read.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readerStateOf(one: AnnouncementFacts, receipt: ReceiptFacts | undefined): 'unread' | 'to-acknowledge' | 'acknowledged' | 'read';
|
|
49
|
+
/**
|
|
50
|
+
* Whether it pops up for the reader (the user, 2026-10-26: 「공지 사항이 있을 때 자동 팝업」): a current popup
|
|
51
|
+
* announcement shows once - seeing it reads it - and one that asks to be acknowledged shows at every sign-in until it
|
|
52
|
+
* is acknowledged.
|
|
53
|
+
*/
|
|
54
|
+
export declare function popupDue(one: AnnouncementFacts, receipt: ReceiptFacts | undefined, now?: Date): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* The place in the readers' list - kept as a column so the list is ordered by the database: pinned first, then urgent,
|
|
57
|
+
* important, normal. The newest comes first within each (the list's second order).
|
|
58
|
+
*/
|
|
59
|
+
export declare function rankOf(one: Pick<AnnouncementFacts, 'pinned' | 'importance'>): number;
|
|
60
|
+
/**
|
|
61
|
+
* A link a reader may follow: a screen of the application by its address (`contract-list?id=…`) or an https page.
|
|
62
|
+
* Nothing that runs (`javascript:`), no other scheme, nothing that leaves through `//`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function linkRefusal(link: {
|
|
65
|
+
label?: string;
|
|
66
|
+
url?: string;
|
|
67
|
+
} | null | undefined): string | undefined;
|
|
68
|
+
export interface AnnouncementInputFacts {
|
|
69
|
+
title?: string;
|
|
70
|
+
category?: string;
|
|
71
|
+
importance?: string;
|
|
72
|
+
startsAt?: Date | string | null;
|
|
73
|
+
endsAt?: Date | string | null;
|
|
74
|
+
requiresAcknowledgement?: boolean;
|
|
75
|
+
acknowledgeBy?: Date | string | null;
|
|
76
|
+
links?: {
|
|
77
|
+
label?: string;
|
|
78
|
+
url?: string;
|
|
79
|
+
}[] | null;
|
|
80
|
+
}
|
|
81
|
+
/** Why what was written cannot be kept, or nothing. Asked on every save - a draft is kept to the same rules. */
|
|
82
|
+
export declare function inputRefusal(input: AnnouncementInputFacts): string | undefined;
|
|
83
|
+
/** Why it cannot go out, or nothing - it is written to the rules and names someone. */
|
|
84
|
+
export declare function publishRefusal(status: string, input: AnnouncementInputFacts, audienceCount: number): string | undefined;
|
|
85
|
+
/** Who on the roster is still owed a reminder: not read yet, or not acknowledged when it asks for that. */
|
|
86
|
+
export declare function owedReminder(one: Pick<AnnouncementFacts, 'requiresAcknowledgement'>, receipt: ReceiptFacts & {
|
|
87
|
+
listed?: boolean;
|
|
88
|
+
}): boolean;
|
|
89
|
+
/** The body as one line of plain words - the notice's text, and the list's preview. */
|
|
90
|
+
export declare function plainSummary(body: string | null | undefined, max?: number): string;
|
|
91
|
+
/** The screen an announcement opens on. */
|
|
92
|
+
export declare const viewAddressOf: (id: string) => string;
|
|
93
|
+
/**
|
|
94
|
+
* Whether its notice is said once to the whole domain - a Teams channel everyone reads (notification `broadcast`).
|
|
95
|
+
* Only an announcement addressed to the whole domain: one for a department or a role must not land where everyone
|
|
96
|
+
* reads it.
|
|
97
|
+
*/
|
|
98
|
+
export declare function broadcastOf(audiences: readonly {
|
|
99
|
+
scope: string;
|
|
100
|
+
targetId: string;
|
|
101
|
+
}[], domainId: string | undefined): boolean;
|
|
102
|
+
/** Why the people are told: it went out, it changed and they are told again, or they still owe it. */
|
|
103
|
+
export type NoticeKind = 'published' | 'changed' | 'reminder';
|
|
104
|
+
/** A sentence in one language, with the English one when that language has none. */
|
|
105
|
+
export type Say = (key: string, english: string, params?: Record<string, unknown>) => string;
|
|
106
|
+
/**
|
|
107
|
+
* What the notice of an announcement says to one person (notification's `NoticeWords`): its title - marked when it is
|
|
108
|
+
* a change or a reminder - the start of its body for the inbox and web push, and the whole body, Markdown, for the mail
|
|
109
|
+
* (ADR-0163 decision 6: the mail carries the announcement, files stay behind the sign-in).
|
|
110
|
+
*/
|
|
111
|
+
export declare function noticeWordsOf(one: {
|
|
112
|
+
title: string;
|
|
113
|
+
body?: string | null;
|
|
114
|
+
}, kind: NoticeKind, say: Say): {
|
|
115
|
+
subject: string;
|
|
116
|
+
summary: string;
|
|
117
|
+
body: string;
|
|
118
|
+
action: string;
|
|
119
|
+
note: string;
|
|
120
|
+
};
|
|
121
|
+
export type NoticeCount = {
|
|
122
|
+
receivers: number;
|
|
123
|
+
email: {
|
|
124
|
+
sent: number;
|
|
125
|
+
failed: number;
|
|
126
|
+
};
|
|
127
|
+
teams: {
|
|
128
|
+
sent: number;
|
|
129
|
+
failed: number;
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
/** One telling as the announcement keeps it - when, why, and what went out. A send that could not go at all counts its receivers, nothing sent. */
|
|
133
|
+
export declare function deliveryOf(kind: NoticeKind, result: NoticeCount | undefined, receivers: number, at?: Date): {
|
|
134
|
+
failed?: boolean;
|
|
135
|
+
at: string;
|
|
136
|
+
kind: NoticeKind;
|
|
137
|
+
receivers: number;
|
|
138
|
+
email: {
|
|
139
|
+
sent: number;
|
|
140
|
+
failed: number;
|
|
141
|
+
};
|
|
142
|
+
teams: {
|
|
143
|
+
sent: number;
|
|
144
|
+
failed: number;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
/** The record kept on an announcement - the newest tellings, at most `MAX_DELIVERIES`. */
|
|
148
|
+
export declare const MAX_DELIVERIES = 50;
|
|
149
|
+
export declare function withDelivery<T>(deliveries: T[] | null | undefined, one: T): T[];
|
|
150
|
+
export {};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_DELIVERIES = exports.viewAddressOf = exports.READER_STATES = exports.PERIODS = exports.MAX_LINK_LABEL = exports.MAX_LINKS = exports.MAX_TITLE = exports.IMPORTANCES = exports.ANNOUNCEMENT_CATEGORIES = void 0;
|
|
4
|
+
exports.isCurrent = isCurrent;
|
|
5
|
+
exports.periodOf = periodOf;
|
|
6
|
+
exports.readerNarrowingOf = readerNarrowingOf;
|
|
7
|
+
exports.publishedStatusOf = publishedStatusOf;
|
|
8
|
+
exports.readerStateOf = readerStateOf;
|
|
9
|
+
exports.popupDue = popupDue;
|
|
10
|
+
exports.rankOf = rankOf;
|
|
11
|
+
exports.linkRefusal = linkRefusal;
|
|
12
|
+
exports.inputRefusal = inputRefusal;
|
|
13
|
+
exports.publishRefusal = publishRefusal;
|
|
14
|
+
exports.owedReminder = owedReminder;
|
|
15
|
+
exports.plainSummary = plainSummary;
|
|
16
|
+
exports.broadcastOf = broadcastOf;
|
|
17
|
+
exports.noticeWordsOf = noticeWordsOf;
|
|
18
|
+
exports.deliveryOf = deliveryOf;
|
|
19
|
+
exports.withDelivery = withDelivery;
|
|
20
|
+
/*
|
|
21
|
+
* The rules of an announcement (ADR-0163 decisions 3-5) - what may be written, where it stands, what a reader owes it,
|
|
22
|
+
* when it pops up, and what the notice of it says.
|
|
23
|
+
*
|
|
24
|
+
* Pure - the rules are tested without a database. The doors (announcement-resolver.ts) ask them.
|
|
25
|
+
*/
|
|
26
|
+
/** The kinds of announcement - declared by this module, named in its translations (`announcement.category.<code>`). */
|
|
27
|
+
exports.ANNOUNCEMENT_CATEGORIES = ['GENERAL', 'HR', 'WELFARE', 'EVENT', 'SYSTEM', 'SECURITY'];
|
|
28
|
+
exports.IMPORTANCES = ['NORMAL', 'IMPORTANT', 'URGENT'];
|
|
29
|
+
exports.MAX_TITLE = 200;
|
|
30
|
+
exports.MAX_LINKS = 10;
|
|
31
|
+
exports.MAX_LINK_LABEL = 100;
|
|
32
|
+
const time = (value) => (value ? new Date(value).getTime() : undefined);
|
|
33
|
+
/** Out to its audience and not past its end. */
|
|
34
|
+
function isCurrent(one, now = new Date()) {
|
|
35
|
+
const end = time(one.endsAt);
|
|
36
|
+
return one.status === 'PUBLISHED' && (end === undefined || end > now.getTime());
|
|
37
|
+
}
|
|
38
|
+
/** Whether an announcement that is out is still current or past - a reader's list and a dashboard card ask it. */
|
|
39
|
+
function periodOf(one, now = new Date()) {
|
|
40
|
+
if (one.status !== 'PUBLISHED')
|
|
41
|
+
return undefined;
|
|
42
|
+
return isCurrent(one, now) ? 'current' : 'past';
|
|
43
|
+
}
|
|
44
|
+
exports.PERIODS = ['current', 'past'];
|
|
45
|
+
exports.READER_STATES = ['unread', 'read', 'to-acknowledge', 'acknowledged'];
|
|
46
|
+
/**
|
|
47
|
+
* The reader's own narrowing in a list's filters - `period` (current | past) and `readerState` (unread, read,
|
|
48
|
+
* to-acknowledge, acknowledged; one or several) - taken out of the column filters, so the screen and the assistant
|
|
49
|
+
* narrow the one list the same way. Absent, a list is not narrowed by them. An unknown value is refused, not ignored.
|
|
50
|
+
*/
|
|
51
|
+
function readerNarrowingOf(filters) {
|
|
52
|
+
const rest = [];
|
|
53
|
+
let period;
|
|
54
|
+
let states;
|
|
55
|
+
for (const filter of filters ?? []) {
|
|
56
|
+
if (filter?.name === 'period') {
|
|
57
|
+
if (!exports.PERIODS.includes(String(filter.value)))
|
|
58
|
+
return { rest, refusal: 'unknown-period' };
|
|
59
|
+
period = filter.value;
|
|
60
|
+
}
|
|
61
|
+
else if (filter?.name === 'readerState') {
|
|
62
|
+
const values = [filter.value].flat().map(String);
|
|
63
|
+
if (!values.length || values.some(one => !exports.READER_STATES.includes(one)))
|
|
64
|
+
return { rest, refusal: 'unknown-reader-state' };
|
|
65
|
+
states = values;
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
rest.push(filter);
|
|
69
|
+
}
|
|
70
|
+
return { rest, period, states };
|
|
71
|
+
}
|
|
72
|
+
/** Publishing now goes out at once, or waits for a start still to come. */
|
|
73
|
+
function publishedStatusOf(startsAt, now = new Date()) {
|
|
74
|
+
const start = time(startsAt);
|
|
75
|
+
return start !== undefined && start > now.getTime() ? 'SCHEDULED' : 'PUBLISHED';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Where it stands for one reader. Reading and acknowledging are apart: one that asks to be acknowledged stays
|
|
79
|
+
* 'to-acknowledge' after it was read.
|
|
80
|
+
*/
|
|
81
|
+
function readerStateOf(one, receipt) {
|
|
82
|
+
if (one.requiresAcknowledgement) {
|
|
83
|
+
if (receipt?.acknowledgedAt)
|
|
84
|
+
return 'acknowledged';
|
|
85
|
+
return receipt?.readAt ? 'to-acknowledge' : 'unread';
|
|
86
|
+
}
|
|
87
|
+
return receipt?.readAt ? 'read' : 'unread';
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Whether it pops up for the reader (the user, 2026-10-26: 「공지 사항이 있을 때 자동 팝업」): a current popup
|
|
91
|
+
* announcement shows once - seeing it reads it - and one that asks to be acknowledged shows at every sign-in until it
|
|
92
|
+
* is acknowledged.
|
|
93
|
+
*/
|
|
94
|
+
function popupDue(one, receipt, now = new Date()) {
|
|
95
|
+
if (!one.popup || !isCurrent(one, now))
|
|
96
|
+
return false;
|
|
97
|
+
return one.requiresAcknowledgement ? !receipt?.acknowledgedAt : !receipt?.readAt;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The place in the readers' list - kept as a column so the list is ordered by the database: pinned first, then urgent,
|
|
101
|
+
* important, normal. The newest comes first within each (the list's second order).
|
|
102
|
+
*/
|
|
103
|
+
function rankOf(one) {
|
|
104
|
+
const importance = one.importance === 'URGENT' ? 0 : one.importance === 'IMPORTANT' ? 1 : 2;
|
|
105
|
+
return (one.pinned ? 0 : 10) + importance;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* A link a reader may follow: a screen of the application by its address (`contract-list?id=…`) or an https page.
|
|
109
|
+
* Nothing that runs (`javascript:`), no other scheme, nothing that leaves through `//`.
|
|
110
|
+
*/
|
|
111
|
+
function linkRefusal(link) {
|
|
112
|
+
const label = String(link?.label ?? '').trim();
|
|
113
|
+
const url = String(link?.url ?? '').trim();
|
|
114
|
+
if (!label || !url)
|
|
115
|
+
return 'link-incomplete';
|
|
116
|
+
if (label.length > exports.MAX_LINK_LABEL)
|
|
117
|
+
return 'link-label-too-long';
|
|
118
|
+
if (/\s/.test(url) || url.includes('\\'))
|
|
119
|
+
return 'bad-link';
|
|
120
|
+
if (/^https:\/\/[^/]/i.test(url))
|
|
121
|
+
return undefined;
|
|
122
|
+
if (url.startsWith('/') || /^[a-z][a-z0-9+.-]*:/i.test(url))
|
|
123
|
+
return 'bad-link';
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
/** Why what was written cannot be kept, or nothing. Asked on every save - a draft is kept to the same rules. */
|
|
127
|
+
function inputRefusal(input) {
|
|
128
|
+
const title = String(input.title ?? '').trim();
|
|
129
|
+
if (!title)
|
|
130
|
+
return 'no-title';
|
|
131
|
+
if (title.length > exports.MAX_TITLE)
|
|
132
|
+
return 'title-too-long';
|
|
133
|
+
if (input.category && !exports.ANNOUNCEMENT_CATEGORIES.includes(input.category))
|
|
134
|
+
return 'unknown-category';
|
|
135
|
+
if (input.importance && !exports.IMPORTANCES.includes(input.importance))
|
|
136
|
+
return 'unknown-importance';
|
|
137
|
+
const start = time(input.startsAt);
|
|
138
|
+
const end = time(input.endsAt);
|
|
139
|
+
if (start !== undefined && end !== undefined && end <= start)
|
|
140
|
+
return 'ends-before-start';
|
|
141
|
+
if (input.acknowledgeBy && !input.requiresAcknowledgement)
|
|
142
|
+
return 'acknowledge-by-without-ask';
|
|
143
|
+
const by = time(input.acknowledgeBy);
|
|
144
|
+
if (by !== undefined && start !== undefined && by < start)
|
|
145
|
+
return 'acknowledge-by-before-start';
|
|
146
|
+
if ((input.links ?? []).length > exports.MAX_LINKS)
|
|
147
|
+
return 'too-many-links';
|
|
148
|
+
for (const link of input.links ?? []) {
|
|
149
|
+
const refusal = linkRefusal(link);
|
|
150
|
+
if (refusal)
|
|
151
|
+
return refusal;
|
|
152
|
+
}
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
/** Why it cannot go out, or nothing - it is written to the rules and names someone. */
|
|
156
|
+
function publishRefusal(status, input, audienceCount) {
|
|
157
|
+
if (status !== 'DRAFT')
|
|
158
|
+
return 'not-a-draft';
|
|
159
|
+
return inputRefusal(input) ?? (audienceCount > 0 ? undefined : 'no-audience');
|
|
160
|
+
}
|
|
161
|
+
/** Who on the roster is still owed a reminder: not read yet, or not acknowledged when it asks for that. */
|
|
162
|
+
function owedReminder(one, receipt) {
|
|
163
|
+
if (!receipt.listed)
|
|
164
|
+
return false;
|
|
165
|
+
return one.requiresAcknowledgement ? !receipt.acknowledgedAt : !receipt.readAt;
|
|
166
|
+
}
|
|
167
|
+
/** The body as one line of plain words - the notice's text, and the list's preview. */
|
|
168
|
+
function plainSummary(body, max = 120) {
|
|
169
|
+
const text = String(body ?? '')
|
|
170
|
+
.replace(/```[\s\S]*?```/g, ' ')
|
|
171
|
+
.replace(/!\[[^\]]*\]\([^)]*\)/g, ' ')
|
|
172
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
173
|
+
.replace(/^\s{0,3}(#{1,6}|>|[-*+]|\d+\.)\s+/gm, '')
|
|
174
|
+
/* Only the marks that wrap words - a lone ~ (22시~24시), _ or | is part of the text. */
|
|
175
|
+
.replace(/(\*\*|__|~~|`)/g, '')
|
|
176
|
+
.replace(/(^|\s)\*(\S[^*]*?)\*(?=\s|$|[.,!?])/g, '$1$2')
|
|
177
|
+
.replace(/\s+/g, ' ')
|
|
178
|
+
.trim();
|
|
179
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
180
|
+
}
|
|
181
|
+
/** The screen an announcement opens on. */
|
|
182
|
+
const viewAddressOf = (id) => `announcement-view?id=${id}`;
|
|
183
|
+
exports.viewAddressOf = viewAddressOf;
|
|
184
|
+
/**
|
|
185
|
+
* Whether its notice is said once to the whole domain - a Teams channel everyone reads (notification `broadcast`).
|
|
186
|
+
* Only an announcement addressed to the whole domain: one for a department or a role must not land where everyone
|
|
187
|
+
* reads it.
|
|
188
|
+
*/
|
|
189
|
+
function broadcastOf(audiences, domainId) {
|
|
190
|
+
return !!domainId && audiences.some(one => one.scope === 'DOMAIN' && one.targetId === domainId);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* What the notice of an announcement says to one person (notification's `NoticeWords`): its title - marked when it is
|
|
194
|
+
* a change or a reminder - the start of its body for the inbox and web push, and the whole body, Markdown, for the mail
|
|
195
|
+
* (ADR-0163 decision 6: the mail carries the announcement, files stay behind the sign-in).
|
|
196
|
+
*/
|
|
197
|
+
function noticeWordsOf(one, kind, say) {
|
|
198
|
+
const subject = kind === 'changed'
|
|
199
|
+
? say('announcement.notice.changed-subject', 'Changed: {title}', { title: one.title })
|
|
200
|
+
: kind === 'reminder'
|
|
201
|
+
? say('announcement.notice.reminder-subject', 'Reminder: {title}', { title: one.title })
|
|
202
|
+
: one.title;
|
|
203
|
+
return {
|
|
204
|
+
subject,
|
|
205
|
+
summary: plainSummary(one.body),
|
|
206
|
+
body: one.body ?? '',
|
|
207
|
+
action: say('announcement.notice.action', 'Open the announcement'),
|
|
208
|
+
note: say('announcement.notice.note', 'Open Announcements in the application to read it.')
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/** One telling as the announcement keeps it - when, why, and what went out. A send that could not go at all counts its receivers, nothing sent. */
|
|
212
|
+
function deliveryOf(kind, result, receivers, at = new Date()) {
|
|
213
|
+
return {
|
|
214
|
+
at: at.toISOString(),
|
|
215
|
+
kind,
|
|
216
|
+
receivers: result?.receivers ?? receivers,
|
|
217
|
+
email: result?.email ?? { sent: 0, failed: 0 },
|
|
218
|
+
teams: result?.teams ?? { sent: 0, failed: 0 },
|
|
219
|
+
...(result ? {} : { failed: true })
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/** The record kept on an announcement - the newest tellings, at most `MAX_DELIVERIES`. */
|
|
223
|
+
exports.MAX_DELIVERIES = 50;
|
|
224
|
+
function withDelivery(deliveries, one) {
|
|
225
|
+
return [...(deliveries ?? []), one].slice(-exports.MAX_DELIVERIES);
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=announcement-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"announcement-rules.js","sourceRoot":"","sources":["../../../server/service/announcement/announcement-rules.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The rules of an announcement (ADR-0163 decisions 3-5) - what may be written, where it stands, what a reader owes it,\n * when it pops up, and what the notice of it says.\n *\n * Pure - the rules are tested without a database. The doors (announcement-resolver.ts) ask them.\n */\n/** The kinds of announcement - declared by this module, named in its translations (`announcement.category.<code>`). */\nexport const ANNOUNCEMENT_CATEGORIES = ['GENERAL', 'HR', 'WELFARE', 'EVENT', 'SYSTEM', 'SECURITY'] as const\n\nexport const IMPORTANCES = ['NORMAL', 'IMPORTANT', 'URGENT'] as const\nexport const MAX_TITLE = 200\nexport const MAX_LINKS = 10\nexport const MAX_LINK_LABEL = 100\n\nexport interface AnnouncementFacts {\n status: string\n importance?: string\n pinned?: boolean\n popup?: boolean\n requiresAcknowledgement?: boolean\n startsAt?: Date | string | null\n endsAt?: Date | string | null\n}\n\nexport interface ReceiptFacts {\n readAt?: Date | string | null\n acknowledgedAt?: Date | string | null\n}\n\nconst time = (value: Date | string | null | undefined) => (value ? new Date(value).getTime() : undefined)\n\n/** Out to its audience and not past its end. */\nexport function isCurrent(one: AnnouncementFacts, now = new Date()): boolean {\n const end = time(one.endsAt)\n return one.status === 'PUBLISHED' && (end === undefined || end > now.getTime())\n}\n\n/** Whether an announcement that is out is still current or past - a reader's list and a dashboard card ask it. */\nexport function periodOf(one: AnnouncementFacts, now = new Date()): 'current' | 'past' | undefined {\n if (one.status !== 'PUBLISHED') return undefined\n return isCurrent(one, now) ? 'current' : 'past'\n}\n\nexport const PERIODS = ['current', 'past'] as const\nexport const READER_STATES = ['unread', 'read', 'to-acknowledge', 'acknowledged'] as const\n\ntype ListFilter = { name: string; operator?: string; value?: unknown }\n\n/**\n * The reader's own narrowing in a list's filters - `period` (current | past) and `readerState` (unread, read,\n * to-acknowledge, acknowledged; one or several) - taken out of the column filters, so the screen and the assistant\n * narrow the one list the same way. Absent, a list is not narrowed by them. An unknown value is refused, not ignored.\n */\nexport function readerNarrowingOf(filters: ListFilter[] | null | undefined): { rest: ListFilter[]; period?: 'current' | 'past'; states?: string[]; refusal?: string } {\n const rest: ListFilter[] = []\n let period: 'current' | 'past' | undefined\n let states: string[] | undefined\n for (const filter of filters ?? []) {\n if (filter?.name === 'period') {\n if (!(PERIODS as readonly string[]).includes(String(filter.value))) return { rest, refusal: 'unknown-period' }\n period = filter.value as 'current' | 'past'\n } else if (filter?.name === 'readerState') {\n const values = [filter.value].flat().map(String)\n if (!values.length || values.some(one => !(READER_STATES as readonly string[]).includes(one))) return { rest, refusal: 'unknown-reader-state' }\n states = values\n } else rest.push(filter)\n }\n return { rest, period, states }\n}\n\n/** Publishing now goes out at once, or waits for a start still to come. */\nexport function publishedStatusOf(startsAt: Date | string | null | undefined, now = new Date()): 'PUBLISHED' | 'SCHEDULED' {\n const start = time(startsAt)\n return start !== undefined && start > now.getTime() ? 'SCHEDULED' : 'PUBLISHED'\n}\n\n/**\n * Where it stands for one reader. Reading and acknowledging are apart: one that asks to be acknowledged stays\n * 'to-acknowledge' after it was read.\n */\nexport function readerStateOf(one: AnnouncementFacts, receipt: ReceiptFacts | undefined): 'unread' | 'to-acknowledge' | 'acknowledged' | 'read' {\n if (one.requiresAcknowledgement) {\n if (receipt?.acknowledgedAt) return 'acknowledged'\n return receipt?.readAt ? 'to-acknowledge' : 'unread'\n }\n return receipt?.readAt ? 'read' : 'unread'\n}\n\n/**\n * Whether it pops up for the reader (the user, 2026-10-26: 「공지 사항이 있을 때 자동 팝업」): a current popup\n * announcement shows once - seeing it reads it - and one that asks to be acknowledged shows at every sign-in until it\n * is acknowledged.\n */\nexport function popupDue(one: AnnouncementFacts, receipt: ReceiptFacts | undefined, now = new Date()): boolean {\n if (!one.popup || !isCurrent(one, now)) return false\n return one.requiresAcknowledgement ? !receipt?.acknowledgedAt : !receipt?.readAt\n}\n\n/**\n * The place in the readers' list - kept as a column so the list is ordered by the database: pinned first, then urgent,\n * important, normal. The newest comes first within each (the list's second order).\n */\nexport function rankOf(one: Pick<AnnouncementFacts, 'pinned' | 'importance'>): number {\n const importance = one.importance === 'URGENT' ? 0 : one.importance === 'IMPORTANT' ? 1 : 2\n return (one.pinned ? 0 : 10) + importance\n}\n\n/**\n * A link a reader may follow: a screen of the application by its address (`contract-list?id=…`) or an https page.\n * Nothing that runs (`javascript:`), no other scheme, nothing that leaves through `//`.\n */\nexport function linkRefusal(link: { label?: string; url?: string } | null | undefined): string | undefined {\n const label = String(link?.label ?? '').trim()\n const url = String(link?.url ?? '').trim()\n if (!label || !url) return 'link-incomplete'\n if (label.length > MAX_LINK_LABEL) return 'link-label-too-long'\n if (/\\s/.test(url) || url.includes('\\\\')) return 'bad-link'\n if (/^https:\\/\\/[^/]/i.test(url)) return undefined\n if (url.startsWith('/') || /^[a-z][a-z0-9+.-]*:/i.test(url)) return 'bad-link'\n return undefined\n}\n\nexport interface AnnouncementInputFacts {\n title?: string\n category?: string\n importance?: string\n startsAt?: Date | string | null\n endsAt?: Date | string | null\n requiresAcknowledgement?: boolean\n acknowledgeBy?: Date | string | null\n links?: { label?: string; url?: string }[] | null\n}\n\n/** Why what was written cannot be kept, or nothing. Asked on every save - a draft is kept to the same rules. */\nexport function inputRefusal(input: AnnouncementInputFacts): string | undefined {\n const title = String(input.title ?? '').trim()\n if (!title) return 'no-title'\n if (title.length > MAX_TITLE) return 'title-too-long'\n if (input.category && !(ANNOUNCEMENT_CATEGORIES as readonly string[]).includes(input.category)) return 'unknown-category'\n if (input.importance && !(IMPORTANCES as readonly string[]).includes(input.importance)) return 'unknown-importance'\n const start = time(input.startsAt)\n const end = time(input.endsAt)\n if (start !== undefined && end !== undefined && end <= start) return 'ends-before-start'\n if (input.acknowledgeBy && !input.requiresAcknowledgement) return 'acknowledge-by-without-ask'\n const by = time(input.acknowledgeBy)\n if (by !== undefined && start !== undefined && by < start) return 'acknowledge-by-before-start'\n if ((input.links ?? []).length > MAX_LINKS) return 'too-many-links'\n for (const link of input.links ?? []) {\n const refusal = linkRefusal(link)\n if (refusal) return refusal\n }\n return undefined\n}\n\n/** Why it cannot go out, or nothing - it is written to the rules and names someone. */\nexport function publishRefusal(status: string, input: AnnouncementInputFacts, audienceCount: number): string | undefined {\n if (status !== 'DRAFT') return 'not-a-draft'\n return inputRefusal(input) ?? (audienceCount > 0 ? undefined : 'no-audience')\n}\n\n/** Who on the roster is still owed a reminder: not read yet, or not acknowledged when it asks for that. */\nexport function owedReminder(one: Pick<AnnouncementFacts, 'requiresAcknowledgement'>, receipt: ReceiptFacts & { listed?: boolean }): boolean {\n if (!receipt.listed) return false\n return one.requiresAcknowledgement ? !receipt.acknowledgedAt : !receipt.readAt\n}\n\n/** The body as one line of plain words - the notice's text, and the list's preview. */\nexport function plainSummary(body: string | null | undefined, max = 120): string {\n const text = String(body ?? '')\n .replace(/```[\\s\\S]*?```/g, ' ')\n .replace(/!\\[[^\\]]*\\]\\([^)]*\\)/g, ' ')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/^\\s{0,3}(#{1,6}|>|[-*+]|\\d+\\.)\\s+/gm, '')\n /* Only the marks that wrap words - a lone ~ (22시~24시), _ or | is part of the text. */\n .replace(/(\\*\\*|__|~~|`)/g, '')\n .replace(/(^|\\s)\\*(\\S[^*]*?)\\*(?=\\s|$|[.,!?])/g, '$1$2')\n .replace(/\\s+/g, ' ')\n .trim()\n return text.length > max ? `${text.slice(0, max - 1)}…` : text\n}\n\n/** The screen an announcement opens on. */\nexport const viewAddressOf = (id: string) => `announcement-view?id=${id}`\n\n/**\n * Whether its notice is said once to the whole domain - a Teams channel everyone reads (notification `broadcast`).\n * Only an announcement addressed to the whole domain: one for a department or a role must not land where everyone\n * reads it.\n */\nexport function broadcastOf(audiences: readonly { scope: string; targetId: string }[], domainId: string | undefined): boolean {\n return !!domainId && audiences.some(one => one.scope === 'DOMAIN' && one.targetId === domainId)\n}\n\n/** Why the people are told: it went out, it changed and they are told again, or they still owe it. */\nexport type NoticeKind = 'published' | 'changed' | 'reminder'\n\n/** A sentence in one language, with the English one when that language has none. */\nexport type Say = (key: string, english: string, params?: Record<string, unknown>) => string\n\n/**\n * What the notice of an announcement says to one person (notification's `NoticeWords`): its title - marked when it is\n * a change or a reminder - the start of its body for the inbox and web push, and the whole body, Markdown, for the mail\n * (ADR-0163 decision 6: the mail carries the announcement, files stay behind the sign-in).\n */\nexport function noticeWordsOf(one: { title: string; body?: string | null }, kind: NoticeKind, say: Say) {\n const subject =\n kind === 'changed'\n ? say('announcement.notice.changed-subject', 'Changed: {title}', { title: one.title })\n : kind === 'reminder'\n ? say('announcement.notice.reminder-subject', 'Reminder: {title}', { title: one.title })\n : one.title\n return {\n subject,\n summary: plainSummary(one.body),\n body: one.body ?? '',\n action: say('announcement.notice.action', 'Open the announcement'),\n note: say('announcement.notice.note', 'Open Announcements in the application to read it.')\n }\n}\n\nexport type NoticeCount = { receivers: number; email: { sent: number; failed: number }; teams: { sent: number; failed: number } }\n\n/** One telling as the announcement keeps it - when, why, and what went out. A send that could not go at all counts its receivers, nothing sent. */\nexport function deliveryOf(kind: NoticeKind, result: NoticeCount | undefined, receivers: number, at = new Date()) {\n return {\n at: at.toISOString(),\n kind,\n receivers: result?.receivers ?? receivers,\n email: result?.email ?? { sent: 0, failed: 0 },\n teams: result?.teams ?? { sent: 0, failed: 0 },\n ...(result ? {} : { failed: true })\n }\n}\n\n/** The record kept on an announcement - the newest tellings, at most `MAX_DELIVERIES`. */\nexport const MAX_DELIVERIES = 50\nexport function withDelivery<T>(deliveries: T[] | null | undefined, one: T): T[] {\n return [...(deliveries ?? []), one].slice(-MAX_DELIVERIES)\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { EntityManager } from 'typeorm';
|
|
2
|
+
import { type AudienceMember } from '@things-factory/organization';
|
|
3
|
+
import { Domain } from '@things-factory/shell';
|
|
4
|
+
import { Announcement, AnnouncementAudience } from './announcement';
|
|
5
|
+
import { type NoticeKind } from './announcement-rules';
|
|
6
|
+
/** The attachments of an announcement hang on it under this `refType` (attachment-base). */
|
|
7
|
+
export declare const ANNOUNCEMENT_REF_TYPE = "Announcement";
|
|
8
|
+
/** Whether the request writes announcements - the one privilege of the module (`announcement.writing`). */
|
|
9
|
+
export declare function isWriter(context: any): Promise<boolean>;
|
|
10
|
+
/** The request's person as an audience sees them - asked once per request. */
|
|
11
|
+
export declare function memberOf(context: any): Promise<AudienceMember>;
|
|
12
|
+
/** The announcements of the domain whose audience names `member` - by their targets, whatever their status. */
|
|
13
|
+
export declare function reachingIds(member: AudienceMember, tx?: EntityManager): Promise<string[]>;
|
|
14
|
+
export declare function audiencesOf(announcementId: string, tx?: EntityManager): Promise<AnnouncementAudience[]>;
|
|
15
|
+
/**
|
|
16
|
+
* The announcement as the request may see it, or nothing - one answer for "none", "not yours" and "not out yet".
|
|
17
|
+
* Its audience reads it once it is out; a writer reads every one that is not somebody else's draft.
|
|
18
|
+
*/
|
|
19
|
+
export declare function visibleAnnouncement(id: string, context: any, tx?: EntityManager): Promise<{
|
|
20
|
+
announcement: Announcement;
|
|
21
|
+
reader: boolean;
|
|
22
|
+
writer: boolean;
|
|
23
|
+
} | undefined>;
|
|
24
|
+
/**
|
|
25
|
+
* Puts the roster in line with the audience as it stands: everyone it names now is listed, one it no longer names is
|
|
26
|
+
* not (their reading stays). Answers those newly listed - the ones not yet told.
|
|
27
|
+
*/
|
|
28
|
+
export declare function alignRoster(announcement: Announcement, domain: Domain, by: {
|
|
29
|
+
id: string;
|
|
30
|
+
}, tx: EntityManager): Promise<string[]>;
|
|
31
|
+
/** The version as it stands, kept - the one that went out, and each change after. */
|
|
32
|
+
export declare function keepRevision(announcement: Announcement, domain: Domain, by: {
|
|
33
|
+
id: string;
|
|
34
|
+
}, tx: EntityManager): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Sends it out: published now, the roster counted, this version kept. Answers the people to tell - the publisher is not
|
|
37
|
+
* told of their own announcement.
|
|
38
|
+
*/
|
|
39
|
+
export declare function sendOut(announcement: Announcement, domain: Domain, by: {
|
|
40
|
+
id: string;
|
|
41
|
+
}, tx: EntityManager): Promise<string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Tells `userIds` of it once the request's transaction has committed - a reader told before it would not find it yet -
|
|
44
|
+
* and keeps what went out. A notice that does not arrive does not undo the announcement: its list, badge and popup
|
|
45
|
+
* still reach them.
|
|
46
|
+
*/
|
|
47
|
+
export declare function tellWhenCommitted(context: any, one: Announcement, kind: NoticeKind, userIds: string[], toEveryone?: boolean): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Publishes the scheduled announcements whose start has come. Each is taken by one update from SCHEDULED, so a server
|
|
50
|
+
* among several that loses the race does not send it twice.
|
|
51
|
+
*/
|
|
52
|
+
export declare function publishDue(now?: Date): Promise<number>;
|
|
53
|
+
/** Looks for scheduled announcements every minute. The timer never keeps the process alive. */
|
|
54
|
+
export declare function schedulePublishing(): void;
|