@cloudparse/up-miniapps-sdk 0.7.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -74,10 +74,6 @@ interface IUserProfile {
74
74
  registrationType: "up" | "apple" | "google";
75
75
  /** Admin status. */
76
76
  admin: boolean;
77
- /** UP points balance. */
78
- ups: number;
79
- /** Experience points balance. */
80
- xp: number;
81
77
  /** ISO timestamp of user creation. */
82
78
  createdAt: string;
83
79
  /** ISO timestamp of last password update. */
@@ -218,9 +214,10 @@ interface IFidelityTransaction {
218
214
  * A player's progress towards a single named Achievement. The Hub tracks a
219
215
  * raw in-app action count per Achievement `slug` (reported server-to-server
220
216
  * by each mini-app's own backend — never by this SDK, see `sdk.achievement`/
221
- * `sdk.achievements` doc comments in `sdk.ts`) and auto-credits UP+XP once
222
- * `playerActionCount` reaches `totalActionCount`. This is a read-only view
223
- * for rendering progress (e.g. a progress bar) — there is no "claim" step.
217
+ * `sdk.achievements` doc comments in `sdk.ts`) and auto-credits points
218
+ * (FPoints) once `playerActionCount` reaches `totalActionCount`. This is a
219
+ * read-only view for rendering progress (e.g. a progress bar) — there is no
220
+ * "claim" step.
224
221
  */
225
222
  interface IAchievement {
226
223
  /** Unique slug identifying the Achievement (e.g. "explore-5-pois"). */
@@ -233,10 +230,8 @@ interface IAchievement {
233
230
  playerActionCount: number;
234
231
  /** The action count required to complete this Achievement. */
235
232
  totalActionCount: number;
236
- /** UP points credited automatically once completed. */
237
- upReward: number;
238
- /** XP credited automatically once completed. */
239
- xpReward: number;
233
+ /** Points (FPoints) credited automatically once completed. */
234
+ pointsReward: number;
240
235
  /** Whether `playerActionCount` has reached `totalActionCount`. */
241
236
  completed: boolean;
242
237
  /** Optional illustration/icon URL. */
@@ -793,6 +788,19 @@ interface MapProps {
793
788
  onPoiClick?: (poi: POI) => void;
794
789
  /** Route geometry to display on the map. */
795
790
  route?: GeoJSON.LineString;
791
+ /**
792
+ * Visual style for the route line the Map draws internally (both for
793
+ * `route` and for an active `navigation`/`navigationData` journey) —
794
+ * see `RouteLayerProps`. Defaults to `RouteLayer`'s own defaults
795
+ * (`#3b82f6` blue, solid) when omitted, so existing consumers are
796
+ * unaffected.
797
+ */
798
+ routeStyle?: {
799
+ color?: string;
800
+ width?: number;
801
+ opacity?: number;
802
+ dashArray?: number[];
803
+ };
796
804
  /** Whether to show the out-of-the-box navigation UI. */
797
805
  showNavigationUI?: boolean;
798
806
  /** Callback triggered when the user aborts/closes the navigation UI. */
@@ -819,6 +827,12 @@ interface MapProps {
819
827
  * Defaults to 'preview'.
820
828
  */
821
829
  mode?: "preview" | "navigation";
830
+ /**
831
+ * Distance in meters from the destination at which the journey
832
+ * counts as arrived. GPS accuracy means the live remaining distance
833
+ * rarely reaches exactly 0. Defaults to 15m.
834
+ */
835
+ arrivalThresholdMeters?: number;
822
836
  };
823
837
  /** Callback triggered when the navigation mode changes (e.g. from preview to navigation). */
824
838
  onNavigationModeChange?: (mode: "preview" | "navigation") => void;
@@ -837,6 +851,8 @@ interface MapProps {
837
851
  mode?: "preview" | "navigation";
838
852
  /** Function to transition to active navigation mode */
839
853
  start?: () => void;
854
+ /** True once the live remaining distance drops to/below `navigation.arrivalThresholdMeters`. */
855
+ hasArrived?: boolean;
840
856
  };
841
857
  /** Whether to show the vertical drawer and floating right controls. Defaults to true. */
842
858
  showControls?: boolean;
@@ -879,6 +895,8 @@ interface EtaData {
879
895
  onStart?: () => void;
880
896
  isCalculating?: boolean;
881
897
  error?: string | null;
898
+ /** True once the live remaining distance drops to/below `navigation.arrivalThresholdMeters`. */
899
+ hasArrived?: boolean;
882
900
  }
883
901
 
884
902
  interface MapInsets {
@@ -1035,6 +1053,12 @@ interface LiveRoutingOptions {
1035
1053
  enabled?: boolean;
1036
1054
  /** Initial mode: 'preview' or 'navigation'. Defaults to 'preview'. */
1037
1055
  initialMode?: "preview" | "navigation";
1056
+ /**
1057
+ * Distance in meters from the destination at which the journey counts
1058
+ * as arrived (`hasArrived` becomes true). GPS accuracy means the live
1059
+ * `currentDistanceMeters` rarely reaches exactly 0. Defaults to 15m.
1060
+ */
1061
+ arrivalThresholdMeters?: number;
1038
1062
  }
1039
1063
  interface LiveRoutingResult {
1040
1064
  /** The calculated route data from the last API call */
@@ -1053,8 +1077,15 @@ interface LiveRoutingResult {
1053
1077
  mode: "preview" | "navigation";
1054
1078
  /** Start active navigation (switch mode to 'navigation') */
1055
1079
  start: () => void;
1080
+ /**
1081
+ * True once `currentDistanceMeters` drops to/below
1082
+ * `arrivalThresholdMeters`. Use this instead of checking
1083
+ * `currentDistanceMeters <= 0` — the point never reaches exactly 0 with
1084
+ * live GPS.
1085
+ */
1086
+ hasArrived: boolean;
1056
1087
  }
1057
- declare const useLiveRouting: ({ origin: manualOrigin, destination, profile, recalculateThreshold, enabled, initialMode, }: LiveRoutingOptions) => LiveRoutingResult;
1088
+ declare const useLiveRouting: ({ origin: manualOrigin, destination, profile, recalculateThreshold, enabled, initialMode, arrivalThresholdMeters, }: LiveRoutingOptions) => LiveRoutingResult;
1058
1089
  /**
1059
1090
  * A simple hook to fetch a route once between two points.
1060
1091
  * Does not automatically recalculate or track user location.
@@ -1082,6 +1113,12 @@ interface DeclarativeJourneyOptions {
1082
1113
  origin?: [number, number] | null;
1083
1114
  /** Initial mode. 'preview' shows the route overview, 'navigation' starts active guidance. Defaults to 'preview'. */
1084
1115
  mode?: NavMode;
1116
+ /**
1117
+ * Distance in meters from the destination at which the journey counts
1118
+ * as arrived (`hasArrived` becomes true). GPS accuracy means the live
1119
+ * `distanceRemainingMeters` rarely reaches exactly 0. Defaults to 15m.
1120
+ */
1121
+ arrivalThresholdMeters?: number;
1085
1122
  }
1086
1123
  interface UseJourneyOptions {
1087
1124
  /**
@@ -1117,6 +1154,13 @@ interface UseJourneyResult {
1117
1154
  etaMinutes: number;
1118
1155
  /** Journey completion progress, 0-100. */
1119
1156
  progress: number;
1157
+ /**
1158
+ * True once the live remaining distance drops to/below
1159
+ * `arrivalThresholdMeters`. Check this instead of
1160
+ * `distanceRemainingMeters <= 0` — it usually never reaches exactly 0
1161
+ * with live GPS, so a UI that waits for that may never show "arrived".
1162
+ */
1163
+ hasArrived: boolean;
1120
1164
  /** Switch from 'preview' to active 'navigation' guidance. */
1121
1165
  start: () => void;
1122
1166
  /** Cancel the journey entirely. Always returns to 'preview' the next time a journey starts. */
@@ -1158,6 +1202,14 @@ interface RouteLayerProps {
1158
1202
  width?: number;
1159
1203
  /** Opacity of the route line. Defaults to 0.8. */
1160
1204
  opacity?: number;
1205
+ /**
1206
+ * MapLibre `line-dasharray` for the colored line (the white casing
1207
+ * underneath always stays solid, for legibility against any basemap).
1208
+ * Units are multiples of `width`. E.g. `[0, 2]` with the layer's own
1209
+ * `line-cap: round` (always on) renders as a dotted trail instead of a
1210
+ * continuous line. Omit for a solid line (default).
1211
+ */
1212
+ dashArray?: number[];
1161
1213
  }
1162
1214
  declare const RouteLayer: React__default.FC<RouteLayerProps>;
1163
1215
 
@@ -1518,6 +1570,14 @@ declare class MiniAppSDK {
1518
1570
  destination: [number, number];
1519
1571
  origin?: [number, number];
1520
1572
  profile?: "driving" | "cycling" | "walking";
1573
+ /**
1574
+ * Distance in meters from the destination at which the journey
1575
+ * counts as arrived (`useJourney().hasArrived` becomes true). GPS
1576
+ * accuracy means the live remaining distance rarely reaches
1577
+ * exactly 0 — this is the threshold a consumer's own UI should
1578
+ * treat as "done" instead of waiting for that. Defaults to 15m.
1579
+ */
1580
+ arrivalThresholdMeters?: number;
1521
1581
  }) => void;
1522
1582
  /**
1523
1583
  * Starts the active navigation (active guidance).
package/dist/index.js CHANGED
@@ -81,8 +81,7 @@ var MOCK_ACHIEVEMENTS = [
81
81
  slug: "first-journey",
82
82
  title: "First Steps",
83
83
  totalActionCount: 1,
84
- upReward: 20,
85
- xpReward: 40
84
+ pointsReward: 10
86
85
  },
87
86
  {
88
87
  completed: false,
@@ -92,8 +91,7 @@ var MOCK_ACHIEVEMENTS = [
92
91
  slug: "explore-5-pois",
93
92
  title: "Explorer",
94
93
  totalActionCount: 5,
95
- upReward: 50,
96
- xpReward: 100
94
+ pointsReward: 15
97
95
  },
98
96
  {
99
97
  completed: false,
@@ -103,8 +101,7 @@ var MOCK_ACHIEVEMENTS = [
103
101
  slug: "scan-10-qrcodes",
104
102
  title: "Code Breaker",
105
103
  totalActionCount: 10,
106
- upReward: 80,
107
- xpReward: 150
104
+ pointsReward: 20
108
105
  }
109
106
  ];
110
107
  function mockAchievement(slug) {
@@ -118,8 +115,7 @@ function mockAchievement(slug) {
118
115
  slug: slug || "unknown-achievement",
119
116
  title: slug || "Unknown Achievement",
120
117
  totalActionCount: 1,
121
- upReward: 0,
122
- xpReward: 0
118
+ pointsReward: 0
123
119
  };
124
120
  }
125
121
  function mockAchievements() {
@@ -2014,13 +2010,15 @@ var useUserLocation = () => {
2014
2010
 
2015
2011
  // src/components/Map/hooks/useLiveRouting.ts
2016
2012
  import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef5, useState as useState6 } from "react";
2013
+ var DEFAULT_ARRIVAL_THRESHOLD_METERS = 15;
2017
2014
  var useLiveRouting = ({
2018
2015
  origin: manualOrigin,
2019
2016
  destination,
2020
2017
  profile = "driving",
2021
2018
  recalculateThreshold = 50,
2022
2019
  enabled = true,
2023
- initialMode = "preview"
2020
+ initialMode = "preview",
2021
+ arrivalThresholdMeters = DEFAULT_ARRIVAL_THRESHOLD_METERS
2024
2022
  }) => {
2025
2023
  const [route, setRoute] = useState6(null);
2026
2024
  const [totalDistance, setTotalDistance] = useState6(null);
@@ -2074,6 +2072,7 @@ var useLiveRouting = ({
2074
2072
  }
2075
2073
  return dist;
2076
2074
  }, [trackingLocation, route, destination, getDistance]);
2075
+ const hasArrived = currentDistanceMeters !== null && currentDistanceMeters <= arrivalThresholdMeters;
2077
2076
  const calculate = useCallback4(
2078
2077
  async (origin, dest, isNewDest) => {
2079
2078
  if (isRequestInProgress.current) return;
@@ -2131,6 +2130,8 @@ var useLiveRouting = ({
2131
2130
  if (lastResetDestKey.current !== destKey) {
2132
2131
  lastResetDestKey.current = destKey;
2133
2132
  setMode(initialMode);
2133
+ setRoute(null);
2134
+ setTotalDistance(null);
2134
2135
  }
2135
2136
  if (destChanged) {
2136
2137
  calculate(calculationOrigin, destination, true);
@@ -2156,6 +2157,7 @@ var useLiveRouting = ({
2156
2157
  return {
2157
2158
  currentDistanceMeters,
2158
2159
  error,
2160
+ hasArrived,
2159
2161
  isCalculating,
2160
2162
  mode,
2161
2163
  recalculate: () => calculationOrigin && destination && calculate(calculationOrigin, destination, false),
@@ -2221,6 +2223,7 @@ var useJourney = (options = {}) => {
2221
2223
  }, []);
2222
2224
  const effectiveOptions = progNav || navigation;
2223
2225
  const internalNav = useLiveRouting({
2226
+ arrivalThresholdMeters: effectiveOptions?.arrivalThresholdMeters,
2224
2227
  destination: effectiveOptions?.destination || null,
2225
2228
  enabled: !isAborted && !!effectiveOptions?.destination,
2226
2229
  initialMode: effectiveOptions?.mode || "preview",
@@ -2276,6 +2279,7 @@ var useJourney = (options = {}) => {
2276
2279
  distanceRemainingMeters,
2277
2280
  error: isActive ? internalNav.error : null,
2278
2281
  etaMinutes,
2282
+ hasArrived: isActive ? internalNav.hasArrived : false,
2279
2283
  isActive,
2280
2284
  isCalculating: isActive ? internalNav.isCalculating : false,
2281
2285
  mode: isActive ? internalNav.mode : "preview",
@@ -2320,7 +2324,8 @@ var RouteLayer = ({
2320
2324
  geometry,
2321
2325
  color = "#3b82f6",
2322
2326
  width = 5,
2323
- opacity = 0.8
2327
+ opacity = 0.8,
2328
+ dashArray
2324
2329
  }) => {
2325
2330
  if (!geometry) return null;
2326
2331
  const data = {
@@ -2353,7 +2358,8 @@ var RouteLayer = ({
2353
2358
  paint: {
2354
2359
  "line-color": color,
2355
2360
  "line-opacity": opacity,
2356
- "line-width": width
2361
+ "line-width": width,
2362
+ ...dashArray ? { "line-dasharray": dashArray } : {}
2357
2363
  },
2358
2364
  type: "line"
2359
2365
  }
@@ -2787,6 +2793,7 @@ var MapInternal = ({
2787
2793
  pois = [],
2788
2794
  onPoiClick,
2789
2795
  route: externalRoute,
2796
+ routeStyle,
2790
2797
  showNavigationUI: externalShowNavigationUI,
2791
2798
  navigationData: externalNavigationData,
2792
2799
  onNavigationAbort,
@@ -2811,6 +2818,7 @@ var MapInternal = ({
2811
2818
  const hasIntegratedNav = journey.isActive;
2812
2819
  const navigationData = hasIntegratedNav ? {
2813
2820
  currentDistanceMeters: journey.distanceRemainingMeters,
2821
+ hasArrived: journey.hasArrived,
2814
2822
  mode: journey.mode,
2815
2823
  route: journey.route,
2816
2824
  start: journey.start,
@@ -2971,6 +2979,7 @@ var MapInternal = ({
2971
2979
  const showEtaData = showNavigationUI && navigationData && route ? {
2972
2980
  distanceRemainingKm: kmRemaining,
2973
2981
  etaMins,
2982
+ hasArrived: navigationData.hasArrived ?? false,
2974
2983
  label: mode === "navigation" ? "Walking Guidance Active" : "Route Preview",
2975
2984
  mode,
2976
2985
  onAbort: () => {
@@ -3145,7 +3154,23 @@ var MapInternal = ({
3145
3154
  initialViewState,
3146
3155
  interactiveLayerIds: mergedInteractiveLayerIds,
3147
3156
  mapStyle,
3148
- maxBounds,
3157
+ maxBounds: (
3158
+ // @vis.gl/react-maplibre's own <Map> component (aliased MapLibre
3159
+ // here) types maxBounds as a flat [west, south, east, north]
3160
+ // tuple — raw maplibre-gl's LngLatBoundsLike still accepts a
3161
+ // pair-of-corners shape, this narrowing is the wrapper's own.
3162
+ // This SDK's public MapProps.maxBounds keeps the
3163
+ // [[lng, lat], [lng, lat]] pair-of-corners shape (matches every
3164
+ // other coordinate prop in this SDK, e.g. flyTo), so it's
3165
+ // flattened here at the point of use rather than changing the
3166
+ // public API.
3167
+ maxBounds ? [
3168
+ maxBounds[0][0],
3169
+ maxBounds[0][1],
3170
+ maxBounds[1][0],
3171
+ maxBounds[1][1]
3172
+ ] : void 0
3173
+ ),
3149
3174
  onClick: handleMapClick,
3150
3175
  onContextMenu,
3151
3176
  onDragStart: handleUserInteraction,
@@ -3182,7 +3207,16 @@ var MapInternal = ({
3182
3207
  radiusMeters: mockLocation.radiusMeters
3183
3208
  }
3184
3209
  ),
3185
- displayRoute && /* @__PURE__ */ jsx16(RouteLayer, { geometry: displayRoute }),
3210
+ displayRoute && /* @__PURE__ */ jsx16(
3211
+ RouteLayer,
3212
+ {
3213
+ color: routeStyle?.color,
3214
+ dashArray: routeStyle?.dashArray,
3215
+ geometry: displayRoute,
3216
+ opacity: routeStyle?.opacity,
3217
+ width: routeStyle?.width
3218
+ }
3219
+ ),
3186
3220
  pois.length > 0 && /* @__PURE__ */ jsxs12(Fragment5, { children: [
3187
3221
  /* @__PURE__ */ jsx16(POILayers, { pois }),
3188
3222
  /* @__PURE__ */ jsx16(POIMarkers, { onClick: onPoiClick, pois })
@@ -3407,7 +3441,7 @@ var loadImage2 = (src) => new Promise((resolve, reject) => {
3407
3441
  });
3408
3442
 
3409
3443
  // package.json
3410
- var version = "0.7.3";
3444
+ var version = "0.9.0";
3411
3445
 
3412
3446
  // src/sdk.ts
3413
3447
  function deepFreeze(obj) {
@@ -3844,8 +3878,7 @@ var MOCK_ACHIEVEMENTS2 = [
3844
3878
  slug: "first-journey",
3845
3879
  title: "First Steps",
3846
3880
  totalActionCount: 1,
3847
- upReward: 20,
3848
- xpReward: 40
3881
+ pointsReward: 10
3849
3882
  },
3850
3883
  {
3851
3884
  completed: false,
@@ -3855,8 +3888,7 @@ var MOCK_ACHIEVEMENTS2 = [
3855
3888
  slug: "explore-5-pois",
3856
3889
  title: "Explorer",
3857
3890
  totalActionCount: 5,
3858
- upReward: 50,
3859
- xpReward: 100
3891
+ pointsReward: 15
3860
3892
  },
3861
3893
  {
3862
3894
  completed: false,
@@ -3866,8 +3898,7 @@ var MOCK_ACHIEVEMENTS2 = [
3866
3898
  slug: "scan-10-qrcodes",
3867
3899
  title: "Code Breaker",
3868
3900
  totalActionCount: 10,
3869
- upReward: 80,
3870
- xpReward: 150
3901
+ pointsReward: 20
3871
3902
  }
3872
3903
  ];
3873
3904
  var AVATAR_MOUTH_VARIANTS = [
@@ -3932,10 +3963,8 @@ function buildMockProfile(mockJWTToken) {
3932
3963
  fullName,
3933
3964
  lastName: lastNameParts.join(" ") || "Caveman",
3934
3965
  localNumber: "0600000000",
3935
- ups: 100,
3936
3966
  username,
3937
- uuid,
3938
- xp: 500
3967
+ uuid
3939
3968
  };
3940
3969
  }
3941
3970
  var getBearing = (p1, p2) => {
@@ -4240,8 +4269,7 @@ var BridgeMocker = ({
4240
4269
  slug: slug || "unknown-achievement",
4241
4270
  title: slug || "Unknown Achievement",
4242
4271
  totalActionCount: 1,
4243
- upReward: 0,
4244
- xpReward: 0
4272
+ pointsReward: 0
4245
4273
  }
4246
4274
  );
4247
4275
  }