@fiduswriter/image-manager 0.1.4 → 0.1.6

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.
Files changed (65) hide show
  1. package/README.md +53 -3
  2. package/dist/copyright_dialog/index.d.ts +10 -13
  3. package/dist/copyright_dialog/index.d.ts.map +1 -1
  4. package/dist/copyright_dialog/index.js +32 -14
  5. package/dist/copyright_dialog/index.js.map +1 -1
  6. package/dist/copyright_dialog/templates.d.ts +16 -12
  7. package/dist/copyright_dialog/templates.d.ts.map +1 -1
  8. package/dist/copyright_dialog/templates.js.map +1 -1
  9. package/dist/database.d.ts +7 -6
  10. package/dist/database.d.ts.map +1 -1
  11. package/dist/database.js +17 -11
  12. package/dist/database.js.map +1 -1
  13. package/dist/edit_dialog/index.d.ts +22 -19
  14. package/dist/edit_dialog/index.d.ts.map +1 -1
  15. package/dist/edit_dialog/index.js +65 -33
  16. package/dist/edit_dialog/index.js.map +1 -1
  17. package/dist/edit_dialog/model.d.ts +2 -19
  18. package/dist/edit_dialog/model.d.ts.map +1 -1
  19. package/dist/edit_dialog/model.js +51 -31
  20. package/dist/edit_dialog/model.js.map +1 -1
  21. package/dist/edit_dialog/templates.d.ts +7 -4
  22. package/dist/edit_dialog/templates.d.ts.map +1 -1
  23. package/dist/edit_dialog/templates.js.map +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/overview/categories.d.ts +7 -5
  27. package/dist/overview/categories.d.ts.map +1 -1
  28. package/dist/overview/categories.js +18 -13
  29. package/dist/overview/categories.js.map +1 -1
  30. package/dist/overview/index.d.ts +31 -27
  31. package/dist/overview/index.d.ts.map +1 -1
  32. package/dist/overview/index.js +64 -33
  33. package/dist/overview/index.js.map +1 -1
  34. package/dist/overview/menu.d.ts +4 -47
  35. package/dist/overview/menu.d.ts.map +1 -1
  36. package/dist/overview/menu.js +13 -8
  37. package/dist/overview/menu.js.map +1 -1
  38. package/dist/overview/templates.d.ts +7 -3
  39. package/dist/overview/templates.d.ts.map +1 -1
  40. package/dist/overview/templates.js.map +1 -1
  41. package/dist/selection_dialog/index.d.ts +17 -15
  42. package/dist/selection_dialog/index.d.ts.map +1 -1
  43. package/dist/selection_dialog/index.js +18 -8
  44. package/dist/selection_dialog/index.js.map +1 -1
  45. package/dist/types.d.ts +118 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +2 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +94 -78
  50. package/src/copyright_dialog/{index.js → index.ts} +70 -38
  51. package/src/copyright_dialog/{templates.js → templates.ts} +19 -3
  52. package/src/database.ts +83 -0
  53. package/src/edit_dialog/{index.js → index.ts} +110 -45
  54. package/src/edit_dialog/{model.js → model.ts} +95 -46
  55. package/src/edit_dialog/{templates.js → templates.ts} +13 -2
  56. package/src/global.d.ts +37 -0
  57. package/src/overview/{categories.js → categories.ts} +34 -21
  58. package/src/overview/{index.js → index.ts} +157 -75
  59. package/src/overview/menu.ts +85 -0
  60. package/src/overview/{templates.js → templates.ts} +16 -2
  61. package/src/selection_dialog/{index.js → index.ts} +85 -49
  62. package/src/types.ts +133 -0
  63. package/src/database.js +0 -57
  64. package/src/overview/menu.js +0 -75
  65. /package/src/{index.js → index.ts} +0 -0
