@things-factory/gallery 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/bootstrap.d.ts +2 -0
- package/dist-client/bootstrap.js +26 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/components/gallery-forms.d.ts +31 -0
- package/dist-client/components/gallery-forms.js +223 -0
- package/dist-client/components/gallery-forms.js.map +1 -0
- package/dist-client/components/gallery-saver-setting-let.d.ts +11 -0
- package/dist-client/components/gallery-saver-setting-let.js +81 -0
- package/dist-client/components/gallery-saver-setting-let.js.map +1 -0
- package/dist-client/components/gallery-show.d.ts +46 -0
- package/dist-client/components/gallery-show.js +316 -0
- package/dist-client/components/gallery-show.js.map +1 -0
- package/dist-client/components/gallery-uploader.d.ts +57 -0
- package/dist-client/components/gallery-uploader.js +624 -0
- package/dist-client/components/gallery-uploader.js.map +1 -0
- package/dist-client/components/gallery-viewer.d.ts +30 -0
- package/dist-client/components/gallery-viewer.js +429 -0
- package/dist-client/components/gallery-viewer.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/gallery-page.d.ts +54 -0
- package/dist-client/pages/gallery-page.js +590 -0
- package/dist-client/pages/gallery-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +9 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/utils/gallery-api.d.ts +99 -0
- package/dist-client/utils/gallery-api.js +46 -0
- package/dist-client/utils/gallery-api.js.map +1 -0
- package/dist-client/utils/gallery-badge.d.ts +4 -0
- package/dist-client/utils/gallery-badge.js +45 -0
- package/dist-client/utils/gallery-badge.js.map +1 -0
- package/dist-client/utils/gallery-saver.d.ts +6 -0
- package/dist-client/utils/gallery-saver.js +69 -0
- package/dist-client/utils/gallery-saver.js.map +1 -0
- package/dist-client/utils/gallery-words.d.ts +7 -0
- package/dist-client/utils/gallery-words.js +20 -0
- package/dist-client/utils/gallery-words.js.map +1 -0
- package/dist-client/utils/photo-prep.d.ts +10 -0
- package/dist-client/utils/photo-prep.js +102 -0
- package/dist-client/utils/photo-prep.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 +1 -0
- package/dist-server/index.js +11 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +15 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/service/gallery/gallery-resolver.d.ts +29 -0
- package/dist-server/service/gallery/gallery-resolver.js +613 -0
- package/dist-server/service/gallery/gallery-resolver.js.map +1 -0
- package/dist-server/service/gallery/gallery-rules.d.ts +90 -0
- package/dist-server/service/gallery/gallery-rules.js +182 -0
- package/dist-server/service/gallery/gallery-rules.js.map +1 -0
- package/dist-server/service/gallery/gallery-service.d.ts +37 -0
- package/dist-server/service/gallery/gallery-service.js +234 -0
- package/dist-server/service/gallery/gallery-service.js.map +1 -0
- package/dist-server/service/gallery/gallery-types.d.ts +65 -0
- package/dist-server/service/gallery/gallery-types.js +239 -0
- package/dist-server/service/gallery/gallery-types.js.map +1 -0
- package/dist-server/service/gallery/gallery.d.ts +84 -0
- package/dist-server/service/gallery/gallery.js +335 -0
- package/dist-server/service/gallery/gallery.js.map +1 -0
- package/dist-server/service/gallery/index.d.ts +4 -0
- package/dist-server/service/gallery/index.js +8 -0
- package/dist-server/service/gallery/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,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The gallery doors as the screens call them (ADR-0165). Every call throws the server's words when it is refused.
|
|
3
|
+
*/
|
|
4
|
+
import gql from 'graphql-tag';
|
|
5
|
+
import { client, uploadContext } from '@operato/graphql';
|
|
6
|
+
const POST_FIELDS = `id space shared caption takenAt tags likeCount liked editable moderatable hiddenAt hiddenReason createdAt
|
|
7
|
+
creator { id name } event { id name startAt endAt location } photos { id name thumbnail fullpath mimetype }`;
|
|
8
|
+
const EVENT_FIELDS = 'id name startAt endAt location';
|
|
9
|
+
function answer(response, field) {
|
|
10
|
+
if (response.errors?.length)
|
|
11
|
+
throw new Error(response.errors.map((one) => one.message).join(' · '));
|
|
12
|
+
return response.data?.[field];
|
|
13
|
+
}
|
|
14
|
+
async function ask(query, variables, field) {
|
|
15
|
+
return answer(await client.query({ query: gql(query), variables, fetchPolicy: 'network-only' }), field);
|
|
16
|
+
}
|
|
17
|
+
async function change(mutation, variables, field, uploading) {
|
|
18
|
+
return answer(await client.mutate({
|
|
19
|
+
mutation: gql(mutation),
|
|
20
|
+
variables,
|
|
21
|
+
...(uploading ? { context: uploadContext(uploading) } : {})
|
|
22
|
+
}), field);
|
|
23
|
+
}
|
|
24
|
+
/** Tells the open gallery and the menu's count that a post came, changed or went. */
|
|
25
|
+
export const galleryChanged = () => window.dispatchEvent(new CustomEvent('gallery-changed'));
|
|
26
|
+
export const galleryApi = {
|
|
27
|
+
posts: (filter, skip = 0, take = 30) => ask(`query ($filter: GalleryFilter, $skip: Int, $take: Int) { galleryPosts(filter: $filter, skip: $skip, take: $take) { items { ${POST_FIELDS} } total } }`, { filter, skip, take }, 'galleryPosts'),
|
|
28
|
+
post: (id) => ask(`query ($id: String!) { galleryPost(id: $id) { ${POST_FIELDS} } }`, { id }, 'galleryPost'),
|
|
29
|
+
likers: (id) => ask(`query ($id: String!) { galleryLikers(id: $id) { id name } }`, { id }, 'galleryLikers'),
|
|
30
|
+
tags: (text = '') => ask(`query ($text: String) { galleryTags(text: $text) { tag count } }`, { text }, 'galleryTags'),
|
|
31
|
+
events: () => ask(`query { galleryEvents { ${EVENT_FIELDS} postCount cover { id thumbnail fullpath } } }`, {}, 'galleryEvents'),
|
|
32
|
+
eventChoices: (takenAt) => ask(`query ($takenAt: DateTimeISO) { galleryEventChoices(takenAt: $takenAt) { ${EVENT_FIELDS} near } }`, { takenAt }, 'galleryEventChoices'),
|
|
33
|
+
summary: () => ask(`query { gallerySummary { fresh } }`, {}, 'gallerySummary'),
|
|
34
|
+
create: (input, photos, uploading) => change(`mutation ($input: GalleryPostInput!, $photos: [Upload!]!) { createGalleryPost(input: $input, photos: $photos) { ${POST_FIELDS} } }`, { input, photos }, 'createGalleryPost', uploading ?? (() => { })),
|
|
35
|
+
update: (id, input, attach, remove, uploading) => change(`mutation ($id: String!, $input: GalleryPostInput!, $attach: [Upload!], $remove: [String!]) {
|
|
36
|
+
updateGalleryPost(id: $id, input: $input, attach: $attach, remove: $remove) { ${POST_FIELDS} }
|
|
37
|
+
}`, { id, input, attach, remove }, 'updateGalleryPost', attach.length ? (uploading ?? (() => { })) : undefined),
|
|
38
|
+
remove: (id) => change(`mutation ($id: String!) { deleteGalleryPost(id: $id) }`, { id }, 'deleteGalleryPost'),
|
|
39
|
+
like: (id, like) => change(`mutation ($id: String!, $like: Boolean!) { likeGalleryPost(id: $id, like: $like) { id likeCount liked } }`, { id, like }, 'likeGalleryPost'),
|
|
40
|
+
visit: () => change(`mutation { visitGallery }`, {}, 'visitGallery'),
|
|
41
|
+
hide: (id, reason) => change(`mutation ($id: String!, $reason: String!) { hideGalleryPost(id: $id, reason: $reason) { ${POST_FIELDS} } }`, { id, reason }, 'hideGalleryPost'),
|
|
42
|
+
unhide: (id) => change(`mutation ($id: String!) { unhideGalleryPost(id: $id) { ${POST_FIELDS} } }`, { id }, 'unhideGalleryPost'),
|
|
43
|
+
renameTag: (from, to) => change(`mutation ($from: String!, $to: String!) { renameGalleryTag(from: $from, to: $to) }`, { from, to }, 'renameGalleryTag'),
|
|
44
|
+
createEvent: (input) => change(`mutation ($input: GalleryEventInput!) { createGalleryEvent(input: $input) { ${EVENT_FIELDS} postCount } }`, { input }, 'createGalleryEvent')
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=gallery-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gallery-api.js","sourceRoot":"","sources":["../../client/utils/gallery-api.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The gallery doors as the screens call them (ADR-0165). Every call throws the server's words when it is refused.\n */\nimport gql from 'graphql-tag'\n\nimport { client, uploadContext } from '@operato/graphql'\n\nexport type Space = 'COMPANY' | 'PERSONAL'\nexport type Photo = { id: string; name?: string; thumbnail?: string | null; fullpath: string; mimetype?: string }\nexport type EventFacts = { id: string; name: string; startAt: string; endAt: string; location?: string | null }\nexport type EventCard = EventFacts & { postCount: number; cover?: Photo | null }\nexport type EventChoice = EventFacts & { near: boolean }\n\nexport interface Post {\n id: string\n space: Space\n shared: boolean\n caption?: string | null\n takenAt: string\n tags?: string[] | null\n likeCount: number\n liked: boolean\n editable: boolean\n moderatable: boolean\n hiddenAt?: string | null\n hiddenReason?: string | null\n createdAt: string\n creator?: { id: string; name?: string } | null\n event?: EventFacts | null\n photos: Photo[]\n}\n\nexport interface PostInput {\n space?: Space\n shared?: boolean\n caption?: string\n takenAt?: string\n tags?: string[]\n eventId?: string | null\n}\n\nexport type Filter = { space?: Space; eventId?: string; tag?: string; authorId?: string; mine?: boolean; everyone?: boolean }\n\nconst POST_FIELDS = `id space shared caption takenAt tags likeCount liked editable moderatable hiddenAt hiddenReason createdAt\n creator { id name } event { id name startAt endAt location } photos { id name thumbnail fullpath mimetype }`\nconst EVENT_FIELDS = 'id name startAt endAt location'\n\nfunction answer<T>(response: any, field: string): T {\n if (response.errors?.length) throw new Error(response.errors.map((one: any) => one.message).join(' · '))\n return response.data?.[field] as T\n}\n\nasync function ask<T>(query: string, variables: Record<string, unknown>, field: string): Promise<T> {\n return answer<T>(await client.query({ query: gql(query), variables, fetchPolicy: 'network-only' }), field)\n}\n\nasync function change<T>(\n mutation: string,\n variables: Record<string, unknown>,\n field: string,\n uploading?: (fraction: number) => void\n): Promise<T> {\n return answer<T>(\n await client.mutate({\n mutation: gql(mutation),\n variables,\n ...(uploading ? { context: uploadContext(uploading) } : {})\n }),\n field\n )\n}\n\n/** Tells the open gallery and the menu's count that a post came, changed or went. */\nexport const galleryChanged = () => window.dispatchEvent(new CustomEvent('gallery-changed'))\n\nexport const galleryApi = {\n posts: (filter: Filter, skip = 0, take = 30) =>\n ask<{ items: Post[]; total: number }>(\n `query ($filter: GalleryFilter, $skip: Int, $take: Int) { galleryPosts(filter: $filter, skip: $skip, take: $take) { items { ${POST_FIELDS} } total } }`,\n { filter, skip, take },\n 'galleryPosts'\n ),\n post: (id: string) =>\n ask<Post | null>(`query ($id: String!) { galleryPost(id: $id) { ${POST_FIELDS} } }`, { id }, 'galleryPost'),\n likers: (id: string) =>\n ask<{ id: string; name?: string }[]>(\n `query ($id: String!) { galleryLikers(id: $id) { id name } }`,\n { id },\n 'galleryLikers'\n ),\n tags: (text = '') =>\n ask<{ tag: string; count: number }[]>(\n `query ($text: String) { galleryTags(text: $text) { tag count } }`,\n { text },\n 'galleryTags'\n ),\n events: () =>\n ask<EventCard[]>(\n `query { galleryEvents { ${EVENT_FIELDS} postCount cover { id thumbnail fullpath } } }`,\n {},\n 'galleryEvents'\n ),\n eventChoices: (takenAt?: string) =>\n ask<EventChoice[]>(\n `query ($takenAt: DateTimeISO) { galleryEventChoices(takenAt: $takenAt) { ${EVENT_FIELDS} near } }`,\n { takenAt },\n 'galleryEventChoices'\n ),\n summary: () => ask<{ fresh: number }>(`query { gallerySummary { fresh } }`, {}, 'gallerySummary'),\n create: (input: PostInput, photos: File[], uploading?: (fraction: number) => void) =>\n change<Post>(\n `mutation ($input: GalleryPostInput!, $photos: [Upload!]!) { createGalleryPost(input: $input, photos: $photos) { ${POST_FIELDS} } }`,\n { input, photos },\n 'createGalleryPost',\n uploading ?? (() => {})\n ),\n update: (id: string, input: PostInput, attach: File[], remove: string[], uploading?: (fraction: number) => void) =>\n change<Post>(\n `mutation ($id: String!, $input: GalleryPostInput!, $attach: [Upload!], $remove: [String!]) {\n updateGalleryPost(id: $id, input: $input, attach: $attach, remove: $remove) { ${POST_FIELDS} }\n }`,\n { id, input, attach, remove },\n 'updateGalleryPost',\n attach.length ? (uploading ?? (() => {})) : undefined\n ),\n remove: (id: string) =>\n change<boolean>(`mutation ($id: String!) { deleteGalleryPost(id: $id) }`, { id }, 'deleteGalleryPost'),\n like: (id: string, like: boolean) =>\n change<{ id: string; likeCount: number; liked: boolean }>(\n `mutation ($id: String!, $like: Boolean!) { likeGalleryPost(id: $id, like: $like) { id likeCount liked } }`,\n { id, like },\n 'likeGalleryPost'\n ),\n visit: () => change<boolean>(`mutation { visitGallery }`, {}, 'visitGallery'),\n hide: (id: string, reason: string) =>\n change<Post>(\n `mutation ($id: String!, $reason: String!) { hideGalleryPost(id: $id, reason: $reason) { ${POST_FIELDS} } }`,\n { id, reason },\n 'hideGalleryPost'\n ),\n unhide: (id: string) =>\n change<Post>(\n `mutation ($id: String!) { unhideGalleryPost(id: $id) { ${POST_FIELDS} } }`,\n { id },\n 'unhideGalleryPost'\n ),\n renameTag: (from: string, to: string) =>\n change<number>(\n `mutation ($from: String!, $to: String!) { renameGalleryTag(from: $from, to: $to) }`,\n { from, to },\n 'renameGalleryTag'\n ),\n createEvent: (input: { name: string; startAt: string; endAt: string; location?: string }) =>\n change<EventCard>(\n `mutation ($input: GalleryEventInput!) { createGalleryEvent(input: $input) { ${EVENT_FIELDS} postCount } }`,\n { input },\n 'createGalleryEvent'\n )\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Asks the server again and tells every badge. A count that could not be asked is not zero: it draws nothing. */
|
|
2
|
+
export declare function refreshGalleryBadge(): Promise<void>;
|
|
3
|
+
/** One badge's counts. The menu asks for a new one when it draws again; the one before it then ends. */
|
|
4
|
+
export declare function generateGalleryBadge(): AsyncGenerator<number | undefined, void, unknown>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The count the menu shows on 갤러리 - posts others made since one last opened it (ADR-0165 decision 6). An application
|
|
3
|
+
* puts `generateGalleryBadge` on its menu entry; the count is asked again when a post comes or the gallery is opened.
|
|
4
|
+
*/
|
|
5
|
+
import { galleryApi } from './gallery-api.js';
|
|
6
|
+
let count;
|
|
7
|
+
let version = 0;
|
|
8
|
+
const waiting = new Set();
|
|
9
|
+
/** Asks the server again and tells every badge. A count that could not be asked is not zero: it draws nothing. */
|
|
10
|
+
export async function refreshGalleryBadge() {
|
|
11
|
+
try {
|
|
12
|
+
count = (await galleryApi.summary()).fresh || undefined;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
count = undefined;
|
|
16
|
+
}
|
|
17
|
+
version++;
|
|
18
|
+
for (const wake of [...waiting])
|
|
19
|
+
wake();
|
|
20
|
+
}
|
|
21
|
+
let generation = 0;
|
|
22
|
+
/** One badge's counts. The menu asks for a new one when it draws again; the one before it then ends. */
|
|
23
|
+
export async function* generateGalleryBadge() {
|
|
24
|
+
const mine = ++generation;
|
|
25
|
+
for (const wake of [...waiting])
|
|
26
|
+
wake();
|
|
27
|
+
let seen = version;
|
|
28
|
+
yield count;
|
|
29
|
+
while (true) {
|
|
30
|
+
if (seen === version) {
|
|
31
|
+
await new Promise(resolve => {
|
|
32
|
+
const wake = () => {
|
|
33
|
+
waiting.delete(wake);
|
|
34
|
+
resolve();
|
|
35
|
+
};
|
|
36
|
+
waiting.add(wake);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (mine !== generation)
|
|
40
|
+
return;
|
|
41
|
+
seen = version;
|
|
42
|
+
yield count;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=gallery-badge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gallery-badge.js","sourceRoot":"","sources":["../../client/utils/gallery-badge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,IAAI,KAAyB,CAAA;AAC7B,IAAI,OAAO,GAAG,CAAC,CAAA;AACf,MAAM,OAAO,GAAG,IAAI,GAAG,EAAc,CAAA;AAErC,kHAAkH;AAClH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC;QACH,KAAK,GAAG,CAAC,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,IAAI,SAAS,CAAA;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,SAAS,CAAA;IACnB,CAAC;IACD,OAAO,EAAE,CAAA;IACT,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,CAAA;AACzC,CAAC;AAED,IAAI,UAAU,GAAG,CAAC,CAAA;AAElB,wGAAwG;AACxG,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,oBAAoB;IACzC,MAAM,IAAI,GAAG,EAAE,UAAU,CAAA;IACzB,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,CAAA;IACvC,IAAI,IAAI,GAAG,OAAO,CAAA;IAClB,MAAM,KAAK,CAAA;IACX,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE;gBAChC,MAAM,IAAI,GAAG,GAAG,EAAE;oBAChB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;oBACpB,OAAO,EAAE,CAAA;gBACX,CAAC,CAAA;gBACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACnB,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,IAAI,IAAI,KAAK,UAAU;YAAE,OAAM;QAC/B,IAAI,GAAG,OAAO,CAAA;QACd,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC","sourcesContent":["/*\n * The count the menu shows on 갤러리 - posts others made since one last opened it (ADR-0165 decision 6). An application\n * puts `generateGalleryBadge` on its menu entry; the count is asked again when a post comes or the gallery is opened.\n */\nimport { galleryApi } from './gallery-api.js'\n\nlet count: number | undefined\nlet version = 0\nconst waiting = new Set<() => void>()\n\n/** Asks the server again and tells every badge. A count that could not be asked is not zero: it draws nothing. */\nexport async function refreshGalleryBadge(): Promise<void> {\n try {\n count = (await galleryApi.summary()).fresh || undefined\n } catch {\n count = undefined\n }\n version++\n for (const wake of [...waiting]) wake()\n}\n\nlet generation = 0\n\n/** One badge's counts. The menu asks for a new one when it draws again; the one before it then ends. */\nexport async function* generateGalleryBadge() {\n const mine = ++generation\n for (const wake of [...waiting]) wake()\n let seen = version\n yield count\n while (true) {\n if (seen === version) {\n await new Promise<void>(resolve => {\n const wake = () => {\n waiting.delete(wake)\n resolve()\n }\n waiting.add(wake)\n })\n }\n if (mine !== generation) return\n seen = version\n yield count\n }\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The waits the setting offers, in minutes; 0 is off. */
|
|
2
|
+
export declare const SAVER_MINUTES: number[];
|
|
3
|
+
export declare function saverMinutes(): Promise<number>;
|
|
4
|
+
export declare function setSaverMinutes(minutes: number): Promise<void>;
|
|
5
|
+
/** Watches for the quiet stretch, once per application. */
|
|
6
|
+
export declare function watchForSaver(): void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The gallery as the screen saver (ADR-0165 decision 9): after the minutes set on this device with no press, move or
|
|
3
|
+
* key, the gallery everyone sees - the company's and the shared own photos - plays over the window until the person
|
|
4
|
+
* comes back. Kept on the device (`clientSettingStore`),
|
|
5
|
+
* since it is the screen that is left - a meeting room's display, not the person - and off until it is turned on.
|
|
6
|
+
*/
|
|
7
|
+
import { clientSettingStore } from '@operato/shell';
|
|
8
|
+
import { auth } from '@things-factory/auth-base/dist-client';
|
|
9
|
+
import { GalleryShow } from '../components/gallery-show.js';
|
|
10
|
+
const KEY = 'gallery-saver';
|
|
11
|
+
/** The waits the setting offers, in minutes; 0 is off. */
|
|
12
|
+
export const SAVER_MINUTES = [0, 5, 10, 30];
|
|
13
|
+
/** What says the person is here. */
|
|
14
|
+
const PRESENCE = ['pointermove', 'pointerdown', 'keydown', 'wheel', 'touchstart'];
|
|
15
|
+
export async function saverMinutes() {
|
|
16
|
+
try {
|
|
17
|
+
const minutes = Number((await clientSettingStore.get(KEY))?.value?.minutes);
|
|
18
|
+
return SAVER_MINUTES.includes(minutes) ? minutes : 0;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function setSaverMinutes(minutes) {
|
|
25
|
+
await clientSettingStore.put({ key: KEY, value: { minutes } });
|
|
26
|
+
window.dispatchEvent(new CustomEvent('gallery-saver-changed'));
|
|
27
|
+
}
|
|
28
|
+
let minutes = 0;
|
|
29
|
+
let timer;
|
|
30
|
+
let signedIn = false;
|
|
31
|
+
let showing;
|
|
32
|
+
function wait() {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
if (!minutes || !signedIn || showing)
|
|
35
|
+
return;
|
|
36
|
+
timer = setTimeout(start, minutes * 60_000);
|
|
37
|
+
}
|
|
38
|
+
function start() {
|
|
39
|
+
/* A hidden tab is not looked at; it waits for the next quiet stretch after it is shown. */
|
|
40
|
+
if (document.visibilityState !== 'visible' || document.querySelector('gallery-show'))
|
|
41
|
+
return wait();
|
|
42
|
+
/* What everyone sees - never the photos the device's person keeps to themselves. */
|
|
43
|
+
showing = GalleryShow.open({ filter: { everyone: true }, saver: true });
|
|
44
|
+
showing.addEventListener('closed', () => {
|
|
45
|
+
showing = undefined;
|
|
46
|
+
wait();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** Watches for the quiet stretch, once per application. */
|
|
50
|
+
export function watchForSaver() {
|
|
51
|
+
const read = async () => {
|
|
52
|
+
minutes = await saverMinutes();
|
|
53
|
+
wait();
|
|
54
|
+
};
|
|
55
|
+
for (const name of PRESENCE)
|
|
56
|
+
window.addEventListener(name, () => !showing && wait(), { passive: true, capture: true });
|
|
57
|
+
document.addEventListener('visibilitychange', () => document.visibilityState === 'visible' && wait());
|
|
58
|
+
window.addEventListener('gallery-saver-changed', read);
|
|
59
|
+
auth.on('profile', () => {
|
|
60
|
+
signedIn = true;
|
|
61
|
+
read();
|
|
62
|
+
});
|
|
63
|
+
auth.on('signout', () => {
|
|
64
|
+
signedIn = false;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
showing?.close();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=gallery-saver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gallery-saver.js","sourceRoot":"","sources":["../../client/utils/gallery-saver.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The gallery as the screen saver (ADR-0165 decision 9): after the minutes set on this device with no press, move or\n * key, the gallery everyone sees - the company's and the shared own photos - plays over the window until the person\n * comes back. Kept on the device (`clientSettingStore`),\n * since it is the screen that is left - a meeting room's display, not the person - and off until it is turned on.\n */\nimport { clientSettingStore } from '@operato/shell'\nimport { auth } from '@things-factory/auth-base/dist-client'\n\nimport { GalleryShow } from '../components/gallery-show.js'\n\nconst KEY = 'gallery-saver'\n/** The waits the setting offers, in minutes; 0 is off. */\nexport const SAVER_MINUTES = [0, 5, 10, 30]\n/** What says the person is here. */\nconst PRESENCE = ['pointermove', 'pointerdown', 'keydown', 'wheel', 'touchstart']\n\nexport async function saverMinutes(): Promise<number> {\n try {\n const minutes = Number((await clientSettingStore.get(KEY))?.value?.minutes)\n return SAVER_MINUTES.includes(minutes) ? minutes : 0\n } catch {\n return 0\n }\n}\n\nexport async function setSaverMinutes(minutes: number) {\n await clientSettingStore.put({ key: KEY, value: { minutes } })\n window.dispatchEvent(new CustomEvent('gallery-saver-changed'))\n}\n\nlet minutes = 0\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet signedIn = false\nlet showing: GalleryShow | undefined\n\nfunction wait() {\n clearTimeout(timer)\n if (!minutes || !signedIn || showing) return\n timer = setTimeout(start, minutes * 60_000)\n}\n\nfunction start() {\n /* A hidden tab is not looked at; it waits for the next quiet stretch after it is shown. */\n if (document.visibilityState !== 'visible' || document.querySelector('gallery-show')) return wait()\n /* What everyone sees - never the photos the device's person keeps to themselves. */\n showing = GalleryShow.open({ filter: { everyone: true }, saver: true })\n showing.addEventListener('closed', () => {\n showing = undefined\n wait()\n })\n}\n\n/** Watches for the quiet stretch, once per application. */\nexport function watchForSaver() {\n const read = async () => {\n minutes = await saverMinutes()\n wait()\n }\n for (const name of PRESENCE) window.addEventListener(name, () => !showing && wait(), { passive: true, capture: true })\n document.addEventListener('visibilitychange', () => document.visibilityState === 'visible' && wait())\n window.addEventListener('gallery-saver-changed', read)\n auth.on('profile', () => {\n signedIn = true\n read()\n })\n auth.on('signout', () => {\n signedIn = false\n clearTimeout(timer)\n showing?.close()\n })\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const t: (key: string, options?: Record<string, unknown>) => string;
|
|
2
|
+
/** A day as the reader's locale writes it. */
|
|
3
|
+
export declare function day(value?: string | Date | null): string;
|
|
4
|
+
/** An event's days - one day, or its first and last. */
|
|
5
|
+
export declare function days(startAt: string, endAt: string): string;
|
|
6
|
+
/** The words of a refusal the server gave, or the fallback. */
|
|
7
|
+
export declare const reasonOf: (error: unknown) => string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The words of the gallery - one place for the page, the viewer and the uploader.
|
|
3
|
+
*/
|
|
4
|
+
import { i18next } from '@operato/i18n';
|
|
5
|
+
export const t = (key, options = {}) => String(i18next.t(key, options));
|
|
6
|
+
/** A day as the reader's locale writes it. */
|
|
7
|
+
export function day(value) {
|
|
8
|
+
if (!value)
|
|
9
|
+
return '';
|
|
10
|
+
return new Date(value).toLocaleDateString(i18next.language, { year: 'numeric', month: 'short', day: 'numeric' });
|
|
11
|
+
}
|
|
12
|
+
/** An event's days - one day, or its first and last. */
|
|
13
|
+
export function days(startAt, endAt) {
|
|
14
|
+
const start = new Date(startAt);
|
|
15
|
+
const end = new Date(endAt);
|
|
16
|
+
return start.toDateString() === end.toDateString() ? day(start) : `${day(start)} – ${day(end)}`;
|
|
17
|
+
}
|
|
18
|
+
/** The words of a refusal the server gave, or the fallback. */
|
|
19
|
+
export const reasonOf = (error) => error?.message || t('gallery.text.failed');
|
|
20
|
+
//# sourceMappingURL=gallery-words.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gallery-words.js","sourceRoot":"","sources":["../../client/utils/gallery-words.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAW,EAAE,UAAmC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;AAExG,8CAA8C;AAC9C,MAAM,UAAU,GAAG,CAAC,KAA4B;IAC9C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAA;AAClH,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,IAAI,CAAC,OAAe,EAAE,KAAa;IACjD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAA;IAC3B,OAAO,KAAK,CAAC,YAAY,EAAE,KAAK,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,CAAA;AACjG,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAE,EAAE,CAAE,KAAe,EAAE,OAAO,IAAI,CAAC,CAAC,qBAAqB,CAAC,CAAA","sourcesContent":["/*\n * The words of the gallery - one place for the page, the viewer and the uploader.\n */\nimport { i18next } from '@operato/i18n'\n\nexport const t = (key: string, options: Record<string, unknown> = {}) => String(i18next.t(key, options))\n\n/** A day as the reader's locale writes it. */\nexport function day(value?: string | Date | null): string {\n if (!value) return ''\n return new Date(value).toLocaleDateString(i18next.language, { year: 'numeric', month: 'short', day: 'numeric' })\n}\n\n/** An event's days - one day, or its first and last. */\nexport function days(startAt: string, endAt: string): string {\n const start = new Date(startAt)\n const end = new Date(endAt)\n return start.toDateString() === end.toDateString() ? day(start) : `${day(start)} – ${day(end)}`\n}\n\n/** The words of a refusal the server gave, or the fallback. */\nexport const reasonOf = (error: unknown) => (error as Error)?.message || t('gallery.text.failed')\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The longest side a photo keeps. */
|
|
2
|
+
export declare const LONG_SIDE = 2560;
|
|
3
|
+
/** "YYYY:MM:DD HH:MM:SS" as the camera wrote it, in the camera's local time. */
|
|
4
|
+
export declare function exifDateOf(text?: string): Date | undefined;
|
|
5
|
+
/** The date the camera recorded the photo, from a JPEG's EXIF - when it was taken (0x9003), else when it was saved (0x0132). */
|
|
6
|
+
export declare function exifTakenAt(buffer: ArrayBuffer): Date | undefined;
|
|
7
|
+
/** When the photo was taken - the camera's record, or nothing. */
|
|
8
|
+
export declare function takenAtOf(file: File): Promise<Date | undefined>;
|
|
9
|
+
/** The photo shrunk to `LONG_SIDE` on its long side as a JPEG - or the photo itself when it is small or cannot be drawn. */
|
|
10
|
+
export declare function shrunk(file: File): Promise<File>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A photo made ready in the browser before it goes up (ADR-0165 decision 3): when it was taken, read from the camera's
|
|
3
|
+
* own record (EXIF), and the photo shrunk to 2560 px on its long side so a phone's original does not fill the storage.
|
|
4
|
+
*/
|
|
5
|
+
/** The longest side a photo keeps. */
|
|
6
|
+
export const LONG_SIDE = 2560;
|
|
7
|
+
/* A photo already small enough goes up as it is - shrinking again would only lose quality. */
|
|
8
|
+
const SMALL_ENOUGH = 2.5 * 1024 * 1024;
|
|
9
|
+
/** "YYYY:MM:DD HH:MM:SS" as the camera wrote it, in the camera's local time. */
|
|
10
|
+
export function exifDateOf(text) {
|
|
11
|
+
const match = /^(\d{4}):(\d{2}):(\d{2})[ T](\d{2}):(\d{2}):(\d{2})/.exec(text ?? '');
|
|
12
|
+
if (!match)
|
|
13
|
+
return undefined;
|
|
14
|
+
const [, y, mo, d, h, mi, s] = match.map(Number);
|
|
15
|
+
const date = new Date(y, mo - 1, d, h, mi, s);
|
|
16
|
+
return isNaN(date.getTime()) || y < 1990 ? undefined : date;
|
|
17
|
+
}
|
|
18
|
+
/** The date the camera recorded the photo, from a JPEG's EXIF - when it was taken (0x9003), else when it was saved (0x0132). */
|
|
19
|
+
export function exifTakenAt(buffer) {
|
|
20
|
+
try {
|
|
21
|
+
const view = new DataView(buffer);
|
|
22
|
+
if (view.byteLength < 4 || view.getUint16(0) !== 0xffd8)
|
|
23
|
+
return undefined;
|
|
24
|
+
let offset = 2;
|
|
25
|
+
while (offset + 10 <= view.byteLength) {
|
|
26
|
+
const marker = view.getUint16(offset);
|
|
27
|
+
if ((marker & 0xff00) !== 0xff00)
|
|
28
|
+
return undefined;
|
|
29
|
+
const size = view.getUint16(offset + 2);
|
|
30
|
+
/* APP1 starting with "Exif\0\0" */
|
|
31
|
+
if (marker === 0xffe1 && view.getUint32(offset + 4) === 0x45786966)
|
|
32
|
+
return exifDateOf(tiffDate(view, offset + 10));
|
|
33
|
+
offset += 2 + size;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* A broken or cut record says nothing. */
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
function tiffDate(view, start) {
|
|
42
|
+
const little = view.getUint16(start) === 0x4949;
|
|
43
|
+
const u16 = (at) => view.getUint16(start + at, little);
|
|
44
|
+
const u32 = (at) => view.getUint32(start + at, little);
|
|
45
|
+
const entries = (ifd) => Array.from({ length: u16(ifd) }, (_, i) => ifd + 2 + i * 12);
|
|
46
|
+
const ascii = (entry) => {
|
|
47
|
+
const count = u32(entry + 4);
|
|
48
|
+
const at = count > 4 ? u32(entry + 8) : entry + 8;
|
|
49
|
+
let text = '';
|
|
50
|
+
for (let i = 0; i < count - 1; i++)
|
|
51
|
+
text += String.fromCharCode(view.getUint8(start + at + i));
|
|
52
|
+
return text;
|
|
53
|
+
};
|
|
54
|
+
let saved;
|
|
55
|
+
let exif;
|
|
56
|
+
for (const entry of entries(u32(4))) {
|
|
57
|
+
const tag = u16(entry);
|
|
58
|
+
if (tag === 0x0132)
|
|
59
|
+
saved = ascii(entry);
|
|
60
|
+
if (tag === 0x8769)
|
|
61
|
+
exif = u32(entry + 8);
|
|
62
|
+
}
|
|
63
|
+
if (exif)
|
|
64
|
+
for (const entry of entries(exif))
|
|
65
|
+
if (u16(entry) === 0x9003)
|
|
66
|
+
return ascii(entry);
|
|
67
|
+
return saved;
|
|
68
|
+
}
|
|
69
|
+
/** When the photo was taken - the camera's record, or nothing. */
|
|
70
|
+
export async function takenAtOf(file) {
|
|
71
|
+
if (!/jpe?g/i.test(file.type) && !/\.jpe?g$/i.test(file.name))
|
|
72
|
+
return undefined;
|
|
73
|
+
return exifTakenAt(await file.slice(0, 256 * 1024).arrayBuffer());
|
|
74
|
+
}
|
|
75
|
+
/** The photo shrunk to `LONG_SIDE` on its long side as a JPEG - or the photo itself when it is small or cannot be drawn. */
|
|
76
|
+
export async function shrunk(file) {
|
|
77
|
+
try {
|
|
78
|
+
const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' });
|
|
79
|
+
const long = Math.max(bitmap.width, bitmap.height);
|
|
80
|
+
if (long <= LONG_SIDE && file.size <= SMALL_ENOUGH) {
|
|
81
|
+
bitmap.close();
|
|
82
|
+
return file;
|
|
83
|
+
}
|
|
84
|
+
const scale = Math.min(1, LONG_SIDE / long);
|
|
85
|
+
const canvas = document.createElement('canvas');
|
|
86
|
+
canvas.width = Math.round(bitmap.width * scale);
|
|
87
|
+
canvas.height = Math.round(bitmap.height * scale);
|
|
88
|
+
canvas.getContext('2d').drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
89
|
+
bitmap.close();
|
|
90
|
+
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg', 0.88));
|
|
91
|
+
if (!blob)
|
|
92
|
+
return file;
|
|
93
|
+
return new File([blob], file.name.replace(/\.[^.]+$/, '') + '.jpg', {
|
|
94
|
+
type: 'image/jpeg',
|
|
95
|
+
lastModified: file.lastModified
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return file;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=photo-prep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"photo-prep.js","sourceRoot":"","sources":["../../client/utils/photo-prep.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * A photo made ready in the browser before it goes up (ADR-0165 decision 3): when it was taken, read from the camera's\n * own record (EXIF), and the photo shrunk to 2560 px on its long side so a phone's original does not fill the storage.\n */\n\n/** The longest side a photo keeps. */\nexport const LONG_SIDE = 2560\n/* A photo already small enough goes up as it is - shrinking again would only lose quality. */\nconst SMALL_ENOUGH = 2.5 * 1024 * 1024\n\n/** \"YYYY:MM:DD HH:MM:SS\" as the camera wrote it, in the camera's local time. */\nexport function exifDateOf(text?: string): Date | undefined {\n const match = /^(\\d{4}):(\\d{2}):(\\d{2})[ T](\\d{2}):(\\d{2}):(\\d{2})/.exec(text ?? '')\n if (!match) return undefined\n const [, y, mo, d, h, mi, s] = match.map(Number)\n const date = new Date(y, mo - 1, d, h, mi, s)\n return isNaN(date.getTime()) || y < 1990 ? undefined : date\n}\n\n/** The date the camera recorded the photo, from a JPEG's EXIF - when it was taken (0x9003), else when it was saved (0x0132). */\nexport function exifTakenAt(buffer: ArrayBuffer): Date | undefined {\n try {\n const view = new DataView(buffer)\n if (view.byteLength < 4 || view.getUint16(0) !== 0xffd8) return undefined\n let offset = 2\n while (offset + 10 <= view.byteLength) {\n const marker = view.getUint16(offset)\n if ((marker & 0xff00) !== 0xff00) return undefined\n const size = view.getUint16(offset + 2)\n /* APP1 starting with \"Exif\\0\\0\" */\n if (marker === 0xffe1 && view.getUint32(offset + 4) === 0x45786966) return exifDateOf(tiffDate(view, offset + 10))\n offset += 2 + size\n }\n } catch {\n /* A broken or cut record says nothing. */\n }\n return undefined\n}\n\nfunction tiffDate(view: DataView, start: number): string | undefined {\n const little = view.getUint16(start) === 0x4949\n const u16 = (at: number) => view.getUint16(start + at, little)\n const u32 = (at: number) => view.getUint32(start + at, little)\n const entries = (ifd: number) => Array.from({ length: u16(ifd) }, (_, i) => ifd + 2 + i * 12)\n const ascii = (entry: number) => {\n const count = u32(entry + 4)\n const at = count > 4 ? u32(entry + 8) : entry + 8\n let text = ''\n for (let i = 0; i < count - 1; i++) text += String.fromCharCode(view.getUint8(start + at + i))\n return text\n }\n let saved: string | undefined\n let exif: number | undefined\n for (const entry of entries(u32(4))) {\n const tag = u16(entry)\n if (tag === 0x0132) saved = ascii(entry)\n if (tag === 0x8769) exif = u32(entry + 8)\n }\n if (exif) for (const entry of entries(exif)) if (u16(entry) === 0x9003) return ascii(entry)\n return saved\n}\n\n/** When the photo was taken - the camera's record, or nothing. */\nexport async function takenAtOf(file: File): Promise<Date | undefined> {\n if (!/jpe?g/i.test(file.type) && !/\\.jpe?g$/i.test(file.name)) return undefined\n return exifTakenAt(await file.slice(0, 256 * 1024).arrayBuffer())\n}\n\n/** The photo shrunk to `LONG_SIDE` on its long side as a JPEG - or the photo itself when it is small or cannot be drawn. */\nexport async function shrunk(file: File): Promise<File> {\n try {\n const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' } as any)\n const long = Math.max(bitmap.width, bitmap.height)\n if (long <= LONG_SIDE && file.size <= SMALL_ENOUGH) {\n bitmap.close()\n return file\n }\n const scale = Math.min(1, LONG_SIDE / long)\n const canvas = document.createElement('canvas')\n canvas.width = Math.round(bitmap.width * scale)\n canvas.height = Math.round(bitmap.height * scale)\n canvas.getContext('2d')!.drawImage(bitmap, 0, 0, canvas.width, canvas.height)\n bitmap.close()\n const blob = await new Promise<Blob | null>(resolve => canvas.toBlob(resolve, 'image/jpeg', 0.88))\n if (!blob) return file\n return new File([blob], file.name.replace(/\\.[^.]+$/, '') + '.jpg', {\n type: 'image/jpeg',\n lastModified: file.lastModified\n })\n } catch {\n return file\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerGalleryPhotos(): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerGalleryPhotos = registerGalleryPhotos;
|
|
4
|
+
/*
|
|
5
|
+
* Who may open a gallery photo by its path - whoever sees the post (ADR-0165 decision 7). attachment-base has already
|
|
6
|
+
* checked the file is of the request's domain.
|
|
7
|
+
*/
|
|
8
|
+
const attachment_base_1 = require("@things-factory/attachment-base");
|
|
9
|
+
const gallery_service_1 = require("./service/gallery/gallery-service");
|
|
10
|
+
function registerGalleryPhotos() {
|
|
11
|
+
(0, attachment_base_1.registerAttachmentReader)(gallery_service_1.GALLERY_REF_TYPE, async (attachment, context) => !!attachment.refBy && !!(await (0, gallery_service_1.visiblePost)(attachment.refBy, context)));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=attachment-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment-reader.js","sourceRoot":"","sources":["../server/attachment-reader.ts"],"names":[],"mappings":";;AAQA,sDAKC;AAbD;;;GAGG;AACH,qEAA0E;AAE1E,uEAAiF;AAEjF,SAAgB,qBAAqB;IACnC,IAAA,0CAAwB,EACtB,kCAAgB,EAChB,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,IAAA,6BAAW,EAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CACtG,CAAA;AACH,CAAC","sourcesContent":["/*\n * Who may open a gallery photo by its path - whoever sees the post (ADR-0165 decision 7). attachment-base has already\n * checked the file is of the request's domain.\n */\nimport { registerAttachmentReader } from '@things-factory/attachment-base'\n\nimport { GALLERY_REF_TYPE, visiblePost } from './service/gallery/gallery-service'\n\nexport function registerGalleryPhotos(): void {\n registerAttachmentReader(\n GALLERY_REF_TYPE,\n async (attachment, context) => !!attachment.refBy && !!(await visiblePost(attachment.refBy, context))\n )\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './service';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./service"), exports);
|
|
5
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
6
|
+
const attachment_reader_1 = require("./attachment-reader");
|
|
7
|
+
const role_tasks_1 = require("./role-tasks");
|
|
8
|
+
for (const task of role_tasks_1.ROLE_TASKS)
|
|
9
|
+
(0, auth_base_1.registerRoleTask)(task);
|
|
10
|
+
(0, attachment_reader_1.registerGalleryPhotos)();
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,oDAAyB;AAEzB,yDAA4D;AAE5D,2DAA2D;AAC3D,6CAAyC;AAEzC,KAAK,MAAM,IAAI,IAAI,uBAAU;IAAE,IAAA,4BAAgB,EAAC,IAAI,CAAC,CAAA;AACrD,IAAA,yCAAqB,GAAE,CAAA","sourcesContent":["export * from './service'\n\nimport { registerRoleTask } from '@things-factory/auth-base'\n\nimport { registerGalleryPhotos } from './attachment-reader'\nimport { ROLE_TASKS } from './role-tasks'\n\nfor (const task of ROLE_TASKS) registerRoleTask(task)\nregisterGalleryPhotos()\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* The work this module names, for the seats applications build (ADR-0139 decision 11).
|
|
4
|
+
*
|
|
5
|
+
* Looking, posting and liking are no task: everyone of the domain does them (ADR-0165 decision 2). Hiding a post,
|
|
6
|
+
* tidying the tags and making an event from the gallery are `gallery.moderation`.
|
|
7
|
+
*
|
|
8
|
+
* Data only - `server/index.ts` registers it - so an application's tests read it without starting the framework.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ROLE_TASKS = void 0;
|
|
12
|
+
exports.ROLE_TASKS = [
|
|
13
|
+
{ id: 'gallery.moderation', grants: [{ category: 'gallery', axes: ['mutation'] }] }
|
|
14
|
+
];
|
|
15
|
+
//# sourceMappingURL=role-tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-tasks.js","sourceRoot":"","sources":["../server/role-tasks.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAIU,QAAA,UAAU,GAAe;IACpC,EAAE,EAAE,EAAE,oBAAoB,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE;CACpF,CAAA","sourcesContent":["/*\n * The work this module names, for the seats applications build (ADR-0139 decision 11).\n *\n * Looking, posting and liking are no task: everyone of the domain does them (ADR-0165 decision 2). Hiding a post,\n * tidying the tags and making an event from the gallery are `gallery.moderation`.\n *\n * Data only - `server/index.ts` registers it - so an application's tests read it without starting the framework.\n */\n\nimport type { RoleTask } from '@things-factory/auth-base'\n\nexport const ROLE_TASKS: RoleTask[] = [\n { id: 'gallery.moderation', grants: [{ category: 'gallery', axes: ['mutation'] }] }\n]\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FileUpload } from 'graphql-upload/GraphQLUpload.js';
|
|
2
|
+
import { User } from '@things-factory/auth-base';
|
|
3
|
+
import { GalleryPost } from './gallery';
|
|
4
|
+
import { GalleryEventCard, GalleryEventChoice, GalleryEventFacts, GalleryEventInput, GalleryFilter, GalleryLikeState, GalleryPerson, GalleryPhoto, GalleryPostInput, GalleryPostList, GallerySummary, GalleryTagCount } from './gallery-types';
|
|
5
|
+
export declare class GalleryResolver {
|
|
6
|
+
galleryPosts(filter: GalleryFilter | undefined, skip: number | undefined, take: number | undefined, context: ResolverContext): Promise<GalleryPostList>;
|
|
7
|
+
galleryPost(id: string, context: ResolverContext): Promise<GalleryPost | undefined>;
|
|
8
|
+
galleryLikers(id: string, context: ResolverContext): Promise<GalleryPerson[]>;
|
|
9
|
+
galleryTags(text: string | undefined, context: ResolverContext): Promise<GalleryTagCount[]>;
|
|
10
|
+
galleryEvents(context: ResolverContext): Promise<GalleryEventCard[]>;
|
|
11
|
+
galleryEventChoices(takenAt: Date | undefined, context: ResolverContext): Promise<GalleryEventChoice[]>;
|
|
12
|
+
gallerySummary(context: ResolverContext): Promise<GallerySummary>;
|
|
13
|
+
createGalleryPost(input: GalleryPostInput, photos: FileUpload[], context: ResolverContext): Promise<GalleryPost>;
|
|
14
|
+
updateGalleryPost(id: string, input: GalleryPostInput, attach: FileUpload[] | undefined, remove: string[] | undefined, context: ResolverContext): Promise<GalleryPost>;
|
|
15
|
+
deleteGalleryPost(id: string, context: ResolverContext): Promise<boolean>;
|
|
16
|
+
likeGalleryPost(id: string, like: boolean, context: ResolverContext): Promise<GalleryLikeState>;
|
|
17
|
+
visitGallery(context: ResolverContext): Promise<boolean>;
|
|
18
|
+
hideGalleryPost(id: string, reason: string, context: ResolverContext): Promise<GalleryPost>;
|
|
19
|
+
unhideGalleryPost(id: string, context: ResolverContext): Promise<GalleryPost>;
|
|
20
|
+
renameGalleryTag(from: string, to: string, context: ResolverContext): Promise<number>;
|
|
21
|
+
createGalleryEvent(input: GalleryEventInput, context: ResolverContext): Promise<GalleryEventCard>;
|
|
22
|
+
photos(post: GalleryPost, context: ResolverContext): Promise<GalleryPhoto[]>;
|
|
23
|
+
creator(post: GalleryPost): Promise<User | undefined>;
|
|
24
|
+
event(post: GalleryPost): Promise<GalleryEventFacts | undefined>;
|
|
25
|
+
liked(post: GalleryPost, context: ResolverContext): Promise<boolean>;
|
|
26
|
+
editable(post: GalleryPost, context: ResolverContext): boolean;
|
|
27
|
+
moderatable(context: ResolverContext): Promise<boolean>;
|
|
28
|
+
hiddenReason(post: GalleryPost, context: ResolverContext): Promise<string | undefined>;
|
|
29
|
+
}
|