@myrjfa/state 3.0.10 → 3.0.11

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.
@@ -1,5 +1,6 @@
1
1
  "use server";
2
2
  import { del, get, post, put } from "./fetcher";
3
+ import { companionEligibilityFailure } from "../companion/eligibility";
3
4
  import { uploadCompanionTripCover as uploadTripCover } from "./companionUpload";
4
5
  const BASE_PATH = "/companion";
5
6
  export async function getCompanionEligibility() {
@@ -18,8 +19,8 @@ export async function getCompanionSavedStatus(type, targetId) {
18
19
  const data = await get(`${BASE_PATH}/saved/${type}/${encodeURIComponent(targetId)}`);
19
20
  return { success: true, saved: data.data.saved };
20
21
  }
21
- catch {
22
- return { success: false, error: "Could not load saved status. Please retry." };
22
+ catch (error) {
23
+ return { success: false, ...companionEligibilityFailure(error), error: "Could not load saved status. Please retry." };
23
24
  }
24
25
  }
25
26
  export async function setCompanionSavedItem(type, targetId, saved) {
@@ -27,8 +28,8 @@ export async function setCompanionSavedItem(type, targetId, saved) {
27
28
  const data = await put(`${BASE_PATH}/saved/${type}/${encodeURIComponent(targetId)}`, { saved });
28
29
  return { success: true, saved: data.data.saved };
29
30
  }
30
- catch {
31
- return { success: false, error: "Could not update your saved items. Please retry." };
31
+ catch (error) {
32
+ return { success: false, ...companionEligibilityFailure(error), error: "Could not update your saved items. Please retry." };
32
33
  }
33
34
  }
34
35
  export async function getCompanionSavedItems(type, cursor) {
@@ -37,8 +38,8 @@ export async function getCompanionSavedItems(type, cursor) {
37
38
  const data = await get(`${BASE_PATH}/saved?${query}`);
38
39
  return { success: true, ...data.data };
39
40
  }
40
- catch {
41
- return { success: false, items: [], nextCursor: null, error: "Could not load your saved items. Please retry." };
41
+ catch (error) {
42
+ return { success: false, ...companionEligibilityFailure(error), items: [], nextCursor: null, error: "Could not load your saved items. Please retry." };
42
43
  }
43
44
  }
44
45
  // ── Travel Profile ──────────────────────────────────────────────────────────────
@@ -51,7 +52,7 @@ export async function getMyTravelProfile() {
51
52
  }
52
53
  catch (error) {
53
54
  const missing = typeof error === "object" && error !== null && "statusCode" in error && error.statusCode === 404;
54
- return { success: false, missing, error: "Could not load your travel profile. Please try again." };
55
+ return { success: false, ...companionEligibilityFailure(error), missing, error: "Could not load your travel profile. Please try again." };
55
56
  }
56
57
  }
57
58
  export async function saveMyTravelProfile(profileData) {
@@ -61,8 +62,8 @@ export async function saveMyTravelProfile(profileData) {
61
62
  return { success: false, error: data.message };
62
63
  return { success: true, profile: data.data };
63
64
  }
64
- catch {
65
- return { success: false, error: "Failed to save profile" };
65
+ catch (error) {
66
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to save profile" };
66
67
  }
67
68
  }
68
69
  // ── Travel Plans ────────────────────────────────────────────────────────────────
@@ -73,8 +74,8 @@ export async function createTravelPlan(planData) {
73
74
  return { success: false, error: data.message };
74
75
  return { success: true, plan: data.data };
75
76
  }
76
- catch {
77
- return { success: false, error: "Failed to create travel plan" };
77
+ catch (error) {
78
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to create travel plan" };
78
79
  }
79
80
  }
80
81
  export async function uploadCompanionTripCover(file) {
@@ -87,8 +88,8 @@ export async function createTravelPlanFromItinerary(itineraryId, inviteCount, jo
87
88
  return { success: false, error: data.message };
88
89
  return { success: true, plan: data.data, message: data.message };
89
90
  }
90
- catch {
91
- return { success: false, error: "We could not post this trip. Please try again." };
91
+ catch (error) {
92
+ return { success: false, ...companionEligibilityFailure(error), error: "We could not post this trip. Please try again." };
92
93
  }
93
94
  }
94
95
  export async function attachItineraryToTravelPlan(planId, itineraryId) {
@@ -102,7 +103,7 @@ export async function attachItineraryToTravelPlan(planId, itineraryId) {
102
103
  const message = typeof error === "object" && error !== null && "message" in error
103
104
  ? String(error.message)
104
105
  : "We could not link this itinerary. Please try again.";
105
- return { success: false, error: message };
106
+ return { success: false, ...companionEligibilityFailure(error), error: message };
106
107
  }
107
108
  }
108
109
  export async function getCompanionListingSuggestions(planId) {
@@ -111,7 +112,7 @@ export async function getCompanionListingSuggestions(planId) {
111
112
  return { success: true, suggestions: data.data };
112
113
  }
113
114
  catch (error) {
114
- return { success: false, suggestions: null, error: error instanceof Error ? error.message : "Could not load trip suggestions." };
115
+ return { success: false, ...companionEligibilityFailure(error), suggestions: null, error: error instanceof Error ? error.message : "Could not load trip suggestions." };
115
116
  }
116
117
  }
117
118
  export async function addCompanionStandaloneLink(planId, kind, entityId) {
@@ -120,7 +121,7 @@ export async function addCompanionStandaloneLink(planId, kind, entityId) {
120
121
  return { success: true, item: data.data };
121
122
  }
122
123
  catch (error) {
123
- return { success: false, error: error instanceof Error ? error.message : "Could not add this listing to your trip." };
124
+ return { success: false, ...companionEligibilityFailure(error), error: error instanceof Error ? error.message : "Could not add this listing to your trip." };
124
125
  }
125
126
  }
126
127
  export async function removeCompanionStandaloneLink(planId, kind, entityId) {
@@ -129,7 +130,7 @@ export async function removeCompanionStandaloneLink(planId, kind, entityId) {
129
130
  return { success: true };
130
131
  }
131
132
  catch (error) {
132
- return { success: false, error: error instanceof Error ? error.message : "Could not remove this listing from your trip." };
133
+ return { success: false, ...companionEligibilityFailure(error), error: error instanceof Error ? error.message : "Could not remove this listing from your trip." };
133
134
  }
134
135
  }
135
136
  export async function getCompanionTripInspiration(filters) {
@@ -139,7 +140,7 @@ export async function getCompanionTripInspiration(filters) {
139
140
  return { success: true, items: data.data || [] };
140
141
  }
141
142
  catch (error) {
142
- return { success: false, items: [], error: error instanceof Error ? error.message : "Could not load trip inspiration." };
143
+ return { success: false, ...companionEligibilityFailure(error), items: [], error: error instanceof Error ? error.message : "Could not load trip inspiration." };
143
144
  }
144
145
  }
145
146
  export async function getTravelPlans(filters = {}) {
@@ -150,8 +151,8 @@ export async function getTravelPlans(filters = {}) {
150
151
  return { success: false, error: data.message, plans: [] };
151
152
  return { success: true, plans: data.data?.plans ?? data.data ?? [], total: data.data?.total ?? 0 };
152
153
  }
153
- catch {
154
- return { success: false, error: "Failed to fetch plans", plans: [] };
154
+ catch (error) {
155
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to fetch plans", plans: [] };
155
156
  }
156
157
  }
157
158
  export async function getTravelPlan(id) {
@@ -161,8 +162,8 @@ export async function getTravelPlan(id) {
161
162
  return { success: false, error: data.message };
162
163
  return { success: true, plan: data.data };
163
164
  }
164
- catch {
165
- return { success: false, error: "Failed to fetch plan" };
165
+ catch (error) {
166
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to fetch plan" };
166
167
  }
167
168
  }
168
169
  export async function updateTravelPlan(id, planData) {
@@ -172,8 +173,8 @@ export async function updateTravelPlan(id, planData) {
172
173
  return { success: false, error: data.message };
173
174
  return { success: true, plan: data.data };
174
175
  }
175
- catch {
176
- return { success: false, error: "Failed to update plan" };
176
+ catch (error) {
177
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to update plan" };
177
178
  }
178
179
  }
179
180
  export async function deleteTravelPlan(id) {
@@ -183,8 +184,8 @@ export async function deleteTravelPlan(id) {
183
184
  return { success: false, error: data.message };
184
185
  return { success: true, message: data.message };
185
186
  }
186
- catch {
187
- return { success: false, error: "Failed to delete plan" };
187
+ catch (error) {
188
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to delete plan" };
188
189
  }
189
190
  }
190
191
  export async function toggleLikePlan(id) {
@@ -194,8 +195,8 @@ export async function toggleLikePlan(id) {
194
195
  return { success: false, error: data.message };
195
196
  return { success: true, isLiked: data.data?.isLiked };
196
197
  }
197
- catch {
198
- return { success: false, error: "Failed to like plan" };
198
+ catch (error) {
199
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to like plan" };
199
200
  }
200
201
  }
201
202
  export async function getMyTravelPlans(options) {
@@ -206,8 +207,8 @@ export async function getMyTravelPlans(options) {
206
207
  return { success: false, error: data.message, plans: [] };
207
208
  return { success: true, plans: data.data?.plans ?? data.data ?? [] };
208
209
  }
209
- catch {
210
- return { success: false, error: "Failed to fetch my plans", plans: [] };
210
+ catch (error) {
211
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to fetch my plans", plans: [] };
211
212
  }
212
213
  }
213
214
  export async function getMySentRequests() {
@@ -217,8 +218,8 @@ export async function getMySentRequests() {
217
218
  return { success: false, error: data.message, requests: [] };
218
219
  return { success: true, requests: data.data ?? [] };
219
220
  }
220
- catch {
221
- return { success: false, error: "Failed to fetch sent requests", requests: [] };
221
+ catch (error) {
222
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to fetch sent requests", requests: [] };
222
223
  }
223
224
  }
224
225
  // ── Join Requests ───────────────────────────────────────────────────────────────
@@ -234,8 +235,8 @@ export async function sendJoinRequest(planId, message) {
234
235
  chatId: data.data?.chatId ?? null,
235
236
  };
236
237
  }
237
- catch {
238
- return { success: false, error: "Failed to send join request" };
238
+ catch (error) {
239
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to send join request" };
239
240
  }
240
241
  }
241
242
  export async function withdrawJoinRequest(planId, requestId) {
@@ -245,8 +246,8 @@ export async function withdrawJoinRequest(planId, requestId) {
245
246
  return { success: false, error: data.message };
246
247
  return { success: true, status: data.data?.status };
247
248
  }
248
- catch {
249
- return { success: false, error: "Could not withdraw this request. Please refresh and try again." };
249
+ catch (error) {
250
+ return { success: false, ...companionEligibilityFailure(error), error: "Could not withdraw this request. Please refresh and try again." };
250
251
  }
251
252
  }
252
253
  export async function getJoinRequests(planId) {
@@ -256,8 +257,8 @@ export async function getJoinRequests(planId) {
256
257
  return { success: false, error: data.message, requests: [] };
257
258
  return { success: true, requests: data.data ?? [] };
258
259
  }
259
- catch {
260
- return { success: false, error: "Failed to fetch requests", requests: [] };
260
+ catch (error) {
261
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to fetch requests", requests: [] };
261
262
  }
262
263
  }
263
264
  export async function handleJoinRequest(planId, requestId, action) {
@@ -267,8 +268,8 @@ export async function handleJoinRequest(planId, requestId, action) {
267
268
  return { success: false, error: data.message };
268
269
  return { success: true, status: data.data?.status, chatId: data.data?.chatId ?? null };
269
270
  }
270
- catch {
271
- return { success: false, error: "Failed to handle request" };
271
+ catch (error) {
272
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to handle request" };
272
273
  }
273
274
  }
274
275
  // ── Swipe Discovery ─────────────────────────────────────────────────────────────
@@ -280,8 +281,8 @@ export async function discoverTrips(filters = {}) {
280
281
  return { success: false, error: data.message, trips: [] };
281
282
  return { success: true, trips: data.data ?? [] };
282
283
  }
283
- catch {
284
- return { success: false, error: "Failed to discover trips", trips: [] };
284
+ catch (error) {
285
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to discover trips", trips: [] };
285
286
  }
286
287
  }
287
288
  export async function swipeTrip(travelPlanId, action) {
@@ -291,8 +292,8 @@ export async function swipeTrip(travelPlanId, action) {
291
292
  return { success: false, error: data.message };
292
293
  return { success: true, joinRequestSent: data.data?.joinRequestSent ?? false };
293
294
  }
294
- catch {
295
- return { success: false, error: "Failed to swipe trip" };
295
+ catch (error) {
296
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to swipe trip" };
296
297
  }
297
298
  }
298
299
  export async function discoverTravelers(filters = {}) {
@@ -303,8 +304,8 @@ export async function discoverTravelers(filters = {}) {
303
304
  return { success: false, error: data.message, travelers: [] };
304
305
  return { success: true, travelers: data.data ?? [] };
305
306
  }
306
- catch {
307
- return { success: false, error: "Failed to discover travelers", travelers: [] };
307
+ catch (error) {
308
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to discover travelers", travelers: [] };
308
309
  }
309
310
  }
310
311
  export async function swipeTraveler(targetUserId, action) {
@@ -319,8 +320,8 @@ export async function swipeTraveler(targetUserId, action) {
319
320
  matchScore: data.data?.matchScore ?? 0,
320
321
  };
321
322
  }
322
- catch {
323
- return { success: false, error: "Failed to swipe traveler" };
323
+ catch (error) {
324
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to swipe traveler" };
324
325
  }
325
326
  }
326
327
  // ── Matches ─────────────────────────────────────────────────────────────────────
@@ -332,8 +333,8 @@ export async function getMyMatches(options) {
332
333
  return { success: false, error: data.message, matches: [] };
333
334
  return { success: true, matches: data.data ?? [] };
334
335
  }
335
- catch {
336
- return { success: false, error: "Failed to fetch matches", matches: [] };
336
+ catch (error) {
337
+ return { success: false, ...companionEligibilityFailure(error), error: "Failed to fetch matches", matches: [] };
337
338
  }
338
339
  }
339
340
  // ── Safety ──────────────────────────────────────────────────────────────────────
@@ -1,11 +1,9 @@
1
1
  import { type UploadFile } from "../utils/uploadFile";
2
+ import type { CompanionEligibilityDenial } from "../models/companion";
2
3
  export declare function uploadCompanionTripCover(file: UploadFile): Promise<{
3
4
  success: boolean;
4
- url: string;
5
- error?: undefined;
6
- } | {
7
- success: boolean;
8
- error: string;
9
- url?: undefined;
5
+ url?: string;
6
+ error?: string;
7
+ eligibilityDenial?: CompanionEligibilityDenial;
10
8
  }>;
11
9
  //# sourceMappingURL=companionUpload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"companionUpload.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/companionUpload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAExE,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,UAAU;;;;;;;;GAS9D"}
1
+ {"version":3,"file":"companionUpload.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/companionUpload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEtE,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,0BAA0B,CAAC;CAChD,CAAC,CASD"}
@@ -1,6 +1,7 @@
1
1
  import { post } from "./fetcher";
2
2
  import { runUploadTask } from "../utils/uploadTasks";
3
3
  import { appendUploadFile } from "../utils/uploadFile";
4
+ import { companionEligibilityFailure } from "../companion/eligibility";
4
5
  export async function uploadCompanionTripCover(file) {
5
6
  const body = new FormData();
6
7
  appendUploadFile(body, "images", file);
@@ -9,6 +10,6 @@ export async function uploadCompanionTripCover(file) {
9
10
  return { success: true, url: response.data.url };
10
11
  }
11
12
  catch (error) {
12
- return { success: false, error: error instanceof Error ? error.message : "Could not upload the trip cover." };
13
+ return { success: false, ...companionEligibilityFailure(error), error: error instanceof Error ? error.message : "Could not upload the trip cover." };
13
14
  }
14
15
  }
@@ -1,5 +1,5 @@
1
1
  import type { UserRole } from "../models/props";
2
- import type { User } from "../models/user";
2
+ import type { User, UserContactDetails, UserContactIdentity } from "../models/user";
3
3
  /** Strict session read for native restoration: outages must not look signed out. */
4
4
  export declare function getTravellerSessionUser(): Promise<User>;
5
5
  export declare function getCurrentUser(): Promise<User | null>;
@@ -34,16 +34,13 @@ export declare const userApi: {
34
34
  };
35
35
  /** Public compatibility name used by profile surfaces. */
36
36
  export declare const userDetails: typeof getUserDetails;
37
- export declare function getUserContact(username: string, role: string): Promise<{
37
+ type ContactLookupResult<T> = {
38
38
  success: boolean;
39
39
  message?: string;
40
40
  error?: string;
41
- details: {
42
- _id: string;
43
- name: string;
44
- email: string;
45
- countryCode: string;
46
- phoneNumber: string;
47
- } | null;
48
- }>;
41
+ details: T | null;
42
+ };
43
+ export declare function getUserContact(username: string, role: string, identityOnly: true): Promise<ContactLookupResult<UserContactIdentity>>;
44
+ export declare function getUserContact(username: string, role: string, identityOnly?: false): Promise<ContactLookupResult<UserContactDetails>>;
45
+ export {};
49
46
  //# sourceMappingURL=user.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/user.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAK3C,oFAAoF;AACpF,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7D;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAe3D;AACD,wBAAsB,cAAc,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,QAAQ,GAAG,MAAM,GACxB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBrG;AAED,eAAO,MAAM,OAAO;wBAEU,MAAM,QAAQ,MAAM;eAED,MAAM;cAAQ,GAAG;;;;;0BAQlC,MAAM,QAAQ,MAAM;eAEH,MAAM;cAAQ,GAAG;;;;;;;6BA4B/B,MAAM,QAAQ,MAAM;mBAEoB,OAAO;oBAAc,OAAO;;CAOtG,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,WAAW,uBAAiB,CAAC;AAE1C,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,CAAC,CAiB5O"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/user.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAKpF,oFAAoF;AACpF,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7D;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAe3D;AACD,wBAAsB,cAAc,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,QAAQ,GAAG,MAAM,GACxB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBrG;AAED,eAAO,MAAM,OAAO;wBAEU,MAAM,QAAQ,MAAM;eAED,MAAM;cAAQ,GAAG;;;;;0BAQlC,MAAM,QAAQ,MAAM;eAEH,MAAM;cAAQ,GAAG;;;;;;;6BA4B/B,MAAM,QAAQ,MAAM;mBAEoB,OAAO;oBAAc,OAAO;;CAOtG,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,WAAW,uBAAiB,CAAC;AAE1C,KAAK,mBAAmB,CAAC,CAAC,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAA;CAAE,CAAC;AAExG,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,GAAG,OAAO,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CAAC;AACtI,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,CAAC,CAAC"}
@@ -97,9 +97,9 @@ export const userApi = {
97
97
  };
98
98
  /** Public compatibility name used by profile surfaces. */
99
99
  export const userDetails = getUserDetails;
100
- export async function getUserContact(username, role) {
100
+ export async function getUserContact(username, role, identityOnly = false) {
101
101
  try {
102
- const endpoint = `/${role}s/getUserContact/${username}`;
102
+ const endpoint = `/${role}s/getUserContact/${encodeURIComponent(username)}${identityOnly ? "?identityOnly=true" : ""}`;
103
103
  const data = await get(endpoint);
104
104
  return {
105
105
  success: true,
@@ -0,0 +1,14 @@
1
+ import type { CompanionEligibilityDenial } from "../models/companion";
2
+ import type { User } from "../models/user";
3
+ export declare function companionProfileFields(user: User): {
4
+ verifiedIdCard: boolean | undefined;
5
+ location: string | undefined;
6
+ pinCode: string | undefined;
7
+ profilePic: string | undefined;
8
+ };
9
+ export declare function isCompanionProfileComplete(profile: ReturnType<typeof companionProfileFields>): boolean;
10
+ export declare function readCompanionEligibilityDenial(value: unknown): CompanionEligibilityDenial | undefined;
11
+ export declare function companionEligibilityFailure(error: unknown): {
12
+ eligibilityDenial?: CompanionEligibilityDenial;
13
+ };
14
+ //# sourceMappingURL=eligibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../../src/lib/companion/eligibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE3C,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI;;;;;EAOhD;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,WAE5F;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,GAAG,SAAS,CAKrG;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,iBAAiB,CAAC,EAAE,0BAA0B,CAAA;CAAE,CAI9G"}
@@ -0,0 +1,24 @@
1
+ export function companionProfileFields(user) {
2
+ return {
3
+ verifiedIdCard: user.verifiedIdCard,
4
+ location: user.location,
5
+ pinCode: user.pinCode,
6
+ profilePic: user.profilePic,
7
+ };
8
+ }
9
+ export function isCompanionProfileComplete(profile) {
10
+ return profile.verifiedIdCard === true && Boolean(profile.location && profile.pinCode && profile.profilePic);
11
+ }
12
+ export function readCompanionEligibilityDenial(value) {
13
+ if (!value || typeof value !== "object" || !("code" in value) || value.code !== "COMPANION_PROFILE_REQUIRED" ||
14
+ !("missingFields" in value) || !Array.isArray(value.missingFields) ||
15
+ !value.missingFields.length || !value.missingFields.every((field) => typeof field === "string"))
16
+ return;
17
+ return { code: value.code, missingFields: value.missingFields };
18
+ }
19
+ export function companionEligibilityFailure(error) {
20
+ if (!error || typeof error !== "object" || !("statusCode" in error) || error.statusCode !== 403 || !("data" in error))
21
+ return {};
22
+ const eligibilityDenial = readCompanionEligibilityDenial(error.data);
23
+ return eligibilityDenial ? { eligibilityDenial } : {};
24
+ }
@@ -3,7 +3,7 @@ import type { Socket } from "socket.io-client";
3
3
  import { type ChatActorModel, type Conversation, type Message, type Poll } from "../models/chat";
4
4
  import type { Notif } from "../models/notifications";
5
5
  export interface ConversationListParams {
6
- filter?: "all" | "unread" | "hosts" | "groups" | "favourites" | "archived";
6
+ filter?: "all" | "unread" | "hosts" | "groups" | "favourites" | "archived" | "primary" | "requests";
7
7
  q?: string;
8
8
  before?: string;
9
9
  limit?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../../src/lib/context/ChatContext.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../../src/lib/context/ChatContext.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { CompanionEligibility } from "../models/companion";
2
+ export type ObserveCompanionRequest = <T>(request: Promise<T>) => Promise<T>;
3
+ export declare function useCompanionEligibility(): {
4
+ canOpen: boolean;
5
+ observe: ObserveCompanionRequest;
6
+ canCheck: boolean;
7
+ checking: boolean;
8
+ fetching: boolean;
9
+ missingFields: string[];
10
+ error: string;
11
+ retry: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<CompanionEligibility, Error>>;
12
+ };
13
+ //# sourceMappingURL=useCompanionEligibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCompanionEligibility.d.ts","sourceRoot":"","sources":["../../../src/lib/hooks/useCompanionEligibility.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAgBhE,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7E,wBAAgB,uBAAuB;;;;;;;;;EAsGtC"}
@@ -0,0 +1,126 @@
1
+ "use client";
2
+ import { useCallback, useEffect, useMemo } from "react";
3
+ import { useAtomValue, useStore } from "jotai";
4
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
5
+ import { authRevisionAtom, authSessionStatusAtom, userAtom } from "../userAtom";
6
+ import { getCompanionEligibility } from "../actions/companion";
7
+ import { getTravellerSessionUser } from "../actions/user";
8
+ import { isTravellerAccount } from "../models/authorization";
9
+ import { companionProfileFields, isCompanionProfileComplete, readCompanionEligibilityDenial } from "../companion/eligibility";
10
+ const eligibilityFreshMs = 60000;
11
+ const checkingError = "Could not check your profile. Please try again.";
12
+ function profileScope(user, revision, authenticated) {
13
+ return authenticated && user?._id && isTravellerAccount(user)
14
+ ? JSON.stringify([user._id, revision, companionProfileFields(user)])
15
+ : null;
16
+ }
17
+ const eligibilityKey = (scope) => ["companion-eligibility", scope];
18
+ export function useCompanionEligibility() {
19
+ const user = useAtomValue(userAtom);
20
+ const revision = useAtomValue(authRevisionAtom);
21
+ const status = useAtomValue(authSessionStatusAtom);
22
+ const store = useStore();
23
+ const queryClient = useQueryClient();
24
+ const scope = profileScope(user, revision, status === "authenticated");
25
+ const complete = Boolean(user && isCompanionProfileComplete(companionProfileFields(user)));
26
+ const queryKey = useMemo(() => eligibilityKey(scope), [scope]);
27
+ const currentScope = useCallback(() => profileScope(store.get(userAtom), store.get(authRevisionAtom), store.get(authSessionStatusAtom) === "authenticated"), [store]);
28
+ const check = useQuery({
29
+ queryKey,
30
+ enabled: Boolean(scope && !complete),
31
+ queryFn: async () => {
32
+ if (!scope || currentScope() !== scope)
33
+ throw new Error(checkingError);
34
+ const result = await getCompanionEligibility();
35
+ if (!result.success)
36
+ throw new Error(result.error);
37
+ return { eligible: result.eligible, missingFields: result.missingFields };
38
+ },
39
+ staleTime: eligibilityFreshMs,
40
+ gcTime: Infinity,
41
+ retry: false,
42
+ refetchOnWindowFocus: false,
43
+ refetchOnReconnect: false,
44
+ refetchOnMount: (query) => query.state.data?.eligible !== false,
45
+ });
46
+ const approved = check.data?.eligible === true;
47
+ useEffect(() => {
48
+ if (!scope || complete || !approved || check.isFetching)
49
+ return;
50
+ void queryClient.fetchQuery({
51
+ queryKey: ["companion-profile-refresh", scope],
52
+ staleTime: eligibilityFreshMs,
53
+ retry: false,
54
+ queryFn: async () => {
55
+ const before = store.get(userAtom);
56
+ if (!before || currentScope() !== scope)
57
+ return false;
58
+ let changed = false;
59
+ const trackChange = () => { if (currentScope() !== scope)
60
+ changed = true; };
61
+ const unsubscribe = [
62
+ store.sub(userAtom, trackChange),
63
+ store.sub(authRevisionAtom, trackChange),
64
+ store.sub(authSessionStatusAtom, trackChange),
65
+ ];
66
+ try {
67
+ const fresh = await getTravellerSessionUser();
68
+ const current = store.get(userAtom);
69
+ if (changed || currentScope() !== scope || !current || fresh?._id !== before._id ||
70
+ !isTravellerAccount(fresh) || !queryClient.getQueryData(queryKey)?.eligible)
71
+ return false;
72
+ const fields = companionProfileFields(fresh);
73
+ if (!isCompanionProfileComplete(fields))
74
+ return false;
75
+ const sessionKey = ["session", store.get(authRevisionAtom)];
76
+ const sessionProfile = queryClient.getQueryData(sessionKey)?.session?.user;
77
+ store.set(userAtom, { ...current, ...fields });
78
+ // Ask the existing session observer to reread an older profile projection
79
+ // so its cached response cannot keep restoring the old fields.
80
+ if (sessionProfile && sessionProfile._id === current._id &&
81
+ JSON.stringify(companionProfileFields(sessionProfile)) !== JSON.stringify(fields)) {
82
+ void queryClient.invalidateQueries({ queryKey: sessionKey, exact: true });
83
+ }
84
+ return true;
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ finally {
90
+ unsubscribe.forEach((stop) => stop());
91
+ }
92
+ },
93
+ });
94
+ }, [approved, check.isFetching, complete, currentScope, queryClient, queryKey, scope, store]);
95
+ const observe = useCallback(async (request) => {
96
+ const startedScope = currentScope();
97
+ const requestKey = eligibilityKey(startedScope);
98
+ const started = queryClient.getQueryState(requestKey)?.dataUpdateCount;
99
+ const result = await request;
100
+ if (!startedScope || currentScope() !== startedScope || !result || typeof result !== "object" || !("eligibilityDenial" in result))
101
+ return result;
102
+ const denial = readCompanionEligibilityDenial(result.eligibilityDenial);
103
+ const current = queryClient.getQueryState(requestKey);
104
+ if (!denial || (current?.data?.eligible && current.dataUpdateCount !== started))
105
+ return result;
106
+ await queryClient.cancelQueries({ queryKey: requestKey, exact: true });
107
+ if (currentScope() === startedScope) {
108
+ queryClient.setQueryData(requestKey, { eligible: false, missingFields: denial.missingFields });
109
+ }
110
+ return result;
111
+ }, [currentScope, queryClient]);
112
+ const denied = check.data?.eligible === false;
113
+ const canOpen = Boolean(scope && !denied && (complete || (approved && !check.isError && (!check.isStale || !check.isFetching))));
114
+ const unavailable = status === "authenticated" && !scope;
115
+ const error = unavailable ? "Find Companion is available to traveller accounts only." : check.isError ? checkingError : "";
116
+ return {
117
+ canOpen,
118
+ observe,
119
+ canCheck: Boolean(scope),
120
+ checking: !canOpen && !denied && !error,
121
+ fetching: check.isFetching,
122
+ missingFields: check.data?.missingFields ?? [],
123
+ error,
124
+ retry: check.refetch,
125
+ };
126
+ }