@@ -0,0 +1,118 @@
1
+ import type { DataTable } from "simple-datatables";
2
+ import type { ImageDB } from "./database.js";
3
+ /** A single license under which an image is available. */
4
+ export interface License {
5
+ url: string;
6
+ title: string;
7
+ start?: string | false;
8
+ }
9
+ /** Copyright and licensing information for an image. */
10
+ export interface Copyright {
11
+ holder?: string | false;
12
+ year?: number | false;
13
+ freeToRead: boolean;
14
+ licenses: License[];
15
+ }
16
+ /** A category used to group images. */
17
+ export interface ImageCategory {
18
+ id: number;
19
+ category_title: string;
20
+ }
21
+ /** A single image as stored in the client-side image database. */
22
+ export interface Image {
23
+ id: number;
24
+ title: string;
25
+ file_type: string;
26
+ image: string;
27
+ thumbnail?: string;
28
+ width: number;
29
+ height: number;
30
+ added: number;
31
+ cats: number[];
32
+ copyright: Copyright;
33
+ [key: string]: unknown;
34
+ }
35
+ /** End-to-end encryption context passed to dialogs that handle images. */
36
+ export interface E2EEContext {
37
+ encrypted: boolean;
38
+ key: CryptoKey;
39
+ }
40
+ /** Subset of the main Fidus Writer app object used by image-manager code. */
41
+ export interface ImageManagerApp {
42
+ imageDB: ImageDB;
43
+ isOffline: () => boolean;
44
+ settings: {
45
+ APPS: string[];
46
+ MEDIA_MAX_SIZE?: number;
47
+ [key: string]: unknown;
48
+ };
49
+ name: string;
50
+ [key: string]: unknown;
51
+ }
52
+ /** Page object passed to image dialogs. */
53
+ export interface ImageManagerPage {
54
+ app: ImageManagerApp;
55
+ e2ee?: E2EEContext;
56
+ menu?: unknown;
57
+ [key: string]: unknown;
58
+ }
59
+ /** Plugin instance created by the overview's plugin activation. */
60
+ export interface ImageOverviewPlugin {
61
+ init?: () => Promise<unknown> | unknown;
62
+ [key: string]: unknown;
63
+ }
64
+ /** Constructor for an image-overview plugin. */
65
+ export interface ImageOverviewPluginConstructor {
66
+ new (overview: unknown): ImageOverviewPlugin;
67
+ }
68
+ /** Row displayed in the image overview data table. */
69
+ export type ImageTableRow = [
70
+ id: number,
71
+ selected: boolean,
72
+ file: string,
73
+ size: string,
74
+ added: string,
75
+ actions: string
76
+ ];
77
+ /** An image selection item coming from either the document or user DB. */
78
+ export interface ImageSelectionItem {
79
+ image: Image;
80
+ db: "document" | "user";
81
+ }
82
+ /** Response body from `POST /api/usermedia/images/`. */
83
+ export interface ImagesResponse {
84
+ imageCategories: ImageCategory[];
85
+ images: Image[];
86
+ }
87
+ /** Response body from `POST /api/usermedia/save/`. */
88
+ export interface SaveImageResponse {
89
+ errormsg: Record<string, string> & {
90
+ error?: string;
91
+ };
92
+ values: Image;
93
+ }
94
+ /** Request body for `POST /api/usermedia/save/`. */
95
+ export interface SaveImageRequest {
96
+ id?: number;
97
+ title: string;
98
+ copyright: Copyright | string;
99
+ cats: (string | number)[];
100
+ image?: File | Blob;
101
+ original_file_type?: string;
102
+ [key: string]: unknown;
103
+ }
104
+ /** Request body for `POST /api/usermedia/save_category/`. */
105
+ export interface SaveCategoriesRequest {
106
+ ids: (string | number)[];
107
+ titles: string[];
108
+ }
109
+ /** Response body from `POST /api/usermedia/save_category/`. */
110
+ export interface SaveCategoriesResponse {
111
+ entries: ImageCategory[];
112
+ }
113
+ /** Request body for `POST /api/usermedia/delete/`. */
114
+ export interface DeleteImagesRequest {
115
+ ids: (string | number)[];
116
+ }
117
+ export type { DataTable };
118
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,85 +1,101 @@
1
1
  {
2
- "name": "@fiduswriter/image-manager",
3
- "version": "0.1.4",
4
- "description": "Fidus Writer image/media manager",
5
- "repository": {
6
- "type": "git",
7
- "url": "ssh://git@codeberg.org/fiduswriter/fiduswriter-image-manager-js.git"
2
+ "name": "@fiduswriter/image-manager",
3
+ "version": "0.1.6",
4
+ "description": "Fidus Writer image/media manager",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "ssh://git@codeberg.org/fiduswriter/fiduswriter-image-manager-js.git"
8
+ },
9
+ "license": "AGPL-3.0",
10
+ "author": "Johannes Wilm",
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
8
18
  },
