@smarterplan/ngx-smarterplan-core 1.4.13 → 1.4.15
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/esm2022/lib/pipes/hashtag-from-id.pipe.mjs +26 -7
- package/esm2022/lib/services/matterport-tag-copy.service.mjs +1230 -201
- package/esm2022/lib/services/models/comment.service.mjs +35 -1
- package/esm2022/lib/services/models/domain.service.mjs +20 -1
- package/esm2022/lib/services/models/equipment.service.mjs +7 -3
- package/esm2022/lib/services/models/feature.service.mjs +20 -1
- package/esm2022/lib/services/models/hashtag.service.mjs +29 -2
- package/esm2022/lib/services/models/mission.service.mjs +17 -1
- package/esm2022/lib/services/s3.service.mjs +29 -1
- package/esm2022/lib/services/utils/matterport-transform.util.mjs +29 -3
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +1503 -287
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
- package/lib/pipes/hashtag-from-id.pipe.d.ts +9 -0
- package/lib/services/matterport-tag-copy.service.d.ts +320 -19
- package/lib/services/models/comment.service.d.ts +16 -0
- package/lib/services/models/domain.service.d.ts +8 -0
- package/lib/services/models/equipment.service.d.ts +4 -1
- package/lib/services/models/feature.service.d.ts +5 -0
- package/lib/services/models/hashtag.service.d.ts +6 -0
- package/lib/services/models/mission.service.d.ts +5 -0
- package/lib/services/s3.service.d.ts +14 -0
- package/lib/services/utils/matterport-transform.util.d.ts +16 -1
- package/package.json +1 -1
|
@@ -4,6 +4,15 @@ import * as i0 from "@angular/core";
|
|
|
4
4
|
export declare class HashtagFromIdPipe implements PipeTransform {
|
|
5
5
|
private hashtagService;
|
|
6
6
|
constructor(hashtagService: HashtagService);
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a hashtag id to its display name.
|
|
9
|
+
*
|
|
10
|
+
* The cache is consulted for THIS id, and the network answers whenever the id is not
|
|
11
|
+
* in it.
|
|
12
|
+
*
|
|
13
|
+
* Hashtag ids are globally unique, so a cache holding entries from several spaces can
|
|
14
|
+
* only ever produce a correct hit or a miss, never a wrong name.
|
|
15
|
+
*/
|
|
7
16
|
transform(hashtagID: string): Promise<string>;
|
|
8
17
|
static ɵfac: i0.ɵɵFactoryDeclaration<HashtagFromIdPipe, never>;
|
|
9
18
|
static ɵpipe: i0.ɵɵPipeDeclaration<HashtagFromIdPipe, "hashtagFromID", false>;
|
|
@@ -8,6 +8,10 @@ import { Object3DService } from './models/object3D.service';
|
|
|
8
8
|
import { FeatureService } from './models/feature.service';
|
|
9
9
|
import { MeasurementService } from './models/measurement.service';
|
|
10
10
|
import { CommentService } from './models/comment.service';
|
|
11
|
+
import { HashtagService } from './models/hashtag.service';
|
|
12
|
+
import { MissionService } from './models/mission.service';
|
|
13
|
+
import { OperationService } from './models/operation.service';
|
|
14
|
+
import { DomainService } from './models/domain.service';
|
|
11
15
|
import { BaseUserService } from './user.service';
|
|
12
16
|
import { Vec3 } from './utils/matterport-transform.util';
|
|
13
17
|
import * as i0 from "@angular/core";
|
|
@@ -20,11 +24,16 @@ export interface CopyProgress {
|
|
|
20
24
|
totalOverall: number;
|
|
21
25
|
percent: number;
|
|
22
26
|
errorCount: number;
|
|
27
|
+
/** Items that already existed in the target space and had missing identity fields
|
|
28
|
+
* filled in, rather than being newly created — see healEquipmentIdentity() and its
|
|
29
|
+
* ticket/feature equivalents. */
|
|
30
|
+
healedCount: number;
|
|
23
31
|
}
|
|
24
|
-
/** A single line in the copy log —
|
|
32
|
+
/** A single line in the copy log — a step status (updated in place), or an error / healed
|
|
33
|
+
* item notice (appended once, permanent). */
|
|
25
34
|
export interface CopyLogEntry {
|
|
26
35
|
id: string;
|
|
27
|
-
type: 'step' | 'error';
|
|
36
|
+
type: 'step' | 'error' | 'healed';
|
|
28
37
|
text: string;
|
|
29
38
|
}
|
|
30
39
|
export declare class MatterportTagCopyService {
|
|
@@ -37,35 +46,314 @@ export declare class MatterportTagCopyService {
|
|
|
37
46
|
private featureService;
|
|
38
47
|
private measurementService;
|
|
39
48
|
private commentService;
|
|
49
|
+
private hashtagService;
|
|
50
|
+
private missionService;
|
|
51
|
+
private operationService;
|
|
52
|
+
private domainService;
|
|
40
53
|
private translate;
|
|
41
54
|
copyProgress$: BehaviorSubject<CopyProgress | null>;
|
|
42
55
|
copyLog$: BehaviorSubject<CopyLogEntry[]>;
|
|
43
56
|
private spModule;
|
|
44
57
|
private log;
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
/** Unique-id sequence shared by every permanent (non-step) log line — errors and healed-item notices alike. */
|
|
59
|
+
private logSeq;
|
|
60
|
+
/** source element id -> id of its copy in the target space. */
|
|
61
|
+
private elementIds;
|
|
62
|
+
/** source Comment id -> id of its copy. Needed to remap POI.tagImageID, which is a
|
|
63
|
+
* reference to a Comment record (see the schema: "connection to comment.id"). */
|
|
64
|
+
private commentIds;
|
|
65
|
+
/** source Hashtag id -> its name. */
|
|
66
|
+
private sourceHashtagNames;
|
|
67
|
+
/** normalised hashtag name -> its id in the TARGET space (pre-existing or just created). */
|
|
68
|
+
private targetHashtagIdsByName;
|
|
69
|
+
/** ids of the hashtags this run created in the target space. */
|
|
70
|
+
private createdHashtagIds;
|
|
71
|
+
/** ids of the hashtags actually persisted on a successfully created feature. Anything in
|
|
72
|
+
* createdHashtagIds but absent here is an orphan and is deleted at the end of the run. */
|
|
73
|
+
private usedHashtagIds;
|
|
74
|
+
/** source ids of the elements whose POIs were already created by an element phase.
|
|
75
|
+
* Guards the standalone POI phase against creating a second, impoverished copy. */
|
|
76
|
+
private handledElementIds;
|
|
77
|
+
/** source Mission id -> its userID. */
|
|
78
|
+
private sourceMissionUserIds;
|
|
79
|
+
/** userID -> that user's Mission id in the TARGET space (only set when they have one). */
|
|
80
|
+
private targetMissionIdsByUserId;
|
|
81
|
+
/** id -> Domain, for every domain visible from EITHER space's operator mission (its
|
|
82
|
+
* organisationID, its orderedOrganisationID, or generic/no-organisation domains) — a
|
|
83
|
+
* superset covering whatever a source object's domainID could point at. */
|
|
84
|
+
private domainsById;
|
|
85
|
+
/** organisationIDs the TARGET operator's mission can see domains from. A domain whose
|
|
86
|
+
* own organisationID is in this set (or has none at all) is already directly
|
|
87
|
+
* selectable in the target space's own dropdown, so its id is reused unchanged. */
|
|
88
|
+
private targetVisibleDomainOrgIds;
|
|
89
|
+
/** `${type}::${normalised name}` -> that domain's id, among domains visible to the
|
|
90
|
+
* TARGET operator — used to find an equivalent when the exact id isn't reusable. */
|
|
91
|
+
private targetDomainIdsByTypeAndName;
|
|
92
|
+
/** Target space id of the run in progress — used to re-home copied tag icons. */
|
|
93
|
+
private targetSpaceId;
|
|
94
|
+
constructor(navigationService: NavigationService, userService: BaseUserService, poiService: PoiService, ticketService: TicketsService, equipmentService: EquipmentService, object3DService: Object3DService, featureService: FeatureService, measurementService: MeasurementService, commentService: CommentService, hashtagService: HashtagService, missionService: MissionService, operationService: OperationService, domainService: DomainService, translate: TranslateService);
|
|
47
95
|
copyTagsFromVisit(sourceSpaceId: string, targetSpaceId: string, landmarkSourceA: Vec3, landmarkTargetA: Vec3, landmarkSourceB: Vec3, landmarkTargetB: Vec3): Promise<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Serialises the current copy log (step lines as last emitted, plus every error and
|
|
98
|
+
* healed-item notice) to a plain-text file and triggers a browser download — wired to a
|
|
99
|
+
* "download log" button in the copy UI, so an operator can keep or share the full report
|
|
100
|
+
* of a run rather than only the ~200px scrollable panel showing it.
|
|
101
|
+
*/
|
|
102
|
+
downloadLog(filename?: string): void;
|
|
48
103
|
/** Adds or updates a step line (identified by stepId) with new text, then republishes the log. */
|
|
49
104
|
private setStepLog;
|
|
50
105
|
/** Appends a new, permanent log line (never overwritten), then republishes the log. */
|
|
51
106
|
private appendErrorLog;
|
|
107
|
+
/** Appends a permanent log line for an item that was repaired rather than newly
|
|
108
|
+
* created — see healEquipmentIdentity() and its ticket/feature equivalents. */
|
|
109
|
+
private appendHealedLog;
|
|
52
110
|
/** Resets the log for a new run. */
|
|
53
111
|
private resetLog;
|
|
112
|
+
/** Clears every id map carried over from a previous run. The service is a root-level
|
|
113
|
+
* singleton, so leftover mappings would silently link a new copy to records created
|
|
114
|
+
* during the previous one. */
|
|
115
|
+
private resetRunState;
|
|
54
116
|
/** Builds and sets a step's log line as "<translated label>: <percent>%", no step numbering. */
|
|
55
117
|
private setStepProgress;
|
|
56
118
|
/** Logs a translated, parameterized error line for a single failed item. */
|
|
57
119
|
private logItemError;
|
|
120
|
+
/** Logs a translated line for an item that already existed in the target space and had
|
|
121
|
+
* its missing identity fields filled in, so the operator can tell it apart from a
|
|
122
|
+
* freshly created item in the copy report. */
|
|
123
|
+
private logHealedItem;
|
|
58
124
|
/** Logs an item-level error, using a specific "missing file / orphaned reference"
|
|
59
125
|
* translation key when the cause is a MissingS3FileError, and the generic per-type
|
|
60
126
|
* key otherwise. Keeps the copy log readable: a missing file is not a bug to
|
|
61
|
-
* investigate the same way a network/permission error is.
|
|
127
|
+
* investigate the same way a network/permission error is.
|
|
128
|
+
* `extraParams` carries the placeholders a few keys need on top of {{name}}/{{error}}
|
|
129
|
+
* (e.g. errorFeatureAttachment's {{feature}}, errorPoi's {{sweep}}), which used to be
|
|
130
|
+
* rendered as raw "{{feature}}" text in the log. */
|
|
62
131
|
private logCopyItemError;
|
|
63
132
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
133
|
+
* Null-safe equivalent of FeatureService.isArchived(): true when the operator has archived
|
|
134
|
+
* this feature on that space. "No mission on the space" answers false (nothing archived)
|
|
135
|
+
* instead of throwing, which is what made a copy from a space the operator is not a member
|
|
136
|
+
* of fail outright.
|
|
137
|
+
*/
|
|
138
|
+
private isArchivedForOperator;
|
|
139
|
+
/** Normalised lookup key for a domain: type disambiguates same-named domains used for
|
|
140
|
+
* different purposes (equipment vs. ticket vs. feature categories, ...). */
|
|
141
|
+
private domainKey;
|
|
142
|
+
/**
|
|
143
|
+
* READ-ONLY indexing of every domain either operator's Mission can see. Creates
|
|
144
|
+
* nothing — unlike hashtags, a domain is a business taxonomy entry an operator manages
|
|
145
|
+
* deliberately; this tool only ever reuses an EXISTING equivalent, it never invents one.
|
|
146
|
+
*
|
|
147
|
+
* Visibility is keyed to `currentMission(spaceId).organisationID` /
|
|
148
|
+
* `.orderedOrganisationID` for EACH space — exactly what
|
|
149
|
+
* `DomainService.getDomainsForOrgForType()` uses to populate the category dropdown on
|
|
150
|
+
* the equipment/ticket/feature edit forms (see form-equipment.component.ts,
|
|
151
|
+
* batch-edit-equipment.component.ts, form-feature.component.ts). Comparing the spaces'
|
|
152
|
+
* OWN `organisationID` instead — the previous approach — missed every domain reachable
|
|
153
|
+
* only through the operator's `orderedOrganisationID` (set when a mission is created "on
|
|
154
|
+
* behalf of" another organisation — see form-mission.component.ts), which is exactly how
|
|
155
|
+
* a domain ends up visible on two spaces that belong to different organisations.
|
|
156
|
+
*/
|
|
157
|
+
private loadDomainIndex;
|
|
158
|
+
/**
|
|
159
|
+
* Resolves a source domainID (equipment/ticket/feature/POI category) for use in the
|
|
160
|
+
* target space.
|
|
161
|
+
*
|
|
162
|
+
* - generic domain (no organisationID of its own — a shared taxonomy entry, e.g. a
|
|
163
|
+
* standard category created by the platform): reused unchanged, valid everywhere.
|
|
164
|
+
* - the domain's own organisationID is already one the TARGET operator's mission can
|
|
165
|
+
* see (its organisationID, or its orderedOrganisationID): reused unchanged too — the
|
|
166
|
+
* id is directly selectable in the target space's own dropdown as-is.
|
|
167
|
+
* - otherwise: matched by (type, name) against the domains visible to the TARGET
|
|
168
|
+
* operator (loadDomainIndex()); dropped when no equivalent exists there.
|
|
169
|
+
*/
|
|
170
|
+
private resolveDomainID;
|
|
171
|
+
/** Normalised lookup key for a hashtag name, so "Moyen Âge" and "moyen âge " match. */
|
|
172
|
+
private hashtagKey;
|
|
173
|
+
/**
|
|
174
|
+
* READ-ONLY indexing of both spaces' hashtags. Creates nothing.
|
|
175
|
+
*
|
|
176
|
+
* `Feature.hashtags` stores Hashtag *ids*, and a Hashtag is a per-space record, so the
|
|
177
|
+
* ids have to be translated by name. The translation table is built here; the missing
|
|
178
|
+
* keywords are created later, one at a time, only when a POI that actually uses them is
|
|
179
|
+
* about to be written (see resolveHashtagIds()).
|
|
180
|
+
*/
|
|
181
|
+
private loadHashtagIndex;
|
|
182
|
+
/**
|
|
183
|
+
* Translates one feature's hashtag ids into target-space ids, creating on the fly the
|
|
184
|
+
* keywords the target space does not have yet.
|
|
185
|
+
*
|
|
186
|
+
* Creation is LAZY on purpose. Doing it up front, for the whole source vocabulary, meant
|
|
187
|
+
* that a copy which failed or was closed halfway left behind keywords no POI referenced —
|
|
188
|
+
* pollution the operator had to clean up by hand. Here a keyword is only ever written
|
|
189
|
+
* when a POI that carries it is about to be created, so an interrupted copy stops
|
|
190
|
+
* creating keywords at the same POI it stops creating content.
|
|
191
|
+
*
|
|
192
|
+
* The residual window (keyword created, then createFeature fails) is closed by
|
|
193
|
+
* markHashtagsUsed() + cleanupUnusedCreatedHashtags().
|
|
194
|
+
*
|
|
195
|
+
* A keyword that can neither be found nor created is dropped rather than left as a
|
|
196
|
+
* dangling id.
|
|
197
|
+
*/
|
|
198
|
+
private resolveHashtagIds;
|
|
199
|
+
/**
|
|
200
|
+
* READ-ONLY indexing of both spaces' missions.
|
|
201
|
+
*
|
|
202
|
+
* A Mission is scoped to one space (it pairs a userID with a spaceID), so the id a
|
|
203
|
+
* source comment's `ownerID` points at never designates anyone in the target space.
|
|
204
|
+
* This builds the two lookup tables resolveCommentOwnerID() needs to translate it:
|
|
205
|
+
* which user a source Mission id belongs to, and which Mission (if any) that same
|
|
206
|
+
* user already holds in the target space.
|
|
207
|
+
*/
|
|
208
|
+
private loadMissionIndex;
|
|
209
|
+
/**
|
|
210
|
+
* Translates a source comment's `ownerID` (a per-space Mission id) into the Mission of
|
|
211
|
+
* the SAME user in the target space, so the copy keeps crediting its real author.
|
|
212
|
+
*
|
|
213
|
+
* Substituting the current operator's mission — the way `ownerMissionID` legitimately
|
|
214
|
+
* does for tickets/equipment/features, which really do change owner on a copy — would
|
|
215
|
+
* misattribute every comment to whoever ran the copy instead of who actually wrote it.
|
|
216
|
+
*
|
|
217
|
+
* When the original author has no mission in the target space (never granted access
|
|
218
|
+
* there), the comment is left with no owner rather than a wrong one: an absent author
|
|
219
|
+
* is honest, a misattributed one is not.
|
|
220
|
+
*/
|
|
221
|
+
private resolveCommentOwnerID;
|
|
222
|
+
/** Records that these hashtags are now referenced by a feature that really exists, so
|
|
223
|
+
* the end-of-run cleanup leaves them alone. */
|
|
224
|
+
private markHashtagsUsed;
|
|
225
|
+
/**
|
|
226
|
+
* Deletes the hashtags this run created that no copied POI ended up referencing.
|
|
227
|
+
*
|
|
228
|
+
* Runs on every exit path — success, general failure, or exception — so the target space
|
|
229
|
+
* never keeps keywords that came from an aborted copy. Only ids created by THIS run are
|
|
230
|
+
* ever considered: a pre-existing keyword of the target space is never touched, even if
|
|
231
|
+
* no copied POI uses it.
|
|
232
|
+
*/
|
|
233
|
+
private cleanupUnusedCreatedHashtags;
|
|
234
|
+
/**
|
|
235
|
+
* Orders a feature's media set exactly the way the UI will read it back, and repairs
|
|
236
|
+
* the ordering key when the source data cannot express the user's intended order.
|
|
237
|
+
*
|
|
238
|
+
* The viewer sorts each media family on its own (`setImages`, `setVideos`, `setAudio`,
|
|
239
|
+
* `setDocuments`, ... all do `.sort((a, b) => a.index - b.index)`), so `index` only has
|
|
240
|
+
* to be consistent WITHIN a CommentType. The attachment form however defaults `index`
|
|
241
|
+
* to 1 for every new attachment, so a multi-image POI very often holds several images
|
|
242
|
+
* all sharing index 1 — or null. Sorting those is left to DynamoDB's arbitrary scan
|
|
243
|
+
* order, which is why the copy came out in a different order than the original.
|
|
244
|
+
*
|
|
245
|
+
* Strategy: sort deterministically (index, then creation date, then id — i.e. the
|
|
246
|
+
* upload order the user actually saw), and only rewrite the indices of a family when
|
|
247
|
+
* they are ambiguous (null or duplicated). A family whose indices are already unique
|
|
248
|
+
* is copied untouched, so a deliberate ordering is never overwritten.
|
|
67
249
|
*/
|
|
68
|
-
private
|
|
250
|
+
private orderCommentsForCopy;
|
|
251
|
+
/** Sort key for a comment's position; a missing index sorts last, not first. */
|
|
252
|
+
private orderingIndexOf;
|
|
253
|
+
/** Creation timestamp of a comment, used as the tie-breaker: it reflects the order in
|
|
254
|
+
* which the user uploaded the media, which is the order they saw. */
|
|
255
|
+
private creationTimeOf;
|
|
256
|
+
/**
|
|
257
|
+
* Copies ONE media item (image, video, audio, document, text, link, embed, iframe...)
|
|
258
|
+
* of a feature, duplicating its file in S3 first when it has one.
|
|
259
|
+
*
|
|
260
|
+
* Every column of the Comment model is carried over. `description` and `metadata` were
|
|
261
|
+
* previously dropped, which emptied the captions of images/videos, the body of TEXT
|
|
262
|
+
* items — where the whole content lives, since a TEXT comment has no file — and any
|
|
263
|
+
* per-media settings stored in metadata.
|
|
264
|
+
*/
|
|
265
|
+
private copyCommentForFeature;
|
|
266
|
+
/** Same, for an attachment carried by a ticket rather than by a feature. */
|
|
267
|
+
private copyCommentForTicket;
|
|
268
|
+
/** Shared body of the two above: `owner` carries whichever foreign key links the copy back
|
|
269
|
+
* to its new parent, everything else is identical. */
|
|
270
|
+
private copyComment;
|
|
271
|
+
/**
|
|
272
|
+
* Creates the target-space copy of a source POI, with EVERY column of the POI model.
|
|
273
|
+
*
|
|
274
|
+
* Single entry point for all phases: the element phases (ticket, equipment, 3D object,
|
|
275
|
+
* feature, measurement) used to build their POI inline with only 5 fields — coordinate,
|
|
276
|
+
* type, elementID, metadata, sweep — which is precisely how museum POIs lost their
|
|
277
|
+
* pastille: `tagDescription` (the popup text), `tagIcon` (the custom icon) and
|
|
278
|
+
* `tagImageID` (the pastille's image) never left the source space.
|
|
279
|
+
*/
|
|
280
|
+
private createCopiedPoi;
|
|
281
|
+
/**
|
|
282
|
+
* Adapts a POI's `metadata` (see MattertagData.getMetadataString(): { icon, normal, sid })
|
|
283
|
+
* to the target space.
|
|
284
|
+
*
|
|
285
|
+
* - `sid` is the id of the Mattertag inside the SOURCE Matterport model. Carried over,
|
|
286
|
+
* the viewer tries to re-create the target's tag with an id belonging to another
|
|
287
|
+
* model ("already in use"), so the pastille fails to attach. It is dropped: the
|
|
288
|
+
* viewer then allocates a fresh sid on first render.
|
|
289
|
+
* - `normal` is the stem direction. It is a direction, so only the rotation applies
|
|
290
|
+
* (never the translation); without it, a copied pastille points into the wall
|
|
291
|
+
* whenever the two models are not aligned on the same axis.
|
|
292
|
+
* - `icon` and anything else is kept as-is.
|
|
293
|
+
*/
|
|
294
|
+
private getAdjustedPoiMetadata;
|
|
295
|
+
/**
|
|
296
|
+
* Resolves a POI's `tagImageID` — which the schema documents as a "connection to
|
|
297
|
+
* comment.id", i.e. the id of the Comment holding the image shown in the pastille.
|
|
298
|
+
*
|
|
299
|
+
* It is NOT an S3 key, so the previous "does it contain a slash?" heuristic never
|
|
300
|
+
* matched and the value travelled unchanged, leaving every copied pastille pointing at
|
|
301
|
+
* a Comment of the source space. It is now translated through the comment id map built
|
|
302
|
+
* while duplicating the media, and dropped when it cannot be resolved (the pastille
|
|
303
|
+
* then falls back to the feature's cover image instead of a broken reference).
|
|
304
|
+
*/
|
|
305
|
+
private resolveTagImageID;
|
|
306
|
+
/**
|
|
307
|
+
* Heuristic duplicate-detection: compares several identifying fields together rather than
|
|
308
|
+
* a single label, to reduce false positives when items share one of them. Returns the
|
|
309
|
+
* matched target item (or undefined) rather than a boolean, so the caller can act on the
|
|
310
|
+
* existing record — heal its missing fields — instead of only skipping it.
|
|
311
|
+
*
|
|
312
|
+
* The fields are supplied by the caller because the models do not agree on them. The
|
|
313
|
+
* previous version hard-coded `title`/`description`, which do not exist on Equipment at
|
|
314
|
+
* all: both sides read `undefined`, every equipment therefore "matched" every other one,
|
|
315
|
+
* and a target space holding a single equipment silently caused the whole equipment phase
|
|
316
|
+
* to copy nothing.
|
|
317
|
+
*/
|
|
318
|
+
private findExisting;
|
|
319
|
+
/**
|
|
320
|
+
* Builds a patch of whichever fields in `fields` the existing target record is missing
|
|
321
|
+
* (`null`/`undefined`/empty array) but the source has, plus `domainID` when requested —
|
|
322
|
+
* domains are organisation-scoped everywhere in this file, so that one is only ever
|
|
323
|
+
* healed across the SAME organisation, exactly like at creation time.
|
|
324
|
+
*
|
|
325
|
+
* Never overwrites a field the target already carries a value for: a value entered by
|
|
326
|
+
* hand in the target space since an earlier, incomplete copy must not be clobbered by
|
|
327
|
+
* an older or different source value.
|
|
328
|
+
*
|
|
329
|
+
* Returns both the patch to send and the list of healed field names, so the caller can
|
|
330
|
+
* both perform the update and describe it in the copy log.
|
|
331
|
+
*/
|
|
332
|
+
private buildHealPatch;
|
|
333
|
+
/**
|
|
334
|
+
* Fills in whichever identity field a PRE-EXISTING target equipment is missing but the
|
|
335
|
+
* source has — see the call site in the EQUIPMENTS phase for why re-running the copy
|
|
336
|
+
* has to be able to repair a previously-copied equipment, not just skip it again.
|
|
337
|
+
*
|
|
338
|
+
* A failed update is left to bubble up to the caller's existing per-item try/catch, the
|
|
339
|
+
* same as a failed creation — it gets the same visible error line, rather than a silent
|
|
340
|
+
* console warning.
|
|
341
|
+
*
|
|
342
|
+
* Returns the list of fields that were actually healed (empty if nothing was missing).
|
|
343
|
+
*/
|
|
344
|
+
private healEquipmentIdentity;
|
|
345
|
+
/**
|
|
346
|
+
* Same idea as healEquipmentIdentity(), for a pre-existing ticket. `title`/`description`
|
|
347
|
+
* are the pair findExisting() matched on to find this ticket in the first place, so they
|
|
348
|
+
* are never part of the heal — by construction, they already agree with the source.
|
|
349
|
+
*/
|
|
350
|
+
private healTicketIdentity;
|
|
351
|
+
/** Same idea, for a pre-existing feature. Keywords, media and POIs are not healed here
|
|
352
|
+
* for the same reason notes/POIs are not healed on equipment — see healEquipmentIdentity(). */
|
|
353
|
+
private healFeatureIdentity;
|
|
354
|
+
/** Serialises the identifying fields unambiguously, so that ["a", "b"] and ["ab", ""]
|
|
355
|
+
* can never produce the same key. */
|
|
356
|
+
private identityKey;
|
|
69
357
|
/**
|
|
70
358
|
* Fetches the position of every sweep in a space and returns it as a Map for O(1) lookup.
|
|
71
359
|
* Uses allSettled so a single failing sweep lookup doesn't abort the whole map —
|
|
@@ -90,6 +378,8 @@ export declare class MatterportTagCopyService {
|
|
|
90
378
|
/**
|
|
91
379
|
* Builds a new S3 key for a copied attachment and sanitizes the filename
|
|
92
380
|
* to prevent AWS Amplify SignatureDoesNotMatch errors.
|
|
381
|
+
* `folderOverride` re-homes the copy into another prefix — used for tag icons, which
|
|
382
|
+
* are stored per space (`icons-tags/<spaceId>/`) and must follow the target space.
|
|
93
383
|
*/
|
|
94
384
|
private buildCopiedAttachmentKey;
|
|
95
385
|
/**
|
|
@@ -100,20 +390,31 @@ export declare class MatterportTagCopyService {
|
|
|
100
390
|
* - Any other kind of failure (permissions, network, etc.) still throws a generic Error.
|
|
101
391
|
*/
|
|
102
392
|
private copyS3KeyIfPresent;
|
|
393
|
+
/** True when the failure means the source object simply is not on S3 (orphaned reference). */
|
|
394
|
+
private isMissingS3File;
|
|
395
|
+
/** True when AppSync refused the operation for lack of permission, as opposed to failing it.
|
|
396
|
+
* Nothing can be retried or worked around client-side: the account simply is not allowed
|
|
397
|
+
* to write that model, which is a backend authorisation rule (@auth) to change. */
|
|
398
|
+
private isUnauthorized;
|
|
399
|
+
/** True for the SigV4 mismatch Storage.copy() raises on keys with special characters. */
|
|
400
|
+
private isSignatureMismatch;
|
|
103
401
|
/**
|
|
104
402
|
* Duplicates the file referenced by a POI's tagIcon (a JSON string shaped like
|
|
105
|
-
* { src, opacity }, per form-feature.component.ts).
|
|
106
|
-
*
|
|
403
|
+
* { src, opacity }, per form-feature.component.ts).
|
|
404
|
+
*
|
|
405
|
+
* Two behaviours matter here:
|
|
406
|
+
* - the copy is re-homed under the TARGET space's icon folder
|
|
407
|
+
* (`icons-tags/<targetSpaceId>/`, the layout form-feature.component.ts uploads to).
|
|
408
|
+
* Keeping the source folder produced a target space whose icons all lived under
|
|
409
|
+
* another space's prefix, so cleaning up the source space broke the copy's icons.
|
|
410
|
+
* - a missing icon file is NOT fatal. It used to bubble up as a MissingS3FileError and
|
|
411
|
+
* abort the whole POI — losing the pastille, its text and its media because of a
|
|
412
|
+
* decorative file. The POI is now created with the icon reference dropped, which
|
|
413
|
+
* renders the module's default icon, and a log line reports it.
|
|
107
414
|
*/
|
|
108
415
|
private duplicateTagIcon;
|
|
109
|
-
/**
|
|
110
|
-
|
|
111
|
-
* separator, whether raw key or pre-signed URL). UNCONFIRMED ASSUMPTION: this
|
|
112
|
-
* field's exact semantics weren't verified — if it's actually an opaque reference
|
|
113
|
-
* to another DB record rather than a file, this heuristic may incorrectly attempt
|
|
114
|
-
* a copy. Confirm the real structure to tighten this if it causes issues.
|
|
115
|
-
*/
|
|
116
|
-
private duplicateTagImageID;
|
|
416
|
+
/** Icon folder of the space being copied into, matching what the feature form uploads to. */
|
|
417
|
+
private buildTargetIconFolder;
|
|
117
418
|
/**
|
|
118
419
|
* Extracts and normalizes the raw S3 key from a stored value (which may be a full URL
|
|
119
420
|
* or already a raw key).
|
|
@@ -15,6 +15,22 @@ export declare class CommentService {
|
|
|
15
15
|
getComment(commentID: string): Promise<Comment>;
|
|
16
16
|
deleteComment(commentID: string): Promise<Comment>;
|
|
17
17
|
createComment(comment: Comment): Promise<Comment>;
|
|
18
|
+
/**
|
|
19
|
+
* Returns EVERY comment attached to a feature, following `nextToken` until the
|
|
20
|
+
* connection is exhausted.
|
|
21
|
+
*
|
|
22
|
+
* The `comments` connection nested inside FeaturesBySpace/GetFeature is capped by
|
|
23
|
+
* the AppSync resolver (`$util.defaultIfNull($context.args.limit, 100)`) and exposes
|
|
24
|
+
* a `nextToken` that nobody follows, so a media-rich POI silently loses every
|
|
25
|
+
* attachment past the first page. Anything that must see the complete media set of
|
|
26
|
+
* a feature (duplication, export, audit) has to use this method rather than
|
|
27
|
+
* `feature.comments.items`.
|
|
28
|
+
*/
|
|
29
|
+
getAllCommentsForFeature(featureID: string): Promise<Comment[]>;
|
|
30
|
+
/** Same as getAllCommentsForFeature(), for the attachments of a ticket. */
|
|
31
|
+
getAllCommentsForTicket(ticketID: string): Promise<Comment[]>;
|
|
32
|
+
/** Drains a paginated comment query until its nextToken is exhausted. */
|
|
33
|
+
private getAllCommentsPaginated;
|
|
18
34
|
updateComment(input: any): Promise<Comment>;
|
|
19
35
|
uploadFileForComment(file: File, id?: string): Promise<string>;
|
|
20
36
|
/** Create a new comment with an existing Annexe */
|
|
@@ -4,6 +4,14 @@ export declare class DomainService {
|
|
|
4
4
|
API: any;
|
|
5
5
|
constructor(apiInjected: any);
|
|
6
6
|
getGenericDomainsAndForOrganisation(organisationID: string): Promise<any | void>;
|
|
7
|
+
/**
|
|
8
|
+
* Same as getGenericDomainsAndForOrganisation(), but drains every page instead of only
|
|
9
|
+
* the first. `ListDomains` has no per-organisation index — every call already scans the
|
|
10
|
+
* WHOLE table across every organisation before filtering client-side — so a catalogue
|
|
11
|
+
* past the first page was silently invisible to callers that need the complete list
|
|
12
|
+
* (e.g. matching an organisation-specific domain by name across a space-to-space copy).
|
|
13
|
+
*/
|
|
14
|
+
getAllGenericDomainsAndForOrganisation(organisationID: string): Promise<Domain[]>;
|
|
7
15
|
getGenericDomainsAndForOrgAndOrderedOrg(orgID: string, orderedOrgID?: string): Promise<Domain[]>;
|
|
8
16
|
getDomainsForOrgExcludingTypes(types: DomainType[], organisationID: string): Promise<Domain[]>;
|
|
9
17
|
getDomain(id: string): Promise<Domain>;
|
|
@@ -70,7 +70,10 @@ export declare class EquipmentService extends BaseObjectService {
|
|
|
70
70
|
* S3 object itself has already been physically copied by the caller beforehand,
|
|
71
71
|
* so this only needs to record the Note pointing at that new key.
|
|
72
72
|
*/
|
|
73
|
-
createNoteWithExistingAttachment(equipmentID: string, attachmentKey: string, type: TypeNote, ownerMissionID: string | null
|
|
73
|
+
createNoteWithExistingAttachment(equipmentID: string, attachmentKey: string, type: TypeNote, ownerMissionID: string | null, dates?: {
|
|
74
|
+
signDate?: number | null;
|
|
75
|
+
expirationDate?: number | null;
|
|
76
|
+
}): Promise<Note>;
|
|
74
77
|
uploadImage(equipment: Equipment, imageFile: File): Promise<Note>;
|
|
75
78
|
updateEquipment(equip: any, file?: File, noteType?: TypeNote): Promise<Equipment>;
|
|
76
79
|
getPrefixDoc(type: TypeNote): any;
|
|
@@ -48,6 +48,11 @@ export declare class FeatureService extends BaseObjectService {
|
|
|
48
48
|
initFeatures(): Promise<void>;
|
|
49
49
|
getFeaturesForSpace(spaceID: string, keepArchived?: boolean): Promise<Feature[]>;
|
|
50
50
|
getFeaturesBySpaceWithoutFiltering(spaceID: string): Promise<Feature[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Returns EVERY feature of a space, following `nextToken` until the index is
|
|
53
|
+
* exhausted, with no user/archive filtering at all.
|
|
54
|
+
*/
|
|
55
|
+
getAllFeaturesBySpaceRaw(spaceID: string): Promise<Feature[]>;
|
|
51
56
|
isBookingPlace(feature: Feature): void;
|
|
52
57
|
setCoverImage(feature: Feature): void;
|
|
53
58
|
updateFeatureLocation(feature: Feature, tagData: MattertagData, spaceID: string): Promise<POI>;
|
|
@@ -6,6 +6,12 @@ export declare class HashtagService {
|
|
|
6
6
|
API: any;
|
|
7
7
|
constructor(apiInjected: any);
|
|
8
8
|
getHashtag(hashtagID: string): Promise<Hashtag>;
|
|
9
|
+
/** Reads a space's keywords without touching the shared cache. */
|
|
10
|
+
listHashtagsForSpace(spaceID: string): Promise<Hashtag[]>;
|
|
11
|
+
/** Creates a keyword in a space without adding it to the shared cache. */
|
|
12
|
+
createHashtagForSpace(name: string, spaceID: string): Promise<Hashtag>;
|
|
13
|
+
/** Deletes a keyword without touching the shared cache. */
|
|
14
|
+
deleteHashtagById(hashtagID: string): Promise<Hashtag>;
|
|
9
15
|
createHashtag(hashtag: Hashtag): Promise<Hashtag>;
|
|
10
16
|
getHashtagsForSpace(spaceID?: string): Promise<Hashtag[]>;
|
|
11
17
|
static ɵfac: i0.ɵɵFactoryDeclaration<HashtagService, never>;
|
|
@@ -21,6 +21,11 @@ export declare class MissionService {
|
|
|
21
21
|
getActivatedMissions(missions: Mission[]): Mission[];
|
|
22
22
|
getMissionsForZone(zoneID: string): Promise<Mission[]>;
|
|
23
23
|
getMissionsForSpace(spaceID: string): Promise<Mission[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Same as getMissionsForSpace(), but follows `nextToken` until the connection is
|
|
26
|
+
* exhausted instead of returning only the first page.
|
|
27
|
+
*/
|
|
28
|
+
getAllMissionsForSpace(spaceID: string): Promise<Mission[]>;
|
|
24
29
|
activateMission(mis: Mission): void;
|
|
25
30
|
getMissionsByTemplate(templateID: string): Promise<Mission[]>;
|
|
26
31
|
getMission(missionID: string): Promise<Mission>;
|
|
@@ -20,3 +20,17 @@ export declare const uploadJsonToS3: (path: string, json: string, filename: stri
|
|
|
20
20
|
* to distinguish an orphaned DB reference from a genuine copy failure.
|
|
21
21
|
*/
|
|
22
22
|
export declare const copyFileInS3: (sourceKey: string, destKey: string) => Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Fallback duplication: downloads the object and re-uploads it under the new key.
|
|
25
|
+
*
|
|
26
|
+
* Storage.copy() builds the S3 `x-amz-copy-source` header by plain string concatenation
|
|
27
|
+
* (see @aws-amplify/storage AWSS3Provider.copy: `${bucket}/${srcPrefix}${srcKey}`), with no
|
|
28
|
+
* URL-encoding of the key. A key holding a space, an accent or a parenthesis therefore
|
|
29
|
+
* yields a header that does not match the signed canonical request, and S3 answers
|
|
30
|
+
* "The request signature we calculated does not match the signature you provided".
|
|
31
|
+
*
|
|
32
|
+
* Round-tripping the bytes avoids that header entirely and works for any key, at the cost
|
|
33
|
+
* of transferring the file through the browser — hence its use as a fallback only.
|
|
34
|
+
* IMPORTANT: like copyFileInS3, this must NOT swallow errors.
|
|
35
|
+
*/
|
|
36
|
+
export declare const copyFileInS3ViaDownload: (sourceKey: string, destKey: string) => Promise<string>;
|
|
@@ -15,7 +15,22 @@ export interface RigidTransform {
|
|
|
15
15
|
export declare function computeRigidTransform(sourceA: Vec3, targetA: Vec3, sourceB: Vec3, targetB: Vec3): RigidTransform;
|
|
16
16
|
/** Applies the rigid transform to a 3D position (x, y, z). */
|
|
17
17
|
export declare function applyRigidTransformToPoint(p: Vec3, t: RigidTransform): Vec3;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Applies the Y rotation (in degrees) to an orientation angle (a pose's yRotation).
|
|
20
|
+
* A null/undefined/non-numeric angle is returned untouched: "no orientation stored"
|
|
21
|
+
* must stay "no orientation stored", not become the raw rotation delta.
|
|
22
|
+
*/
|
|
19
23
|
export declare function applyRigidTransformToYRotationDeg(yRotationDeg: number, t: RigidTransform): number;
|
|
20
24
|
/** Euclidean distance between two points, useful to validate landmark quality. */
|
|
21
25
|
export declare function distance(a: Vec3, b: Vec3): number;
|
|
26
|
+
/**
|
|
27
|
+
* Applies ONLY the rotation part of the transform to a direction vector
|
|
28
|
+
* (a normal, a stem vector, ...).
|
|
29
|
+
*
|
|
30
|
+
* A direction has no origin, so the translation must NOT be applied to it —
|
|
31
|
+
* doing so would turn a unit normal into a point somewhere in the target model.
|
|
32
|
+
* Used for a POI's `metadata.normal`, which drives the orientation of the tag's
|
|
33
|
+
* stem: without this rotation, a pastille copied into a space whose model is
|
|
34
|
+
* rotated relative to the source ends up pointing into the wall it is stuck on.
|
|
35
|
+
*/
|
|
36
|
+
export declare function applyRigidTransformToDirection(v: Vec3, t: RigidTransform): Vec3;
|