underpost 3.2.4 → 3.2.8
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/.github/workflows/release.cd.yml +1 -2
- package/CHANGELOG.md +268 -1
- package/CLI-HELP.md +26 -13
- package/Dockerfile +0 -4
- package/README.md +3 -3
- package/bin/build.js +13 -3
- package/bin/deploy.js +570 -1
- package/bin/file.js +5 -0
- package/conf.js +11 -2
- package/jsconfig.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
- package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
- package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
- package/package.json +20 -11
- package/src/api/core/core.controller.js +10 -10
- package/src/api/core/core.service.js +10 -10
- package/src/api/default/default.controller.js +10 -10
- package/src/api/default/default.service.js +10 -10
- package/src/api/document/document.controller.js +12 -12
- package/src/api/document/document.model.js +10 -16
- package/src/api/file/file.controller.js +8 -8
- package/src/api/file/file.model.js +10 -10
- package/src/api/file/file.service.js +36 -36
- package/src/api/test/test.controller.js +8 -8
- package/src/api/test/test.service.js +8 -8
- package/src/api/user/guest.service.js +99 -0
- package/src/api/user/user.controller.js +6 -6
- package/src/api/user/user.model.js +8 -13
- package/src/api/user/user.service.js +3 -20
- package/src/cli/deploy.js +33 -30
- package/src/cli/fs.js +62 -5
- package/src/cli/image.js +43 -1
- package/src/cli/index.js +5 -1
- package/src/cli/release.js +58 -2
- package/src/cli/repository.js +35 -3
- package/src/cli/run.js +304 -38
- package/src/cli/ssh.js +1 -1
- package/src/cli/static.js +43 -115
- package/src/client/Default.index.js +21 -33
- package/src/client/components/core/404.js +4 -4
- package/src/client/components/core/500.js +4 -4
- package/src/client/components/core/Account.js +73 -60
- package/src/client/components/core/AgGrid.js +23 -33
- package/src/client/components/core/Alert.js +12 -13
- package/src/client/components/core/AppStore.js +1 -1
- package/src/client/components/core/Auth.js +20 -32
- package/src/client/components/core/Badge.js +7 -13
- package/src/client/components/core/BtnIcon.js +15 -17
- package/src/client/components/core/CalendarCore.js +42 -63
- package/src/client/components/core/Chat.js +13 -15
- package/src/client/components/core/ClientEvents.js +87 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/Content.js +17 -14
- package/src/client/components/core/Css.js +15 -71
- package/src/client/components/core/CssCore.js +12 -16
- package/src/client/components/core/D3Chart.js +4 -4
- package/src/client/components/core/Docs.js +60 -59
- package/src/client/components/core/DropDown.js +69 -91
- package/src/client/components/core/EventBus.js +92 -0
- package/src/client/components/core/EventsUI.js +14 -17
- package/src/client/components/core/FileExplorer.js +102 -234
- package/src/client/components/core/FullScreen.js +47 -75
- package/src/client/components/core/Input.js +24 -69
- package/src/client/components/core/Keyboard.js +25 -18
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +25 -31
- package/src/client/components/core/LogIn.js +41 -41
- package/src/client/components/core/LogOut.js +23 -14
- package/src/client/components/core/Modal.js +397 -176
- package/src/client/components/core/NotificationManager.js +14 -18
- package/src/client/components/core/Panel.js +54 -50
- package/src/client/components/core/PanelForm.js +25 -125
- package/src/client/components/core/Polyhedron.js +110 -214
- package/src/client/components/core/PublicProfile.js +39 -32
- package/src/client/components/core/Recover.js +52 -48
- package/src/client/components/core/Responsive.js +88 -32
- package/src/client/components/core/RichText.js +9 -18
- package/src/client/components/core/Router.js +24 -3
- package/src/client/components/core/SearchBox.js +37 -37
- package/src/client/components/core/SignUp.js +39 -30
- package/src/client/components/core/SocketIo.js +31 -2
- package/src/client/components/core/SocketIoHandler.js +6 -6
- package/src/client/components/core/ToggleSwitch.js +8 -20
- package/src/client/components/core/ToolTip.js +5 -17
- package/src/client/components/core/Translate.js +56 -59
- package/src/client/components/core/Validator.js +26 -16
- package/src/client/components/core/Wallet.js +15 -26
- package/src/client/components/core/Worker.js +140 -25
- package/src/client/components/core/windowGetDimensions.js +7 -7
- package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
- package/src/client/components/default/CssDefault.js +12 -12
- package/src/client/components/default/LogInDefault.js +6 -4
- package/src/client/components/default/LogOutDefault.js +6 -4
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +4 -4
- package/src/client/components/default/SignUpDefault.js +6 -4
- package/src/client/components/default/TranslateDefault.js +3 -3
- package/src/client/services/core/core.service.js +17 -49
- package/src/client/services/default/default.management.js +139 -242
- package/src/client/services/default/default.service.js +10 -16
- package/src/client/services/document/document.service.js +14 -19
- package/src/client/services/file/file.service.js +8 -13
- package/src/client/services/test/test.service.js +8 -13
- package/src/client/services/user/guest.service.js +79 -0
- package/src/client/services/user/user.management.js +5 -5
- package/src/client/services/user/user.service.js +14 -20
- package/src/client/ssr/body/404.js +3 -3
- package/src/client/ssr/body/500.js +3 -3
- package/src/client/ssr/body/CacheControl.js +5 -2
- package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
- package/src/client/ssr/offline/Maintenance.js +12 -11
- package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
- package/src/client/ssr/pages/Test.js +2 -2
- package/src/client/sw/core.sw.js +212 -0
- package/src/index.js +1 -1
- package/src/runtime/express/Dockerfile +4 -4
- package/src/runtime/lampp/Dockerfile +8 -7
- package/src/runtime/wp/Dockerfile +11 -17
- package/src/server/backup.js +1 -2
- package/src/server/client-build-docs.js +45 -46
- package/src/server/client-build.js +334 -60
- package/src/server/client-formatted.js +47 -16
- package/src/server/conf.js +29 -13
- package/src/server/cron.js +6 -8
- package/src/server/dns.js +2 -1
- package/src/server/ipfs-client.js +232 -91
- package/src/server/process.js +13 -27
- package/src/server/start.js +6 -3
- package/src/server/valkey.js +134 -235
- package/tsconfig.docs.json +15 -0
- package/typedoc.json +20 -0
- package/jsdoc.json +0 -52
- package/src/client/components/core/ColorPalette.js +0 -5267
- package/src/client/components/core/JoyStick.js +0 -80
- package/src/client/components/default/RoutesDefault.js +0 -49
- package/src/client/sw/default.sw.js +0 -127
- package/src/client/sw/template.sw.js +0 -84
|
@@ -14,16 +14,12 @@ import { Scroll } from './Scroll.js';
|
|
|
14
14
|
import { LoadingAnimation } from './LoadingAnimation.js';
|
|
15
15
|
import { loggerFactory } from './Logger.js';
|
|
16
16
|
import { getApiBaseUrl } from '../../services/core/core.service.js';
|
|
17
|
-
|
|
18
17
|
const logger = loggerFactory(import.meta, { trace: true });
|
|
19
|
-
|
|
20
18
|
function sanitizeFilename(title, options = {}) {
|
|
21
19
|
const { replacement = '-', maxLength = 255, preserveExtension = true } = options;
|
|
22
|
-
|
|
23
20
|
if (typeof title !== 'string' || title.trim() === '') {
|
|
24
21
|
return 'untitled';
|
|
25
22
|
}
|
|
26
|
-
|
|
27
23
|
// 1) Extract extension (optional)
|
|
28
24
|
let name = title;
|
|
29
25
|
let ext = '';
|
|
@@ -34,43 +30,32 @@ function sanitizeFilename(title, options = {}) {
|
|
|
34
30
|
name = title.slice(0, -ext.length);
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
|
-
|
|
38
33
|
// 2) Normalize Unicode and remove diacritics
|
|
39
34
|
name = name.normalize('NFKD').replace(/[\u0300-\u036f]/g, '');
|
|
40
|
-
|
|
41
35
|
// 3) Remove control characters and null bytes
|
|
42
36
|
name = name.replace(/[\x00-\x1f\x7f]/g, '');
|
|
43
|
-
|
|
44
37
|
// 4) Remove forbidden filename characters (Windows / POSIX)
|
|
45
38
|
name = name.replace(/[<>:"/\\|?*\u0000]/g, '');
|
|
46
|
-
|
|
47
39
|
// 5) Collapse whitespace and replace with separator
|
|
48
40
|
name = name.replace(/\s+/g, replacement);
|
|
49
|
-
|
|
50
41
|
// 6) Collapse multiple separators
|
|
51
42
|
const escaped = replacement.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
52
43
|
name = name.replace(new RegExp(`${escaped}{2,}`, 'g'), replacement);
|
|
53
|
-
|
|
54
44
|
// 7) Trim dots and separators from edges
|
|
55
45
|
name = name.replace(new RegExp(`^[\\.${escaped}]+|[\\.${escaped}]+$`, 'g'), '');
|
|
56
|
-
|
|
57
46
|
// 8) Protect against Windows reserved names
|
|
58
47
|
if (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(name)) {
|
|
59
48
|
name = '_' + name;
|
|
60
49
|
}
|
|
61
|
-
|
|
62
50
|
// 9) Enforce max length
|
|
63
51
|
const maxNameLength = Math.max(1, maxLength - ext.length);
|
|
64
52
|
if (name.length > maxNameLength) {
|
|
65
53
|
name = name.slice(0, maxNameLength);
|
|
66
54
|
}
|
|
67
|
-
|
|
68
55
|
// 10) Fallback
|
|
69
56
|
if (!name) name = 'untitled';
|
|
70
|
-
|
|
71
57
|
return name + ext;
|
|
72
58
|
}
|
|
73
|
-
|
|
74
59
|
const userInfoFactory = (userDoc) => ({
|
|
75
60
|
username: userDoc.userId.username,
|
|
76
61
|
email: userDoc.userId.email,
|
|
@@ -78,10 +63,9 @@ const userInfoFactory = (userDoc) => ({
|
|
|
78
63
|
profileImageId: userDoc.userId.profileImageId,
|
|
79
64
|
briefDescription: userDoc.userId.briefDescription,
|
|
80
65
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
instance: async function (
|
|
66
|
+
class PanelForm {
|
|
67
|
+
static Data = {};
|
|
68
|
+
static async instance(
|
|
85
69
|
options = {
|
|
86
70
|
idPanel: '',
|
|
87
71
|
defaultUrlImage: '',
|
|
@@ -98,11 +82,10 @@ const PanelForm = {
|
|
|
98
82
|
},
|
|
99
83
|
) {
|
|
100
84
|
const { idPanel, defaultUrlImage, appStore } = options;
|
|
101
|
-
|
|
102
85
|
// Authenticated users don't need 'public' tag - they see all their own posts
|
|
103
86
|
// Only include 'public' for unauthenticated users (handled by backend)
|
|
104
87
|
let prefixTags = [idPanel];
|
|
105
|
-
|
|
88
|
+
PanelForm.Data[idPanel] = {
|
|
106
89
|
originData: [],
|
|
107
90
|
data: [],
|
|
108
91
|
filesData: [],
|
|
@@ -112,7 +95,6 @@ const PanelForm = {
|
|
|
112
95
|
loading: false,
|
|
113
96
|
lasIdAvailable: null,
|
|
114
97
|
};
|
|
115
|
-
|
|
116
98
|
const formData = [
|
|
117
99
|
{
|
|
118
100
|
id: 'panel-title',
|
|
@@ -163,10 +145,9 @@ const PanelForm = {
|
|
|
163
145
|
},
|
|
164
146
|
},
|
|
165
147
|
];
|
|
166
|
-
|
|
167
148
|
const titleIcon = html`<i class="fa-solid fa-quote-left title-icon-${idPanel}"></i>`;
|
|
168
149
|
const panelRender = async ({ data }) =>
|
|
169
|
-
await Panel.
|
|
150
|
+
await Panel.instance({
|
|
170
151
|
idPanel,
|
|
171
152
|
formData,
|
|
172
153
|
data,
|
|
@@ -193,10 +174,8 @@ const PanelForm = {
|
|
|
193
174
|
render: imageShimmer(),
|
|
194
175
|
});
|
|
195
176
|
}
|
|
196
|
-
|
|
197
177
|
// Get the filesData for this item
|
|
198
178
|
const filesDataItem = PanelForm.Data[idPanel].filesData.find((f) => f._id === options.data._id);
|
|
199
|
-
|
|
200
179
|
// Priority 1: Check if there's an actual file (not markdown content)
|
|
201
180
|
// fileId array defaults to [null] for batch upload logic
|
|
202
181
|
const fileBlob = filesDataItem?.fileId?.fileBlob;
|
|
@@ -210,7 +189,6 @@ const PanelForm = {
|
|
|
210
189
|
},
|
|
211
190
|
});
|
|
212
191
|
}
|
|
213
|
-
|
|
214
192
|
// Priority 2: If no actual file, show default image
|
|
215
193
|
// (Don't show markdown content in file area - mdFileId stays in content area)
|
|
216
194
|
return await options.htmlRender({
|
|
@@ -236,7 +214,7 @@ const PanelForm = {
|
|
|
236
214
|
html: async () => {
|
|
237
215
|
return html`
|
|
238
216
|
<div class="in section-mp" style="text-align: center">
|
|
239
|
-
${Translate.
|
|
217
|
+
${Translate.instance('confirm-delete-item')}
|
|
240
218
|
<br />
|
|
241
219
|
"${data.title}"
|
|
242
220
|
</div>
|
|
@@ -252,29 +230,23 @@ const PanelForm = {
|
|
|
252
230
|
html: status,
|
|
253
231
|
status,
|
|
254
232
|
});
|
|
255
|
-
|
|
256
233
|
// Handle cid query param update (supports comma-separated list)
|
|
257
234
|
if (status === 'success') {
|
|
258
235
|
const currentCid = getQueryParams().cid;
|
|
259
|
-
|
|
260
236
|
if (currentCid) {
|
|
261
237
|
// Parse cid as comma-separated list
|
|
262
238
|
const cidList = currentCid
|
|
263
239
|
.split(',')
|
|
264
240
|
.map((id) => id.trim())
|
|
265
241
|
.filter((id) => id);
|
|
266
|
-
|
|
267
242
|
// Remove the deleted panel's id from the list
|
|
268
243
|
const updatedCidList = cidList.filter((id) => id !== data.id);
|
|
269
|
-
|
|
270
244
|
if (updatedCidList.length !== cidList.length) {
|
|
271
245
|
// Wait for DOM cleanup before updating query
|
|
272
|
-
|
|
273
246
|
if (updatedCidList.length === 0) {
|
|
274
247
|
// No cids remain, clear query and reload panels with limit
|
|
275
248
|
logger.warn('All cids removed, clearing query');
|
|
276
249
|
setQueryPath({ path: options.route, queryPath: '' });
|
|
277
|
-
|
|
278
250
|
if (options.parentIdModal) Modal.Data[options.parentIdModal].query = window.location.search;
|
|
279
251
|
if (PanelForm.Data[idPanel].updatePanel) await PanelForm.Data[idPanel].updatePanel();
|
|
280
252
|
} else {
|
|
@@ -285,41 +257,32 @@ const PanelForm = {
|
|
|
285
257
|
if (options.parentIdModal) Modal.Data[options.parentIdModal].query = actualQuery;
|
|
286
258
|
}
|
|
287
259
|
}
|
|
288
|
-
|
|
289
260
|
// Return early to skip smart deletion logic when cid is present
|
|
290
261
|
return { status };
|
|
291
262
|
}
|
|
292
263
|
}
|
|
293
|
-
|
|
294
264
|
// Smart deletion: remove from arrays and intelligently load more if needed
|
|
295
265
|
if (status === 'success') {
|
|
296
266
|
const panelData = PanelForm.Data[idPanel];
|
|
297
|
-
|
|
298
267
|
// Remove the deleted item from all data arrays
|
|
299
268
|
const indexInOrigin = panelData.originData.findIndex((d) => d._id === data._id);
|
|
300
269
|
const indexInData = panelData.data.findIndex((d) => d._id === data._id);
|
|
301
270
|
const indexInFiles = panelData.filesData.findIndex((d) => d._id === data._id);
|
|
302
|
-
|
|
303
271
|
if (indexInOrigin > -1) panelData.originData.splice(indexInOrigin, 1);
|
|
304
272
|
if (indexInData > -1) panelData.data.splice(indexInData, 1);
|
|
305
273
|
if (indexInFiles > -1) panelData.filesData.splice(indexInFiles, 1);
|
|
306
|
-
|
|
307
274
|
// Adjust skip count since we removed an item
|
|
308
275
|
if (panelData.skip > 0) panelData.skip--;
|
|
309
|
-
|
|
310
276
|
// If panels are below limit and there might be more, load them
|
|
311
277
|
if (panelData.data.length < panelData.limit && panelData.hasMore && !panelData.loading) {
|
|
312
278
|
const oldDataCount = panelData.data.length;
|
|
313
279
|
const needed = panelData.limit - panelData.data.length; // Calculate exact number needed
|
|
314
280
|
const originalLimit = panelData.limit;
|
|
315
|
-
|
|
316
281
|
// Temporarily set limit to only fetch what's needed (1-to-1 replacement)
|
|
317
282
|
panelData.limit = needed;
|
|
318
283
|
await getPanelData(true); // Load only the needed items
|
|
319
284
|
panelData.limit = originalLimit; // Restore original limit
|
|
320
|
-
|
|
321
285
|
const newItems = panelData.data.slice(oldDataCount);
|
|
322
|
-
|
|
323
286
|
if (oldDataCount === 0) {
|
|
324
287
|
// List was empty, render all panels
|
|
325
288
|
if (panelData.data.length > 0) {
|
|
@@ -331,7 +294,6 @@ const PanelForm = {
|
|
|
331
294
|
<div class="in panel-placeholder-bottom panel-placeholder-bottom-${idPanel}"></div>
|
|
332
295
|
`,
|
|
333
296
|
);
|
|
334
|
-
|
|
335
297
|
// Show spinner if there's potentially more data
|
|
336
298
|
const lastOriginItem = panelData.originData[panelData.originData.length - 1];
|
|
337
299
|
if (
|
|
@@ -360,7 +322,6 @@ const PanelForm = {
|
|
|
360
322
|
}
|
|
361
323
|
}
|
|
362
324
|
}
|
|
363
|
-
|
|
364
325
|
return { status };
|
|
365
326
|
}
|
|
366
327
|
return { status: 'error' };
|
|
@@ -392,33 +353,27 @@ const PanelForm = {
|
|
|
392
353
|
// Validate that either mdFileId has content OR fileId has files
|
|
393
354
|
const hasMdContent = data.mdFileId && data.mdFileId.trim().length > 0;
|
|
394
355
|
const hasFiles = data.fileId && data.fileId.length > 0;
|
|
395
|
-
|
|
396
356
|
if (!data.title || (!hasMdContent && !hasFiles)) {
|
|
397
357
|
NotificationManager.Push({
|
|
398
|
-
html: Translate.
|
|
358
|
+
html: Translate.instance('require-title-and-content-or-file'),
|
|
399
359
|
status: 'error',
|
|
400
360
|
});
|
|
401
361
|
return { data: [], status: 'error', message: 'Must provide either content or attach a file' };
|
|
402
362
|
}
|
|
403
|
-
|
|
404
363
|
// Sanitize title for filename - normalize UTF-8 string
|
|
405
364
|
// In browser, strings are already UTF-16, just ensure valid characters
|
|
406
365
|
const sanitizedTitle = sanitizeFilename(data.title);
|
|
407
|
-
|
|
408
366
|
let mdFileId;
|
|
409
367
|
const mdFileName = `${getCapVariableName(sanitizedTitle)}.md`;
|
|
410
368
|
const location = `${prefixTags.join('/')}`;
|
|
411
|
-
|
|
412
369
|
// Only create markdown file if there's actual content
|
|
413
370
|
let md = null;
|
|
414
371
|
let mdBlob = null;
|
|
415
372
|
let mdPlain = null;
|
|
416
|
-
|
|
417
373
|
if (hasMdContent) {
|
|
418
374
|
// Markdown content is already UTF-16 in browser, use as-is
|
|
419
375
|
const blob = new Blob([data.mdFileId], { type: 'text/markdown' });
|
|
420
376
|
md = new File([blob], mdFileName, { type: 'text/markdown' });
|
|
421
|
-
|
|
422
377
|
mdBlob = {
|
|
423
378
|
data: {
|
|
424
379
|
data: await getDataFromInputFile(md),
|
|
@@ -428,7 +383,6 @@ const PanelForm = {
|
|
|
428
383
|
};
|
|
429
384
|
mdPlain = await getRawContentFile(getBlobFromUint8ArrayFile(mdBlob.data.data, mdBlob.mimetype));
|
|
430
385
|
}
|
|
431
|
-
|
|
432
386
|
// Parse and normalize tags
|
|
433
387
|
// Note: 'public' tag is automatically extracted by the backend and converted to isPublic field
|
|
434
388
|
// It will be filtered from the tags array to keep visibility control separate from content tags
|
|
@@ -452,14 +406,12 @@ const PanelForm = {
|
|
|
452
406
|
originFileObj = PanelForm.Data[idPanel].filesData.find((d) => d._id === editId);
|
|
453
407
|
}
|
|
454
408
|
}
|
|
455
|
-
|
|
456
409
|
const baseNewDoc = newInstance(data);
|
|
457
410
|
baseNewDoc.tags = tags.filter((t) => !prefixTags.includes(t));
|
|
458
411
|
baseNewDoc.mdFileId = hasMdContent
|
|
459
412
|
? `<div class="markdown-content">${marked.parse(data.mdFileId)}</div>`
|
|
460
413
|
: null;
|
|
461
414
|
baseNewDoc.userId = appStore.Data.user?.main?.model?.user?._id;
|
|
462
|
-
|
|
463
415
|
// Ensure profileImageId is properly formatted as object with _id property
|
|
464
416
|
const profileImageIdValue = appStore.Data.user?.main?.model?.user?.profileImageId;
|
|
465
417
|
const formattedProfileImageId = profileImageIdValue
|
|
@@ -467,7 +419,6 @@ const PanelForm = {
|
|
|
467
419
|
? { _id: profileImageIdValue }
|
|
468
420
|
: profileImageIdValue
|
|
469
421
|
: null;
|
|
470
|
-
|
|
471
422
|
baseNewDoc.userInfo = {
|
|
472
423
|
username: appStore.Data.user?.main?.model?.user?.username,
|
|
473
424
|
email: appStore.Data.user?.main?.model?.user?.email,
|
|
@@ -475,18 +426,14 @@ const PanelForm = {
|
|
|
475
426
|
profileImageId: formattedProfileImageId,
|
|
476
427
|
};
|
|
477
428
|
baseNewDoc.tools = true;
|
|
478
|
-
|
|
479
429
|
const documents = [];
|
|
480
430
|
let message = '';
|
|
481
431
|
let status = 'success';
|
|
482
432
|
let indexFormDoc = -1;
|
|
483
|
-
|
|
484
433
|
const inputFiles = data.fileId ? data.fileId : [null];
|
|
485
|
-
|
|
486
434
|
for (const file of inputFiles) {
|
|
487
435
|
indexFormDoc++;
|
|
488
436
|
let fileId = undefined; // Reset for each iteration - only set if user uploaded a file
|
|
489
|
-
|
|
490
437
|
await (async () => {
|
|
491
438
|
const body = new FormData();
|
|
492
439
|
// Only append md file if it was created (has content)
|
|
@@ -495,7 +442,7 @@ const PanelForm = {
|
|
|
495
442
|
const { status, data: uploadedFiles } = await FileService.post({ body });
|
|
496
443
|
// await timer(3000);
|
|
497
444
|
NotificationManager.Push({
|
|
498
|
-
html: Translate.
|
|
445
|
+
html: Translate.instance(`${status}-upload-file`),
|
|
499
446
|
status,
|
|
500
447
|
});
|
|
501
448
|
if (status === 'success' && uploadedFiles && Array.isArray(uploadedFiles)) {
|
|
@@ -506,7 +453,6 @@ const PanelForm = {
|
|
|
506
453
|
// Both can be markdown files, but we must distinguish:
|
|
507
454
|
// Rich text editor content → mdFileId
|
|
508
455
|
// User-uploaded file → fileId
|
|
509
|
-
|
|
510
456
|
for (const uploadedFile of uploadedFiles) {
|
|
511
457
|
if (hasMdContent && uploadedFile.name === mdFileName) {
|
|
512
458
|
// This is the markdown file created FROM rich text editor
|
|
@@ -518,7 +464,6 @@ const PanelForm = {
|
|
|
518
464
|
logger.info(`Assigned user-uploaded file to fileId: ${uploadedFile.name}`);
|
|
519
465
|
}
|
|
520
466
|
}
|
|
521
|
-
|
|
522
467
|
// Validation: mdFileId should exist only if rich text content was provided
|
|
523
468
|
if (hasMdContent && !mdFileId) {
|
|
524
469
|
logger.error(
|
|
@@ -544,7 +489,6 @@ const PanelForm = {
|
|
|
544
489
|
: await DocumentService.post({
|
|
545
490
|
body,
|
|
546
491
|
});
|
|
547
|
-
|
|
548
492
|
const newDoc = {
|
|
549
493
|
...baseNewDoc,
|
|
550
494
|
fileId: file ? URL.createObjectURL(file) : undefined,
|
|
@@ -561,10 +505,8 @@ const PanelForm = {
|
|
|
561
505
|
? userInfoFactory(documentData)
|
|
562
506
|
: null),
|
|
563
507
|
};
|
|
564
|
-
|
|
565
508
|
if (documentStatus === 'error') status = 'error';
|
|
566
509
|
if (message) message += `${indexFormDoc === 0 ? '' : ', '}${documentMessage}`;
|
|
567
|
-
|
|
568
510
|
const filesData = {
|
|
569
511
|
id: documentData._id,
|
|
570
512
|
_id: documentData._id,
|
|
@@ -582,7 +524,6 @@ const PanelForm = {
|
|
|
582
524
|
filePlain: undefined,
|
|
583
525
|
},
|
|
584
526
|
};
|
|
585
|
-
|
|
586
527
|
if (originObj && indexFormDoc === 0) {
|
|
587
528
|
PanelForm.Data[idPanel].originData[indexOriginObj] = documentData;
|
|
588
529
|
PanelForm.Data[idPanel].data[indexOriginObj] = newDoc;
|
|
@@ -594,31 +535,26 @@ const PanelForm = {
|
|
|
594
535
|
}
|
|
595
536
|
documents.push(newDoc);
|
|
596
537
|
}
|
|
597
|
-
|
|
598
538
|
NotificationManager.Push({
|
|
599
539
|
html:
|
|
600
540
|
status === 'success'
|
|
601
541
|
? originObj
|
|
602
|
-
? Translate.
|
|
603
|
-
: Translate.
|
|
542
|
+
? Translate.instance('success-edit-post')
|
|
543
|
+
: Translate.instance('success-add-post')
|
|
604
544
|
: message,
|
|
605
545
|
status: status,
|
|
606
546
|
});
|
|
607
|
-
|
|
608
547
|
setQueryPath({ path: options.route, queryPath: documents.map((d) => d._id).join(',') });
|
|
609
548
|
if (options.parentIdModal) Modal.Data[options.parentIdModal].query = `${window.location.search}`;
|
|
610
|
-
|
|
611
549
|
return { data: documents, status, message };
|
|
612
550
|
},
|
|
613
551
|
},
|
|
614
552
|
});
|
|
615
|
-
|
|
616
553
|
const getPanelData = async (isLoadMore = false) => {
|
|
617
554
|
const panelData = PanelForm.Data[idPanel];
|
|
618
555
|
logger.warn('getPanelData called, isLoadMore:', isLoadMore);
|
|
619
556
|
try {
|
|
620
557
|
const cidQuery = getQueryParams().cid;
|
|
621
|
-
|
|
622
558
|
// When cid query exists, bypass pagination and loading checks
|
|
623
559
|
if (!cidQuery) {
|
|
624
560
|
if (panelData.loading || !panelData.hasMore) {
|
|
@@ -626,48 +562,39 @@ const PanelForm = {
|
|
|
626
562
|
return;
|
|
627
563
|
}
|
|
628
564
|
}
|
|
629
|
-
|
|
630
565
|
panelData.loading = true;
|
|
631
|
-
|
|
632
566
|
if (!isLoadMore) {
|
|
633
567
|
// Reset for a fresh load
|
|
634
568
|
panelData.skip = 0;
|
|
635
569
|
panelData.hasMore = true;
|
|
636
570
|
}
|
|
637
|
-
|
|
638
571
|
// When cid query exists, don't apply skip/limit pagination
|
|
639
572
|
const params = {
|
|
640
573
|
tags: prefixTags.join(','),
|
|
641
574
|
...(cidQuery && { cid: cidQuery }),
|
|
642
575
|
};
|
|
643
|
-
|
|
644
576
|
// Only apply pagination when there's no cid query
|
|
645
577
|
if (!cidQuery) {
|
|
646
578
|
params.skip = panelData.skip;
|
|
647
579
|
params.limit = panelData.limit;
|
|
648
580
|
}
|
|
649
|
-
|
|
650
581
|
const result = await DocumentService.get({
|
|
651
582
|
params,
|
|
652
583
|
id: 'public/',
|
|
653
584
|
});
|
|
654
|
-
|
|
655
585
|
if (result.status === 'success') {
|
|
656
586
|
if (!isLoadMore) {
|
|
657
587
|
panelData.originData = [];
|
|
658
588
|
panelData.filesData = [];
|
|
659
589
|
panelData.data = [];
|
|
660
590
|
}
|
|
661
|
-
|
|
662
591
|
panelData.originData.push(...newInstance(result.data.data));
|
|
663
592
|
panelData.lasIdAvailable = result.data.lastId;
|
|
664
|
-
|
|
665
593
|
for (const documentObject of result.data.data) {
|
|
666
594
|
let mdFileId, fileId;
|
|
667
595
|
let mdBlob, fileBlob;
|
|
668
596
|
let mdPlain, filePlain;
|
|
669
597
|
let parsedMarkdown = '';
|
|
670
|
-
|
|
671
598
|
try {
|
|
672
599
|
// Fetch markdown content if mdFileId exists
|
|
673
600
|
if (documentObject.mdFileId) {
|
|
@@ -693,7 +620,6 @@ const PanelForm = {
|
|
|
693
620
|
parsedMarkdown = '';
|
|
694
621
|
}
|
|
695
622
|
}
|
|
696
|
-
|
|
697
623
|
// Handle optional fileId
|
|
698
624
|
if (documentObject.fileId) {
|
|
699
625
|
const fileIdValue = documentObject.fileId._id || documentObject.fileId;
|
|
@@ -708,7 +634,6 @@ const PanelForm = {
|
|
|
708
634
|
logger.error('Error fetching file metadata:', fileIdValue, fetchError);
|
|
709
635
|
}
|
|
710
636
|
}
|
|
711
|
-
|
|
712
637
|
// Store file metadata and references
|
|
713
638
|
panelData.filesData.push({
|
|
714
639
|
id: documentObject._id,
|
|
@@ -716,7 +641,6 @@ const PanelForm = {
|
|
|
716
641
|
mdFileId: { mdBlob, mdPlain },
|
|
717
642
|
fileId: { fileBlob, filePlain },
|
|
718
643
|
});
|
|
719
|
-
|
|
720
644
|
// Add to data array for display - use pre-parsed markdown
|
|
721
645
|
panelData.data.push({
|
|
722
646
|
id: documentObject._id,
|
|
@@ -753,7 +677,6 @@ const PanelForm = {
|
|
|
753
677
|
mdFileId: { mdBlob: null, mdPlain: '' },
|
|
754
678
|
fileId: { fileBlob: null, filePlain: undefined },
|
|
755
679
|
});
|
|
756
|
-
|
|
757
680
|
panelData.data.push({
|
|
758
681
|
id: documentObject._id,
|
|
759
682
|
title: documentObject.title,
|
|
@@ -780,7 +703,6 @@ const PanelForm = {
|
|
|
780
703
|
});
|
|
781
704
|
}
|
|
782
705
|
}
|
|
783
|
-
|
|
784
706
|
// Only update pagination when not using cid query
|
|
785
707
|
if (!cidQuery) {
|
|
786
708
|
panelData.skip += result.data.data.length;
|
|
@@ -789,7 +711,6 @@ const PanelForm = {
|
|
|
789
711
|
// When cid query is used, disable infinite scroll
|
|
790
712
|
panelData.hasMore = false;
|
|
791
713
|
}
|
|
792
|
-
|
|
793
714
|
const lastItem = result.data.data[result.data.data.length - 1];
|
|
794
715
|
if (result.data.data.length === 0 || (lastItem && lastItem._id === panelData.lasIdAvailable)) {
|
|
795
716
|
LoadingAnimation.spinner.stop(`.panel-placeholder-bottom-${idPanel}`);
|
|
@@ -805,7 +726,6 @@ const PanelForm = {
|
|
|
805
726
|
} catch (error) {
|
|
806
727
|
logger.error(error);
|
|
807
728
|
}
|
|
808
|
-
|
|
809
729
|
await timer(250);
|
|
810
730
|
panelData.loading = false;
|
|
811
731
|
LoadingAnimation.spinner.stop(`.panel-placeholder-bottom-${idPanel}`);
|
|
@@ -859,10 +779,10 @@ const PanelForm = {
|
|
|
859
779
|
let loadingGetData = false;
|
|
860
780
|
closeModalRouteChangeEvents[idPanel] = () => {
|
|
861
781
|
setTimeout(() => {
|
|
862
|
-
|
|
782
|
+
PanelForm.Data[idPanel].updatePanel();
|
|
863
783
|
});
|
|
864
784
|
};
|
|
865
|
-
|
|
785
|
+
PanelForm.Data[idPanel].updatePanel = async (...args) => {
|
|
866
786
|
const _updatePanel = async (...args) => {
|
|
867
787
|
try {
|
|
868
788
|
const cid = getQueryParams().cid ? getQueryParams().cid : '';
|
|
@@ -871,7 +791,6 @@ const PanelForm = {
|
|
|
871
791
|
appStore.Data.user.main.model.user &&
|
|
872
792
|
appStore.Data.user.main.model.user._id &&
|
|
873
793
|
lastUserId !== appStore.Data.user.main.model.user._id;
|
|
874
|
-
|
|
875
794
|
logger.warn(
|
|
876
795
|
{
|
|
877
796
|
idPanel,
|
|
@@ -882,22 +801,18 @@ const PanelForm = {
|
|
|
882
801
|
? JSON.stringify(appStore.Data.user.main.model.user, null, 4)
|
|
883
802
|
: 'No user data',
|
|
884
803
|
);
|
|
885
|
-
|
|
886
804
|
// Normalize empty values for comparison (undefined, null, '' should all be treated as empty)
|
|
887
805
|
const normalizedCid = cid || '';
|
|
888
806
|
const normalizedLastCid = lastCid || '';
|
|
889
|
-
|
|
890
807
|
if (loadingGetData || (normalizedLastCid === normalizedCid && !forceUpdate)) return;
|
|
891
808
|
loadingGetData = true;
|
|
892
809
|
lastUserId = appStore.Data.user?.main?.model?.user?._id
|
|
893
810
|
? newInstance(appStore.Data.user.main.model.user._id)
|
|
894
811
|
: null;
|
|
895
812
|
lastCid = cid;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
this.Data[idPanel] = {
|
|
900
|
-
...this.Data[idPanel],
|
|
813
|
+
logger.warn('instance render panel data');
|
|
814
|
+
PanelForm.Data[idPanel] = {
|
|
815
|
+
...PanelForm.Data[idPanel],
|
|
901
816
|
originData: [],
|
|
902
817
|
data: [],
|
|
903
818
|
filesData: [],
|
|
@@ -905,47 +820,37 @@ const PanelForm = {
|
|
|
905
820
|
hasMore: true,
|
|
906
821
|
loading: false,
|
|
907
822
|
};
|
|
908
|
-
|
|
909
823
|
// Always reset skip to 0 when reloading (whether cid exists or not)
|
|
910
|
-
|
|
911
|
-
|
|
824
|
+
PanelForm.Data[idPanel].skip = 0;
|
|
912
825
|
const containerSelector = `.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`;
|
|
913
826
|
htmls(containerSelector, await renderSrrPanelData());
|
|
914
|
-
|
|
915
827
|
await getPanelData();
|
|
916
|
-
|
|
917
828
|
htmls(
|
|
918
829
|
containerSelector,
|
|
919
830
|
html`
|
|
920
|
-
<div class="in">${await panelRender({ data:
|
|
831
|
+
<div class="in">${await panelRender({ data: PanelForm.Data[idPanel].data })}</div>
|
|
921
832
|
<div class="in panel-placeholder-bottom panel-placeholder-bottom-${idPanel}"></div>
|
|
922
833
|
`,
|
|
923
834
|
);
|
|
924
|
-
|
|
925
|
-
const lastOriginItem = this.Data[idPanel].originData[this.Data[idPanel].originData.length - 1];
|
|
835
|
+
const lastOriginItem = PanelForm.Data[idPanel].originData[PanelForm.Data[idPanel].originData.length - 1];
|
|
926
836
|
if (
|
|
927
|
-
!
|
|
837
|
+
!PanelForm.Data[idPanel].lasIdAvailable ||
|
|
928
838
|
!lastOriginItem ||
|
|
929
|
-
|
|
839
|
+
PanelForm.Data[idPanel].lasIdAvailable !== lastOriginItem._id
|
|
930
840
|
)
|
|
931
841
|
LoadingAnimation.spinner.play(`.panel-placeholder-bottom-${idPanel}`, 'dual-ring-mini');
|
|
932
|
-
|
|
933
842
|
const scrollContainerSelector = `.modal-${options.route}`;
|
|
934
|
-
|
|
935
843
|
// Always remove old scroll event before setting new one
|
|
936
|
-
if (
|
|
937
|
-
|
|
844
|
+
if (PanelForm.Data[idPanel].removeScrollEvent) {
|
|
845
|
+
PanelForm.Data[idPanel].removeScrollEvent();
|
|
938
846
|
}
|
|
939
|
-
|
|
940
847
|
if (cid) {
|
|
941
848
|
LoadingAnimation.spinner.stop(`.panel-placeholder-bottom-${idPanel}`);
|
|
942
849
|
return;
|
|
943
850
|
}
|
|
944
|
-
|
|
945
851
|
const { removeEvent } = Scroll.setEvent(scrollContainerSelector, async (payload) => {
|
|
946
852
|
const panelData = PanelForm.Data[idPanel];
|
|
947
853
|
if (!panelData) return;
|
|
948
|
-
|
|
949
854
|
// Infinite scroll: load more items at bottom
|
|
950
855
|
if (payload.atBottom && panelData.hasMore && !panelData.loading) {
|
|
951
856
|
const oldDataCount = panelData.data.length;
|
|
@@ -957,8 +862,7 @@ const PanelForm = {
|
|
|
957
862
|
}
|
|
958
863
|
}
|
|
959
864
|
});
|
|
960
|
-
|
|
961
|
-
|
|
865
|
+
PanelForm.Data[idPanel].removeScrollEvent = removeEvent;
|
|
962
866
|
if (!firsUpdateEvent && options.firsUpdateEvent) {
|
|
963
867
|
firsUpdateEvent = true;
|
|
964
868
|
await options.firsUpdateEvent();
|
|
@@ -967,7 +871,6 @@ const PanelForm = {
|
|
|
967
871
|
logger.error(error);
|
|
968
872
|
}
|
|
969
873
|
};
|
|
970
|
-
|
|
971
874
|
await _updatePanel(...args);
|
|
972
875
|
loadingGetData = false;
|
|
973
876
|
};
|
|
@@ -998,14 +901,11 @@ const PanelForm = {
|
|
|
998
901
|
await PanelForm.Data[idPanel].updatePanel();
|
|
999
902
|
};
|
|
1000
903
|
}
|
|
1001
|
-
|
|
1002
904
|
if (options.parentIdModal) {
|
|
1003
905
|
htmls(`.html-${options.parentIdModal}`, await renderSrrPanelData());
|
|
1004
906
|
return '';
|
|
1005
907
|
}
|
|
1006
|
-
|
|
1007
908
|
return await renderSrrPanelData();
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
|
|
909
|
+
}
|
|
910
|
+
}
|
|
1011
911
|
export { PanelForm };
|