9
- "license": "AGPL-3.0",
10
- "author": "Johannes Wilm",
11
- "type": "module",
12
- "main": "./dist/index.js",
13
- "types": "./dist/index.d.ts",
14
- "exports": {
15
- ".": {
16
- "types": "./dist/index.d.ts",
17
- "default": "./dist/index.js"
18
- },
19
- "./*": {
20
- "types": "./dist/*.d.ts",
21
- "default": "./dist/*.js"
22
- },
23
- "./copyright_dialog": {
24
- "types": "./dist/copyright_dialog/index.d.ts",
25
- "default": "./dist/copyright_dialog/index.js"
26
- },
27
- "./copyright_dialog/*": {
28
- "types": "./dist/copyright_dialog/*.d.ts",
29
- "default": "./dist/copyright_dialog/*.js"
30
- },
31
- "./edit_dialog": {
32
- "types": "./dist/edit_dialog/index.d.ts",
33
- "default": "./dist/edit_dialog/index.js"
34
- },
35
- "./edit_dialog/*": {
36
- "types": "./dist/edit_dialog/*.d.ts",
37
- "default": "./dist/edit_dialog/*.js"
38
- },
39
- "./overview": {
40
- "types": "./dist/overview/index.d.ts",
41
- "default": "./dist/overview/index.js"
42
- },
43
- "./overview/*": {
44
- "types": "./dist/overview/*.d.ts",
45
- "default": "./dist/overview/*.js"
46
- },
47
- "./selection_dialog": {
48
- "types": "./dist/selection_dialog/index.d.ts",
49
- "default": "./dist/selection_dialog/index.js"
50
- },
51
- "./selection_dialog/*": {
52
- "types": "./dist/selection_dialog/*.d.ts",
53
- "default": "./dist/selection_dialog/*.js"
54
- }
19
+ "./*": {
20
+ "types": "./dist/*.d.ts",
21
+ "default": "./dist/*.js"
55
22
  },
56
- "files": [
57
- "dist/",
58
- "src/",
59
- "README.md",
60
- "LICENSE"
61
- ],
62
- "scripts": {
63
- "build": "tsc",
64
- "prepare": "npm run build",
65
- "prepublishOnly": "npm run build",
66
- "typecheck": "tsc --noEmit",
67
- "lint": "eslint src",
68
- "lint:fix": "eslint src --fix",
69
- "format": "prettier --write .",
70
- "format:check": "prettier --check ."
23
+ "./copyright_dialog": {
24
+ "types": "./dist/copyright_dialog/index.d.ts",
25
+ "default": "./dist/copyright_dialog/index.js"
26
+ },
27
+ "./copyright_dialog/*": {
28
+ "types": "./dist/copyright_dialog/*.d.ts",
29
+ "default": "./dist/copyright_dialog/*.js"
30
+ },
31
+ "./edit_dialog": {
32
+ "types": "./dist/edit_dialog/index.d.ts",
33
+ "default": "./dist/edit_dialog/index.js"
34
+ },
35
+ "./edit_dialog/*": {
36
+ "types": "./dist/edit_dialog/*.d.ts",
37
+ "default": "./dist/edit_dialog/*.js"
71
38
  },
72
- "dependencies": {
73
- "@fiduswriter/common": "^0.1.4",
74
- "cropperjs": "^1.6.2",
75
- "fwtoolkit": "^0.1.9"
39
+ "./overview": {
40
+ "types": "./dist/overview/index.d.ts",
41
+ "default": "./dist/overview/index.js"
76
42
  },
77
- "devDependencies": {
78
- "@eslint/js": "^10.0.1",
79
- "eslint": "^10.6.0",
80
- "globals": "^15.15.0",
81
- "prettier": "^3.5.3",
82
- "typescript": "^6.0.3",
83
- "typescript-eslint": "^8.62.0"
43
+ "./overview/*": {
44
+ "types": "./dist/overview/*.d.ts",
45
+ "default": "./dist/overview/*.js"
46
+ },
47
+ "./selection_dialog": {
48
+ "types": "./dist/selection_dialog/index.d.ts",
49
+ "default": "./dist/selection_dialog/index.js"
50
+ },
51
+ "./selection_dialog/*": {
52
+ "types": "./dist/selection_dialog/*.d.ts",
53
+ "default": "./dist/selection_dialog/*.js"
84
54
  }
55
+ },
56
+ "files": [
57
+ "dist/",
58
+ "src/",
59
+ "README.md",
60
+ "LICENSE"
61
+ ],
62
+ "scripts": {
63
+ "build": "tsc",
64
+ "prepare": "npm run build",
65
+ "prepublishOnly": "npm run build",
66
+ "typecheck": "tsc --noEmit",
67
+ "lint": "eslint src",
68
+ "lint:fix": "eslint src --fix",
69
+ "format": "prettier --write .",
70
+ "format:check": "prettier --check .",
71
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.config.js"
72
+ },
73
+ "dependencies": {
74
+ "@fiduswriter/common": "^0.1.4",
75
+ "bibliojson": "^4.0.3",
76
+ "cropperjs": "^1.6.2",
77
+ "fast-deep-equal": "^3.1.3",
78
+ "fwtoolkit": "^0.1.9"
79
+ },
80
+ "devDependencies": {
81
+ "@eslint/js": "^10.0.1",
82
+ "@types/jest": "^30.0.0",
83
+ "eslint": "^10.6.0",
84
+ "globals": "^15.15.0",
85
+ "happy-dom": "^20.10.6",
86
+ "jest": "^30.4.2",
87
+ "prettier": "^3.5.3",
88
+ "ts-jest": "^29.4.11",
89
+ "ts-jest-resolver": "^2.0.1",
90
+ "typescript": "^6.0.3",
91
+ "typescript-eslint": "^8.62.0"
92
+ },
93
+ "lint-staged": {
94
+ "*.{ts,js,mjs,css,md,json,html}": [
95
+ "prettier --write"
96
+ ],
97
+ "*.{ts,js,mjs}": [
98
+ "eslint --fix"
99
+ ]
100
+ }
85
101
  }
@@ -1,11 +1,14 @@
1
1
  import {edtfParse} from "bibliojson"
2
2
  import deepEqual from "fast-deep-equal"
3
3
  import {Dialog, InputList, TypeSwitch, escapeText} from "fwtoolkit"
4
+ import type {InputListItemRenderResult} from "fwtoolkit"
5
+
4
6
  import {
5
7
  copyrightTemplate,
6
8
  licenseInputTemplate,
7
9
  licenseSelectTemplate
8
10
  } from "./templates.js"
11
+ import type {Copyright, License} from "../types.js"
9
12
 
10
13
  export const LICENSE_URLS = [
11
14
  ["CC BY 4.0", "https://creativecommons.org/licenses/by/4.0/"],
@@ -15,42 +18,58 @@ export const LICENSE_URLS = [
15
18
  ["CC BY-NC-SA 4.0", "https://creativecommons.org/licenses/by-nc-sa/4.0/"],
16
19
  ["CC BY-NC-ND 4.0", "https://creativecommons.org/licenses/by-nc-nd/4.0/"],
17
20
  ["CC0", "https://creativecommons.org/publicdomain/zero/1.0/"]
18
- ]
21
+ ] as [string, string][]
19
22
 
20
- function getLicenseTitle(url) {
23
+ function getLicenseTitle(url: string): string {
21
24
  const license = LICENSE_URLS.find(license => license[1] === url)
22
25
  return license ? license[0] : ""
23
26
  }
24
27
 
25
28
  export class CopyrightDialog {
26
- constructor(copyright) {
29
+ copyright: Copyright
30
+
31
+ origCopyright: Copyright
32
+
33
+ dialog: Dialog | false = false
34
+
35
+ licensesList!: InputList<License>
36
+
37
+ constructor(copyright: Copyright) {
27
38
  this.copyright = copyright
28
39
  this.origCopyright = copyright
29
40
  this.dialog = false
30
41
  }
31
42
 
32
- getCurrentValue() {
33
- this.copyright = {}
34
- const holder = this.dialog.dialogEl.querySelector(".holder").value
43
+ getCurrentValue(): void {
44
+ if (!this.dialog) {
45
+ return
46
+ }
47
+ const dialogEl = this.dialog.dialogEl
48
+ this.copyright = {} as Copyright
49
+ const holder = (
50
+ dialogEl.querySelector(".holder") as HTMLInputElement
51
+ ).value
35
52
  this.copyright.holder = holder.length ? holder : false
36
- const year = this.dialog.dialogEl.querySelector(".year").value
53
+ const year = (
54
+ dialogEl.querySelector(".year") as HTMLInputElement
55
+ ).value
37
56
  this.copyright.year = year.length
38
57
  ? Math.max(0, Math.min(Number.parseInt(year) || 0, 2100))
39
58
  : false
40
- this.copyright.freeToRead = this.dialog.dialogEl.querySelector(
59
+ this.copyright.freeToRead = dialogEl.querySelector(
41
60
  ".free-to-read:checked"
42
61
  )
43
62
  ? true
44
63
  : false
45
64
  const licenseStartDates = Array.from(
46
- this.dialog.dialogEl.querySelectorAll(".license-start")
47
- ).map(el => el.value)
65
+ dialogEl.querySelectorAll(".license-start")
66
+ ).map(el => (el as HTMLInputElement).value)
48
67
  this.copyright.licenses = this.licensesList.values
49
68
  .map((license, index) => {
50
69
  if (!license.url.length) {
51
70
  return false
52
71
  }
53
- const returnValue = {
72
+ const returnValue: License = {
54
73
  url: license.url,
55
74
  title: license.title
56
75
  }
@@ -67,17 +86,17 @@ export class CopyrightDialog {
67
86
  }
68
87
  return returnValue
69
88
  })
70
- .filter(license => license)
89
+ .filter((license): license is License => license !== false)
71
90
  }
72
91
 
73
- init() {
92
+ init(): Promise<Copyright | false> {
74
93
  return new Promise(resolve => {
75
94
  const buttons = []
76
95
  buttons.push({
77
96
  text: gettext("Change"),
78
97
  classes: "fw-dark",
79
98
  click: () => {
80
- this.dialog.close()
99
+ dialog.close()
81
100
  this.getCurrentValue()
82
101
  if (deepEqual(this.copyright, this.origCopyright)) {
83
102
  // No change.
@@ -88,10 +107,10 @@ export class CopyrightDialog {
88
107
  })
89
108
 
90
109
  buttons.push({
91
- type: "cancel"
110
+ type: "cancel" as const
92
111
  })
93
112
 
94
- this.dialog = new Dialog({
113
+ const dialog = new Dialog({
95
114
  width: 940,
96
115
  height: 300,
97
116
  id: "configure-copyright",
@@ -99,28 +118,35 @@ export class CopyrightDialog {
99
118
  body: copyrightTemplate(this.copyright),
100
119
  buttons
101
120
  })
121
+ this.dialog = dialog
102
122
 
103
- this.dialog.open()
123
+ dialog.open()
104
124
  this.bind()
105
125
  })
106
126
  }
107
127
 
108
- bind() {
109
- this.licensesList = new InputList({
110
- dom: this.dialog.dialogEl.querySelector(".copyright-licenses-list"),
128
+ bind(): void {
129
+ if (!this.dialog) {
130
+ return
131
+ }
132
+ const dialogEl = this.dialog.dialogEl
133
+ this.licensesList = new InputList<License>({
134
+ dom: dialogEl.querySelector(
135
+ ".copyright-licenses-list"
136
+ ) as HTMLElement,
111
137
  initialValues: this.copyright.licenses || [],
112
138
  emptyValue: {url: "", title: "", start: false},
113
- renderItem: license => ({
139
+ renderItem: (license): InputListItemRenderResult<License> => ({
114
140
  html: `<div class="copyright-license-switch"></div>
115
141
  <div class="field-part field-part-small">
116
142
  <input type="text" class="license-start" value="${license.start ? escapeText(license.start) : ""}" placeholder="${gettext("Start date")}">
117
143
  </div>`,
118
144
  bind: el => {
119
- const licenseContainer = el.closest("tr")
145
+ const licenseContainer = el.closest("tr") as HTMLElement
120
146
  const startInput =
121
147
  licenseContainer.querySelector(".license-start")
122
- if (license.start) {
123
- startInput.value = license.start
148
+ if (license.start && startInput) {
149
+ ;(startInput as HTMLInputElement).value = license.start
124
150
  }
125
151
 
126
152
  const mode =
@@ -131,12 +157,13 @@ export class CopyrightDialog {
131
157
  ? 1
132
158
  : 2
133
159
  new TypeSwitch({
134
- dom: el.querySelector(".copyright-license-switch"),
160
+ dom: el.querySelector(
161
+ ".copyright-license-switch"
162
+ ) as HTMLElement,
135
163
  label1: gettext("From list"),
136
164
  label2: gettext("Custom"),
137
- initialMode: mode,
138
- render1: () =>
139
- licenseSelectTemplate({url: license.url}),
165
+ initialMode: mode as 1 | 2,
166
+ render1: () => licenseSelectTemplate({url: license.url}),
140
167
  render2: () =>
141
168
  licenseInputTemplate({
142
169
  url: license.url,
@@ -148,7 +175,7 @@ export class CopyrightDialog {
148
175
  ".fw-type-switch-input-inner input, .fw-type-switch-input-inner select"
149
176
  )
150
177
  if (focusable) {
151
- focusable.focus()
178
+ ;(focusable as HTMLElement).focus()
152
179
  }
153
180
  }
154
181
  })
@@ -157,22 +184,27 @@ export class CopyrightDialog {
157
184
  getValue: el => {
158
185
  const licenseInput = el.querySelector(
159
186
  ".fw-type-switch-input-inner"
160
- )
187
+ ) as HTMLElement
161
188
  const selectEl = licenseInput.querySelector("select.license")
162
- let url, title
189
+ let url: string, title: string
163
190
  if (selectEl) {
164
- url = selectEl.value
191
+ url = (selectEl as HTMLSelectElement).value
165
192
  title = getLicenseTitle(url)
166
193
  } else {
167
- url = licenseInput.querySelector("input.license").value
168
- title = licenseInput.querySelector(
194
+ url = (licenseInput.querySelector("input.license") as HTMLInputElement).value
195
+ title = (licenseInput.querySelector(
169
196
  "input.license-title"
170
- ).value
197
+ ) as HTMLInputElement).value
171
198
  }
172
199
  const start =
173
- el.closest("tr").querySelector(".license-start").value ||
174
- false
175
- return {url, title, start}
200
+ (el.closest("tr") as HTMLElement).querySelector(
201
+ ".license-start"
202
+ )
203
+ return {
204
+ url,
205
+ title,
206
+ start: start ? (start as HTMLInputElement).value || false : false
207
+ }
176
208
  }
177
209
  })
178
210
  }
@@ -1,7 +1,23 @@
1
1
  import {escapeText} from "fwtoolkit"
2
+
2
3
  import {LICENSE_URLS} from "./index.js"
3
4
 
4
- export const licenseSelectTemplate = ({url}) =>
5
+ interface LicenseSelectTemplateData {
6
+ url: string
7
+ }
8
+
9
+ interface LicenseInputTemplateData {
10
+ url: string
11
+ title: string
12
+ }
13
+
14
+ interface CopyrightTemplateData {
15
+ holder?: string | false
16
+ year?: number | false
17
+ freeToRead: boolean
18
+ }
19
+
20
+ export const licenseSelectTemplate = ({url}: LicenseSelectTemplateData) =>
5
21
  `<select class="license">
6
22
  <option value=""></option>
7
23
  ${LICENSE_URLS.map(
@@ -11,7 +27,7 @@ export const licenseSelectTemplate = ({url}) =>
11
27
  </select>
12
28
  <div class="fw-select-arrow fa fa-caret-down"></div>`
13
29
 
14
- export const licenseInputTemplate = ({url, title}) =>
30
+ export const licenseInputTemplate = ({url, title}: LicenseInputTemplateData) =>
15
31
  `<div class="field-part field-part-huge">
16
32
  <input type='text' class='license' value="${escapeText(url)}" placeholder="${gettext("License URL")}">
17
33
  </div>
@@ -19,7 +35,7 @@ export const licenseInputTemplate = ({url, title}) =>
19
35
  <input type='text' class='license-title' value="${escapeText(title)}" placeholder="${gettext("License Title")}">
20
36
  </div>`
21
37
 
22
- export const copyrightTemplate = ({holder, year, freeToRead}) =>
38
+ export const copyrightTemplate = ({holder, year, freeToRead}: CopyrightTemplateData) =>
23
39
  `<table class="fw-dialog-table">
24
40
  <tbody>
25
41
  <tr>
@@ -0,0 +1,83 @@
1
+ import {activateWait, addAlert, deactivateWait, gettext, postJson} from "fwtoolkit"
2
+
3
+ import type {
4
+ Image,
5
+ ImageCategory,
6
+ ImageManagerApp,
7
+ ImagesResponse,
8
+ SaveImageRequest,
9
+ SaveImageResponse
10
+ } from "./types.js"
11
+
12
+ /* A class that holds information about images uploaded by the user. */
13
+
14
+ export class ImageDB {
15
+ app: ImageManagerApp
16
+
17
+ db: Record<number, Image>
18
+
19
+ cats: ImageCategory[]
20
+
21
+ constructor(app: ImageManagerApp) {
22
+ this.app = app
23
+ this.db = {}
24
+ this.cats = []
25
+ }
26
+
27
+ getDB(): Promise<void> {
28
+ this.db = {}
29
+ this.cats = []
30
+
31
+ activateWait()
32
+
33
+ return postJson("/api/usermedia/images/").then(({json}) => {
34
+ const response = json as ImagesResponse
35
+ this.cats = response.imageCategories
36
+ response.images.forEach(image => {
37
+ this.db[image.id] = image
38
+ })
39
+ deactivateWait()
40
+ return
41
+ })
42
+ }
43
+
44
+ saveImage(imageData: SaveImageRequest): Promise<number> {
45
+ activateWait()
46
+ const {image, ...jsonData} = imageData
47
+
48
+ return postJson(
49
+ "/api/usermedia/save/",
50
+ jsonData,
51
+ image ? {image} : {}
52
+ )
53
+ .then(({json}) => {
54
+ const response = json as SaveImageResponse
55
+ deactivateWait()
56
+ if (Object.keys(response.errormsg).length) {
57
+ return Promise.reject(new Error(response.errormsg.error))
58
+ } else {
59
+ this.db[response.values.id] = response.values
60
+ return response.values.id
61
+ }
62
+ })
63
+ .catch(error => {
64
+ const networkError = error as {
65
+ status?: number
66
+ message?: string
67
+ statusText?: string
68
+ }
69
+ if (networkError.status === 413) {
70
+ addAlert(
71
+ "error",
72
+ `${gettext("Image is larger than the maximum permitted size")}${this.app.settings?.MEDIA_MAX_SIZE ? `: ${Number.parseInt(String(this.app.settings.MEDIA_MAX_SIZE / 1000000))}MB` : "."}`
73
+ )
74
+ } else if (networkError.message) {
75
+ addAlert("error", gettext(networkError.message))
76
+ } else {
77
+ addAlert("error", gettext(networkError.statusText || ""))
78
+ }
79
+ deactivateWait()
80
+ throw error
81
+ })
82
+ }
83
+ }