@smarterplan/ngx-smarterplan-core 1.4.11 → 1.4.12

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.
@@ -57,6 +57,31 @@ export declare class MatterportNavigationService {
57
57
  * Disable forbidden sweeps. Resolves each sweep ID to sid before calling SDK.
58
58
  */
59
59
  removeForbiddenSweeps(sdk: any, forbiddenSweeps: string[]): Promise<void>;
60
+ /**
61
+ * Move to a position
62
+ * @param sdk
63
+ * @param position
64
+ * @param rotation
65
+ * @param options
66
+ */
67
+ move_to_position(sdk: any, position: {
68
+ x: number;
69
+ y: number;
70
+ z: number;
71
+ }, rotation: {
72
+ yaw: number;
73
+ pitch?: number;
74
+ roll?: number;
75
+ } | null, options?: {
76
+ transition?: 'FLY' | 'INSTANT' | 'IMMEDIATE';
77
+ durationMs?: number;
78
+ anglesInDegrees?: boolean;
79
+ offset?: {
80
+ x: number;
81
+ y: number;
82
+ z: number;
83
+ };
84
+ }): Promise<void>;
60
85
  /**
61
86
  * Returns the current sweep as a **uuid** (for database matching).
62
87
  * The SDK's poseCamera.sweep is a sid; we translate it.
@@ -0,0 +1,129 @@
1
+ import { BehaviorSubject } from 'rxjs';
2
+ import { TranslateService } from '@ngx-translate/core';
3
+ import { NavigationService } from './models/navigation.service';
4
+ import { PoiService } from './models/poi.service';
5
+ import { TicketsService } from './models/ticket.service';
6
+ import { EquipmentService } from './models/equipment.service';
7
+ import { Object3DService } from './models/object3D.service';
8
+ import { FeatureService } from './models/feature.service';
9
+ import { MeasurementService } from './models/measurement.service';
10
+ import { CommentService } from './models/comment.service';
11
+ import { BaseUserService } from './user.service';
12
+ import { Vec3 } from './utils/matterport-transform.util';
13
+ import * as i0 from "@angular/core";
14
+ /** Progress snapshot emitted during copyTagsFromVisit(), so the UI can show a live status. */
15
+ export interface CopyProgress {
16
+ phase: 'collecting' | 'tickets' | 'equipments' | 'pois' | 'objects3d' | 'features' | 'measurements' | 'done';
17
+ processedInPhase: number;
18
+ totalInPhase: number;
19
+ processedOverall: number;
20
+ totalOverall: number;
21
+ percent: number;
22
+ errorCount: number;
23
+ }
24
+ /** A single line in the copy log — either a step status (updated in place) or an error (appended once). */
25
+ export interface CopyLogEntry {
26
+ id: string;
27
+ type: 'step' | 'error';
28
+ text: string;
29
+ }
30
+ export declare class MatterportTagCopyService {
31
+ private navigationService;
32
+ private userService;
33
+ private poiService;
34
+ private ticketService;
35
+ private equipmentService;
36
+ private object3DService;
37
+ private featureService;
38
+ private measurementService;
39
+ private commentService;
40
+ private translate;
41
+ copyProgress$: BehaviorSubject<CopyProgress | null>;
42
+ copyLog$: BehaviorSubject<CopyLogEntry[]>;
43
+ private spModule;
44
+ private log;
45
+ private errorSeq;
46
+ constructor(navigationService: NavigationService, userService: BaseUserService, poiService: PoiService, ticketService: TicketsService, equipmentService: EquipmentService, object3DService: Object3DService, featureService: FeatureService, measurementService: MeasurementService, commentService: CommentService, translate: TranslateService);
47
+ copyTagsFromVisit(sourceSpaceId: string, targetSpaceId: string, landmarkSourceA: Vec3, landmarkTargetA: Vec3, landmarkSourceB: Vec3, landmarkTargetB: Vec3): Promise<boolean>;
48
+ /** Adds or updates a step line (identified by stepId) with new text, then republishes the log. */
49
+ private setStepLog;
50
+ /** Appends a new, permanent log line (never overwritten), then republishes the log. */
51
+ private appendErrorLog;
52
+ /** Resets the log for a new run. */
53
+ private resetLog;
54
+ /** Builds and sets a step's log line as "<translated label>: <percent>%", no step numbering. */
55
+ private setStepProgress;
56
+ /** Logs a translated, parameterized error line for a single failed item. */
57
+ private logItemError;
58
+ /** Logs an item-level error, using a specific "missing file / orphaned reference"
59
+ * translation key when the cause is a MissingS3FileError, and the generic per-type
60
+ * 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. */
62
+ private logCopyItemError;
63
+ /**
64
+ * Heuristic duplicate-detection: compares title AND description together instead of
65
+ * title alone, to reduce false positives when several source items happen to share
66
+ * the same title. Not a perfect guarantee, but meaningfully more robust.
67
+ */
68
+ private matchesExisting;
69
+ /**
70
+ * Fetches the position of every sweep in a space and returns it as a Map for O(1) lookup.
71
+ * Uses allSettled so a single failing sweep lookup doesn't abort the whole map —
72
+ * that sweep is simply absent from the resulting map (and skipped later).
73
+ */
74
+ private buildNavigationPositionMap;
75
+ /** Applies the rigid transform to a POI coordinate (position + orientation). */
76
+ private getAdjustedCoordinates;
77
+ /**
78
+ * Applies the rigid transform's translation+rotation to a plain {x,y,z} position,
79
+ * WITHOUT adding xRotation/yRotation keys. Used for measurements, whose stored
80
+ * coordinate is a bare position (no orientation), unlike other POI types.
81
+ */
82
+ private getAdjustedPositionOnly;
83
+ /**
84
+ * Finds the closest target sweep, comparing positions in the SAME coordinate frame.
85
+ * Pure in-memory lookup — no network call, thanks to the pre-built position maps.
86
+ */
87
+ private findClosestTargetSweepId;
88
+ /** Generates a short random id, used to give each copied S3 file a fresh, collision-safe key. */
89
+ private generateShortId;
90
+ /**
91
+ * Builds a new S3 key for a copied attachment and sanitizes the filename
92
+ * to prevent AWS Amplify SignatureDoesNotMatch errors.
93
+ */
94
+ private buildCopiedAttachmentKey;
95
+ /**
96
+ * Duplicates an S3 object if a key is present.
97
+ * - If the source file is genuinely missing from S3 (NoSuchKey / orphaned DB reference),
98
+ * throws a MissingS3FileError so the caller can log a clear, specific message and
99
+ * skip the whole item entirely, rather than creating a record with a broken file link.
100
+ * - Any other kind of failure (permissions, network, etc.) still throws a generic Error.
101
+ */
102
+ private copyS3KeyIfPresent;
103
+ /**
104
+ * Duplicates the file referenced by a POI's tagIcon (a JSON string shaped like
105
+ * { src, opacity }, per form-feature.component.ts). If the JSON can't be parsed or
106
+ * has no `src`, the value is returned unchanged rather than risk corrupting it.
107
+ */
108
+ private duplicateTagIcon;
109
+ /**
110
+ * Duplicates tagImageID if it looks like a reference to a file (contains a path
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;
117
+ /**
118
+ * Extracts and normalizes the raw S3 key from a stored value (which may be a full URL
119
+ * or already a raw key).
120
+ * IMPORTANT: this must return the RAW (decoded, non URL-encoded) key. Amplify's
121
+ * Storage.copy() already builds and encodes the 'x-amz-copy-source' header internally
122
+ * from the key we give it. Pre-encoding it here caused a double-encoding bug (e.g. a
123
+ * space became "%2520" instead of "%20"), so Storage.copy() looked for a key that never
124
+ * existed on S3, resulting in a NoSuchKey error.
125
+ */
126
+ private extractS3KeyFromValue;
127
+ static ɵfac: i0.ɵɵFactoryDeclaration<MatterportTagCopyService, never>;
128
+ static ɵprov: i0.ɵɵInjectableDeclaration<MatterportTagCopyService>;
129
+ }
@@ -190,6 +190,31 @@ export declare class MatterportService {
190
190
  goToLastTag(): Promise<void>;
191
191
  /** Navigates the camera to a specific Mattertag by its ID. */
192
192
  goToTag(mattertagID: string): Promise<void>;
193
+ /**
194
+ * Move the viewer camera to a specific 3D position and apply a rotation.
195
+ * Delegates the heavy lifting to MatterportNavigationService.move_to_position.
196
+ * - position: { x, y, z }
197
+ * - rotation: { yaw, pitch?, roll? } in degrees by default
198
+ * - options: optional transition and conversion settings (see navigation service)
199
+ */
200
+ goToPosition(position: {
201
+ x: number;
202
+ y: number;
203
+ z: number;
204
+ }, rotation?: {
205
+ yaw: number;
206
+ pitch?: number;
207
+ roll?: number;
208
+ } | null, options?: {
209
+ transition?: 'FLY' | 'INSTANT' | 'IMMEDIATE';
210
+ durationMs?: number;
211
+ anglesInDegrees?: boolean;
212
+ offset?: {
213
+ x: number;
214
+ y: number;
215
+ z: number;
216
+ };
217
+ }): Promise<void>;
193
218
  /**
194
219
  * Computes the context-aware opacity for a single tag at the moment it is rendered.
195
220
  * - Cursor tag: always use provided defaultOpacity.
@@ -63,6 +63,14 @@ export declare class EquipmentService extends BaseObjectService {
63
63
  nextToken: string | null;
64
64
  }>;
65
65
  uploadImageForAllEquipments(equipments: Equipment[], imageFile: File): Promise<Equipment[]>;
66
+ /**
67
+ * Creates a Note attached to an equipment from an S3 key that ALREADY exists
68
+ * (as opposed to uploadDoc/uploadImage, which upload a fresh browser File).
69
+ * Used when duplicating equipment attachments during a cross-visit copy: the
70
+ * S3 object itself has already been physically copied by the caller beforehand,
71
+ * so this only needs to record the Note pointing at that new key.
72
+ */
73
+ createNoteWithExistingAttachment(equipmentID: string, attachmentKey: string, type: TypeNote, ownerMissionID: string | null): Promise<Note>;
66
74
  uploadImage(equipment: Equipment, imageFile: File): Promise<Note>;
67
75
  updateEquipment(equip: any, file?: File, noteType?: TypeNote): Promise<Equipment>;
68
76
  getPrefixDoc(type: TypeNote): any;
@@ -19,6 +19,20 @@ export declare class PoiService {
19
19
  updatePoi(poi: {}): Promise<POI>;
20
20
  deletePoi(poiID: string): Promise<POI>;
21
21
  getPoisByMatterportSweepByType(sweep: string, type: PoiType): Promise<POI[]>;
22
+ /**
23
+ * Optimized version: fetches POIs for MANY sweeps in as few GraphQL calls as possible,
24
+ * using an `or: [{ matterportSweepID: { eq } }, ...]` filter instead of looping one
25
+ * request per sweep. This is a DynamoDB Scan with a server-side filter (not a GSI
26
+ * hash-key query), so `or` composition works and collapses N sweep-by-sweep calls
27
+ * into a handful of paginated calls total.
28
+ *
29
+ * NOTE: `in` is NOT supported on this schema's ModelStringInput (older Amplify codegen),
30
+ * hence the `or` composition instead — functionally equivalent for this use case.
31
+ *
32
+ * Chunked because DynamoDB/AppSync filter expressions have a practical size ceiling —
33
+ * splitting keeps each request well within safe limits regardless of space size.
34
+ */
35
+ getPoisByMatterportSweeps(sweeps: string[]): Promise<POI[]>;
22
36
  unsubscribe(): void;
23
37
  static ɵfac: i0.ɵɵFactoryDeclaration<PoiService, never>;
24
38
  static ɵprov: i0.ɵɵInjectableDeclaration<PoiService>;
@@ -12,3 +12,11 @@ export declare const getSignedFile: (path: any) => Promise<string | null>;
12
12
  export declare const downloadFileAsObject: (path: any) => Promise<Object | void>;
13
13
  export declare const getBufferForFileFromS3: (path: any) => Promise<ArrayBuffer>;
14
14
  export declare const uploadJsonToS3: (path: string, json: string, filename: string) => Promise<string>;
15
+ /**
16
+ * Physically duplicates an S3 object to a new key. Used when copying attachments
17
+ * between features/tickets/etc. so the copy is fully independent: deleting the
18
+ * original file later must not affect the copy.
19
+ * IMPORTANT: does NOT swallow errors — callers need the real error (e.g. NoSuchKey)
20
+ * to distinguish an orphaned DB reference from a genuine copy failure.
21
+ */
22
+ export declare const copyFileInS3: (sourceKey: string, destKey: string) => Promise<string>;
@@ -0,0 +1,21 @@
1
+ export interface Vec3 {
2
+ x: number;
3
+ y: number;
4
+ z: number;
5
+ }
6
+ export interface RigidTransform {
7
+ rotationYRad: number;
8
+ refSource: Vec3;
9
+ refTarget: Vec3;
10
+ }
11
+ /**
12
+ * Computes the rigid transform (Y-axis rotation + translation) between two
13
+ * coordinate frames, from 2 pairs of points representing the same physical spot.
14
+ */
15
+ export declare function computeRigidTransform(sourceA: Vec3, targetA: Vec3, sourceB: Vec3, targetB: Vec3): RigidTransform;
16
+ /** Applies the rigid transform to a 3D position (x, y, z). */
17
+ export declare function applyRigidTransformToPoint(p: Vec3, t: RigidTransform): Vec3;
18
+ /** Applies the Y rotation (in degrees) to an orientation angle (a pose's yRotation). */
19
+ export declare function applyRigidTransformToYRotationDeg(yRotationDeg: number, t: RigidTransform): number;
20
+ /** Euclidean distance between two points, useful to validate landmark quality. */
21
+ export declare function distance(a: Vec3, b: Vec3): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smarterplan/ngx-smarterplan-core",
3
- "version": "1.4.11",
3
+ "version": "1.4.12",
4
4
  "dependencies": {
5
5
  "tslib": "^2.5.0"
6
6
  },
package/public-api.d.ts CHANGED
@@ -18,7 +18,9 @@ export * from './lib/services/filter.service';
18
18
  export * from './lib/services/search.service';
19
19
  export * from './lib/services/zone-drawer.service';
20
20
  export * from './lib/services/matterport-import.service';
21
+ export * from './lib/services/matterport-tag-copy.service';
21
22
  export * from './lib/mattertagData';
23
+ export * from './lib/services/utils/matterport-transform.util';
22
24
  export * from './lib/services/models/profile.service';
23
25
  export * from './lib/services/models/zone.service';
24
26
  export * from './lib/services/models/mission.service';