@scalemule/news-publication 0.0.61 → 0.0.63

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.
Files changed (68) hide show
  1. package/dist/chunk-3QU6BJVF.js +540 -0
  2. package/dist/chunk-4WVAT5M2.js +213 -0
  3. package/dist/chunk-6SBK3D3B.js +347 -0
  4. package/dist/chunk-CHF3YPDV.js +314 -0
  5. package/dist/chunk-FHRO2Z3F.js +174 -0
  6. package/dist/{chunk-DM4KQNJH.js → chunk-FWEYKWKP.js} +1 -1
  7. package/dist/{chunk-FMCHQI7K.js → chunk-KYUVXEJY.js} +34 -12
  8. package/dist/{chunk-F53CPZQO.js → chunk-Q77T2S7B.js} +1 -1
  9. package/dist/chunk-SKG5C3MS.js +379 -0
  10. package/dist/{chunk-GYYUJYI4.js → chunk-X7AU7DV6.js} +2 -3
  11. package/dist/{chunk-XPOZGZTM.js → chunk-XSAVJJVW.js} +11 -4
  12. package/dist/{chunk-6S5FARL3.js → chunk-XWXPERLK.js} +2 -2
  13. package/dist/cookie-consent-banner.cjs +10 -3
  14. package/dist/cookie-consent-banner.d.cts +1 -1
  15. package/dist/cookie-consent-banner.d.ts +1 -1
  16. package/dist/cookie-consent-banner.js +2 -2
  17. package/dist/cookie-settings-view.cjs +2 -3
  18. package/dist/cookie-settings-view.js +2 -2
  19. package/dist/index.cjs +60 -18
  20. package/dist/index.d.cts +8 -7
  21. package/dist/index.d.ts +8 -7
  22. package/dist/index.js +10 -8
  23. package/dist/meet-view.d.cts +1 -1
  24. package/dist/meet-view.d.ts +1 -1
  25. package/dist/network-bar.js +2 -2
  26. package/dist/network-copyright.js +2 -2
  27. package/dist/network.js +1 -1
  28. package/dist/pets-plugin.js +4 -347
  29. package/dist/{snapshot-DmRw8G_B.d.ts → snapshot-CjP7bSXJ.d.ts} +1 -1
  30. package/dist/snapshot-file.d.cts +2 -2
  31. package/dist/snapshot-file.d.ts +2 -2
  32. package/dist/{snapshot-Dx4Lv-lB.d.cts → snapshot-qj4BeNzB.d.cts} +1 -1
  33. package/dist/toys-forms.cjs +609 -0
  34. package/dist/toys-forms.d.cts +35 -0
  35. package/dist/toys-forms.d.ts +35 -0
  36. package/dist/toys-forms.js +4 -0
  37. package/dist/toys-interactions.cjs +594 -0
  38. package/dist/toys-interactions.d.cts +54 -0
  39. package/dist/toys-interactions.d.ts +54 -0
  40. package/dist/toys-interactions.js +520 -0
  41. package/dist/toys-plugin.cjs +713 -0
  42. package/dist/toys-plugin.d.cts +88 -0
  43. package/dist/toys-plugin.d.ts +88 -0
  44. package/dist/toys-plugin.js +10 -0
  45. package/dist/toys-publication.cjs +437 -0
  46. package/dist/toys-publication.d.cts +26 -0
  47. package/dist/toys-publication.d.ts +26 -0
  48. package/dist/toys-publication.js +72 -0
  49. package/dist/toys-server.cjs +275 -0
  50. package/dist/toys-server.d.cts +28 -0
  51. package/dist/toys-server.d.ts +28 -0
  52. package/dist/toys-server.js +4 -0
  53. package/dist/toys-view.cjs +420 -0
  54. package/dist/toys-view.d.cts +55 -0
  55. package/dist/toys-view.d.ts +55 -0
  56. package/dist/toys-view.js +2 -0
  57. package/dist/toys.cjs +196 -0
  58. package/dist/toys.d.cts +278 -0
  59. package/dist/toys.d.ts +278 -0
  60. package/dist/toys.js +1 -0
  61. package/dist/{types-BJFCWWi0.d.cts → types-DBi49N8m.d.cts} +12 -3
  62. package/dist/{types-BJFCWWi0.d.ts → types-DBi49N8m.d.ts} +12 -3
  63. package/dist/verified-card.cjs +34 -12
  64. package/dist/verified-card.d.cts +1 -1
  65. package/dist/verified-card.d.ts +1 -1
  66. package/dist/verified-card.js +1 -1
  67. package/package.json +41 -4
  68. package/toys.css +823 -0
@@ -0,0 +1,379 @@
1
+ import { ToyWordmark, ToyPhoto, ToyBadges, ToyListingCard, ToyLocationCard, ToyRulesContent } from './chunk-CHF3YPDV.js';
2
+ import { toyCollectionRequest } from './chunk-4WVAT5M2.js';
3
+ import { TOY_BRAND, TOY_STATUS_LABELS, TOY_REQUEST_STATUS_LABELS, formatLocalStart, TOY_UUID, toyOptionLabel, TOY_CATEGORIES, TOY_CONDITIONS, TOY_AGE_RANGES, TOY_SAFETY, resolveToyCommunity } from './chunk-FHRO2Z3F.js';
4
+ import { petReaderAuthenticated } from './chunk-6SBK3D3B.js';
5
+ import { ToySignIn, ToyMemberSetup, ToyOwnerActions, ToyListingForm } from '@scalemule/news-publication/toys-forms';
6
+ import { ToySignOut, ToyAccountDelete, ToyRequestPanel, ToyListingActions, ToyExchangeThread, ToyLocationSuggestForm } from '@scalemule/news-publication/toys-interactions';
7
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
+
9
+ var publicationToyPaths = {
10
+ home: "/toys",
11
+ account: "/toys/account",
12
+ submit: "/toys/submit",
13
+ listing: "/toys",
14
+ exchanges: "/toys/exchanges",
15
+ locations: "/toys/places",
16
+ rules: "/toys/rules",
17
+ profiles: "/toys/neighbors",
18
+ api: "/api/toys",
19
+ auth: "/api/paw-auth",
20
+ community: "/api/toy-community"
21
+ };
22
+ var standaloneToyPaths = {
23
+ home: "/",
24
+ account: "/account",
25
+ submit: "/give",
26
+ listing: "/toys",
27
+ exchanges: "/exchanges",
28
+ locations: "/places",
29
+ rules: "/rules",
30
+ profiles: "/neighbors",
31
+ api: "/api/toys",
32
+ auth: "/api/paw-auth",
33
+ community: "/api/toy-community"
34
+ };
35
+ var toyReaderAuthenticated = petReaderAuthenticated;
36
+ var toyMediaUrl = (api) => (listing, media) => `${api}/${encodeURIComponent(listing)}/media/${encodeURIComponent(media)}`;
37
+ function Shell({ paths, children, signedIn, embedded = false }) {
38
+ return /* @__PURE__ */ jsxs("div", { className: `toy${embedded ? " toy-embedded" : ""}`, children: [
39
+ /* @__PURE__ */ jsxs("header", { className: "toy-page-heading", children: [
40
+ /* @__PURE__ */ jsx(ToyWordmark, { href: paths.home }),
41
+ /* @__PURE__ */ jsxs("nav", { className: "toy-masthead-links", "aria-label": "ToyRound", children: [
42
+ /* @__PURE__ */ jsx("a", { href: paths.submit, children: "Give or swap" }),
43
+ /* @__PURE__ */ jsx("a", { href: paths.account, children: signedIn ? "My toys" : "Sign in" }),
44
+ signedIn && /* @__PURE__ */ jsx(ToySignOut, { returnTo: paths.home, authPath: paths.auth })
45
+ ] })
46
+ ] }),
47
+ children,
48
+ /* @__PURE__ */ jsxs("footer", { className: "toy-footer toy-page-footer", children: [
49
+ /* @__PURE__ */ jsxs("div", { children: [
50
+ /* @__PURE__ */ jsx(ToyWordmark, { size: 28, href: paths.home }),
51
+ /* @__PURE__ */ jsx("p", { children: TOY_BRAND.tagline })
52
+ ] }),
53
+ /* @__PURE__ */ jsxs("p", { children: [
54
+ embedded && /* @__PURE__ */ jsxs(Fragment, { children: [
55
+ /* @__PURE__ */ jsx("a", { href: TOY_BRAND.origin, children: TOY_BRAND.poweredBy }),
56
+ /* @__PURE__ */ jsx("br", {})
57
+ ] }),
58
+ "A community toy exchange by ",
59
+ /* @__PURE__ */ jsx("a", { href: TOY_BRAND.networkUrl, children: "Bay Area News Network" }),
60
+ "."
61
+ ] }),
62
+ /* @__PURE__ */ jsxs("p", { className: "toy-footer-links", children: [
63
+ /* @__PURE__ */ jsx("a", { href: paths.rules, children: "Rules, prohibited items, privacy & safety" }),
64
+ /* @__PURE__ */ jsx("a", { href: paths.locations, children: "Meeting places" })
65
+ ] })
66
+ ] })
67
+ ] });
68
+ }
69
+ function LoadError({ error, retry, account, auth }) {
70
+ const status = error && typeof error === "object" && "status" in error ? error.status : void 0;
71
+ if (status === 401 || status === 403)
72
+ return /* @__PURE__ */ jsxs("div", { className: "toy-load-error", role: "status", children: [
73
+ /* @__PURE__ */ jsx("h2", { children: "Reconnect your network account." }),
74
+ /* @__PURE__ */ jsx(ToySignIn, { returnTo: retry, authPath: auth })
75
+ ] });
76
+ return /* @__PURE__ */ jsxs("div", { className: "toy-load-error", role: "status", children: [
77
+ /* @__PURE__ */ jsx("h2", { children: "We couldn't open ToyRound." }),
78
+ /* @__PURE__ */ jsx("p", { children: status === 429 ? "There have been several requests in a short time. Wait a moment, then try again." : "The connection is temporarily unavailable. Try loading this page again." }),
79
+ /* @__PURE__ */ jsxs("div", { className: "toy-form-actions", children: [
80
+ /* @__PURE__ */ jsx("a", { className: "toy-button", href: retry, children: "Try again" }),
81
+ retry !== account && /* @__PURE__ */ jsx("a", { className: "toy-text-link", href: account, children: "My toys" })
82
+ ] })
83
+ ] });
84
+ }
85
+ async function policy(scope) {
86
+ return toyCollectionRequest({ ...scope, accessToken: void 0 }, "/policy");
87
+ }
88
+ async function ToyAccountPage({ scope, signedIn, paths = publicationToyPaths, signInEnabled = true, embedded = false }) {
89
+ let body;
90
+ if (!signedIn) body = signInEnabled ? /* @__PURE__ */ jsx(ToySignIn, { returnTo: paths.account, authPath: paths.auth }) : /* @__PURE__ */ jsx("p", { children: "Community sign-in is being prepared. Browsing is open." });
91
+ else
92
+ try {
93
+ const [member, mine, inbox] = await Promise.all([
94
+ toyCollectionRequest(scope, "/member"),
95
+ toyCollectionRequest(scope, "/mine"),
96
+ toyCollectionRequest(scope, "/requests")
97
+ ]);
98
+ const open = inbox.items.filter((r) => ["pending", "accepted"].includes(r.status));
99
+ const closed = inbox.items.filter((r) => !["pending", "accepted"].includes(r.status));
100
+ body = /* @__PURE__ */ jsxs(Fragment, { children: [
101
+ /* @__PURE__ */ jsx(ToyMemberSetup, { member, apiPath: paths.api }),
102
+ /* @__PURE__ */ jsxs("section", { className: "toy-section", "aria-labelledby": "toy-my-listings", children: [
103
+ /* @__PURE__ */ jsxs("div", { className: "toy-section-heading", children: [
104
+ /* @__PURE__ */ jsx("h2", { id: "toy-my-listings", children: "My listings" }),
105
+ /* @__PURE__ */ jsx("a", { className: "toy-button", href: paths.submit, children: "Give or swap a toy" })
106
+ ] }),
107
+ !mine.items.length && /* @__PURE__ */ jsx("p", { className: "toy-note", children: "Nothing listed yet. Outgrown toys, puzzles and games are welcome." }),
108
+ mine.items.map((listing) => /* @__PURE__ */ jsxs("article", { className: "toy-account-row", children: [
109
+ /* @__PURE__ */ jsx(ToyPhoto, { listing, mediaUrl: toyMediaUrl(paths.api) }),
110
+ /* @__PURE__ */ jsxs("div", { children: [
111
+ /* @__PURE__ */ jsx("h3", { children: listing.status === "available" || listing.status === "reserved" ? /* @__PURE__ */ jsx("a", { href: `${paths.listing}/${listing.id}`, children: listing.content.title }) : listing.content.title }),
112
+ /* @__PURE__ */ jsxs("p", { className: "toy-context", children: [
113
+ TOY_STATUS_LABELS[listing.status] || listing.status,
114
+ listing.open_requests ? ` \xB7 ${listing.open_requests} open request${listing.open_requests === 1 ? "" : "s"}` : ""
115
+ ] }),
116
+ listing.review_note && /* @__PURE__ */ jsxs("p", { className: "toy-note", children: [
117
+ "Editor's note: ",
118
+ listing.review_note
119
+ ] }),
120
+ listing.suspension && /* @__PURE__ */ jsxs("p", { className: "toy-note", children: [
121
+ "Suspended for safety review",
122
+ listing.suspension.note ? `: ${listing.suspension.note}` : "."
123
+ ] }),
124
+ /* @__PURE__ */ jsx(ToyOwnerActions, { listing, apiPath: paths.api, submitUrl: paths.submit })
125
+ ] })
126
+ ] }, listing.id))
127
+ ] }),
128
+ /* @__PURE__ */ jsxs("section", { className: "toy-section", "aria-labelledby": "toy-exchanges-title", children: [
129
+ /* @__PURE__ */ jsx("div", { className: "toy-section-heading", children: /* @__PURE__ */ jsx("h2", { id: "toy-exchanges-title", children: "Exchanges" }) }),
130
+ !open.length && /* @__PURE__ */ jsx("p", { className: "toy-note", children: "No open requests or offers." }),
131
+ /* @__PURE__ */ jsx("ul", { className: "toy-inbox", children: [...open, ...closed.slice(0, 10)].map((r) => /* @__PURE__ */ jsx("li", { className: ["pending", "accepted"].includes(r.status) ? "open" : "closed", children: /* @__PURE__ */ jsxs("a", { href: `${paths.exchanges}/${r.id}`, children: [
132
+ /* @__PURE__ */ jsx("strong", { children: r.listing.title }),
133
+ /* @__PURE__ */ jsxs("span", { children: [
134
+ r.role === "owner" ? "From" : "To",
135
+ " ",
136
+ r.counterpart_deleted ? "a closed account" : r.counterpart?.display_name || "a neighbor",
137
+ " \xB7 ",
138
+ TOY_REQUEST_STATUS_LABELS[r.status] || r.status,
139
+ r.appointment?.status === "confirmed" ? ` \xB7 meeting ${formatLocalStart(r.appointment.local_start, r.appointment.timezone)}` : r.appointment?.status === "proposed" ? " \xB7 meeting proposed" : ""
140
+ ] })
141
+ ] }) }, r.id)) })
142
+ ] }),
143
+ /* @__PURE__ */ jsx(ToyAccountDelete, { revision: member.revision, apiPath: paths.api, returnTo: paths.home })
144
+ ] });
145
+ } catch (error) {
146
+ body = /* @__PURE__ */ jsx(LoadError, { error, retry: paths.account, account: paths.account, auth: paths.auth });
147
+ }
148
+ return /* @__PURE__ */ jsx(Shell, { paths, signedIn, embedded, children: /* @__PURE__ */ jsxs("section", { className: "toy-story", children: [
149
+ /* @__PURE__ */ jsx("p", { className: "toy-eyebrow", children: "Your ToyRound account" }),
150
+ /* @__PURE__ */ jsx("h1", { children: "My toys" }),
151
+ body
152
+ ] }) });
153
+ }
154
+ async function ToySubmitPage({ scope, signedIn, paths = publicationToyPaths, edit, community, submissionsEnabled = true, embedded = false }) {
155
+ let form;
156
+ const next = paths.submit + "?" + new URLSearchParams({ ...edit ? { edit } : {}, ...community ? { community } : {} });
157
+ if (!submissionsEnabled)
158
+ form = /* @__PURE__ */ jsxs("section", { className: "toy-form", children: [
159
+ /* @__PURE__ */ jsx("h1", { children: "Listings open soon." }),
160
+ /* @__PURE__ */ jsx("p", { children: "New listings are paused while our local review team gets ready. Browsing stays open." })
161
+ ] });
162
+ else if (!signedIn)
163
+ form = /* @__PURE__ */ jsxs("section", { className: "toy-form", children: [
164
+ /* @__PURE__ */ jsx("h1", { children: "Give or swap a toy." }),
165
+ /* @__PURE__ */ jsx("p", { children: "Sign in with your network account to list a toy. Listing needs an adult account with verified email and phone." }),
166
+ /* @__PURE__ */ jsx(ToySignIn, { returnTo: next, authPath: paths.auth })
167
+ ] });
168
+ else
169
+ try {
170
+ const [rules, member, initial] = await Promise.all([
171
+ policy(scope),
172
+ toyCollectionRequest(scope, "/member"),
173
+ edit && TOY_UUID.test(edit) ? toyCollectionRequest(scope, "/mine/" + edit) : void 0
174
+ ]);
175
+ if (!member.verification.ready || member.status === "suspended")
176
+ form = /* @__PURE__ */ jsxs("section", { className: "toy-form", children: [
177
+ /* @__PURE__ */ jsx("h1", { children: "Almost there." }),
178
+ /* @__PURE__ */ jsx("p", { children: "Finish these steps, then come back to list your toy." }),
179
+ /* @__PURE__ */ jsx(ToyMemberSetup, { member, apiPath: paths.api, compact: true }),
180
+ /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", { className: "toy-button", href: next, children: "Continue to the listing form" }) })
181
+ ] });
182
+ else if (edit && !initial) form = /* @__PURE__ */ jsx("p", { children: "This listing is unavailable. Open it from My toys." });
183
+ else {
184
+ const home = member.community && rules.areas.some((a) => a.id === member.community.id) ? member.community.id : void 0;
185
+ form = /* @__PURE__ */ jsx(ToyListingForm, { communities: rules.areas, policy: rules.submission_policy, initial, defaultCommunity: rules.areas.find((a) => a.slug === community)?.id || home, apiPath: paths.api, accountUrl: paths.account, listingBasePath: paths.listing });
186
+ }
187
+ } catch (error) {
188
+ form = /* @__PURE__ */ jsxs("section", { className: "toy-form", children: [
189
+ /* @__PURE__ */ jsx("h1", { children: "Give or swap a toy." }),
190
+ /* @__PURE__ */ jsx(LoadError, { error, retry: next, account: paths.account, auth: paths.auth })
191
+ ] });
192
+ }
193
+ return /* @__PURE__ */ jsx(Shell, { paths, signedIn, embedded, children: form });
194
+ }
195
+ async function fetchToyListing(scope, id) {
196
+ if (!TOY_UUID.test(id)) return null;
197
+ try {
198
+ return await toyCollectionRequest({ ...scope, accessToken: void 0 }, "/" + id);
199
+ } catch (error) {
200
+ if (error && typeof error === "object" && "status" in error && error.status === 404) return null;
201
+ throw error;
202
+ }
203
+ }
204
+ async function ToyListingPage({ scope, listing, signedIn, paths = publicationToyPaths, canonicalOrigin, embedded = false }) {
205
+ const c = listing.content;
206
+ let ready = false, own = [];
207
+ if (signedIn)
208
+ try {
209
+ const [member, mine] = await Promise.all([toyCollectionRequest(scope, "/member"), toyCollectionRequest(scope, "/mine")]);
210
+ ready = member.verification.ready && member.status === "active";
211
+ own = mine.items;
212
+ } catch {
213
+ }
214
+ const listingPath = `${paths.listing}/${listing.id}`;
215
+ const mediaUrl = toyMediaUrl(paths.api);
216
+ return /* @__PURE__ */ jsx(Shell, { paths, signedIn, embedded, children: /* @__PURE__ */ jsxs("article", { className: "toy-listing", children: [
217
+ /* @__PURE__ */ jsx("div", { className: "toy-listing-gallery", children: listing.media.map((photo, index) => /* @__PURE__ */ jsx(ToyPhoto, { listing, mediaUrl, large: index === 0, index }, photo.id)) }),
218
+ /* @__PURE__ */ jsxs("div", { className: "toy-listing-copy", children: [
219
+ /* @__PURE__ */ jsxs("p", { className: "toy-context", children: [
220
+ toyOptionLabel(TOY_CATEGORIES, listing.category),
221
+ listing.community ? ` \xB7 ${listing.community}` : ""
222
+ ] }),
223
+ /* @__PURE__ */ jsx("h1", { children: c.title }),
224
+ /* @__PURE__ */ jsx(ToyBadges, { listing }),
225
+ /* @__PURE__ */ jsx("p", { style: { whiteSpace: "pre-wrap" }, children: c.description }),
226
+ /* @__PURE__ */ jsxs("dl", { className: "toy-facts", children: [
227
+ (c.brand || c.model) && /* @__PURE__ */ jsxs(Fragment, { children: [
228
+ /* @__PURE__ */ jsx("dt", { children: "Brand / model" }),
229
+ /* @__PURE__ */ jsx("dd", { children: [c.brand, c.model].filter(Boolean).join(" \xB7 ") })
230
+ ] }),
231
+ /* @__PURE__ */ jsx("dt", { children: "Completeness" }),
232
+ /* @__PURE__ */ jsx("dd", { children: c.completeness === "complete" ? "Complete, per the owner" : c.completeness === "missing_parts" ? `Missing parts: ${c.missing_parts}` : "Not sure, per the owner" }),
233
+ /* @__PURE__ */ jsx("dt", { children: "Condition" }),
234
+ /* @__PURE__ */ jsx("dd", { children: toyOptionLabel(TOY_CONDITIONS, listing.condition) }),
235
+ /* @__PURE__ */ jsx("dt", { children: "Manufacturer age guidance" }),
236
+ /* @__PURE__ */ jsxs("dd", { children: [
237
+ toyOptionLabel(TOY_AGE_RANGES, listing.age_range),
238
+ c.age_guidance ? ` \u2014 "${c.age_guidance}"` : ""
239
+ ] }),
240
+ c.warnings && /* @__PURE__ */ jsxs(Fragment, { children: [
241
+ /* @__PURE__ */ jsx("dt", { children: "Manufacturer warnings" }),
242
+ /* @__PURE__ */ jsx("dd", { children: c.warnings })
243
+ ] }),
244
+ listing.exchange_type === "swap" && c.swap_wishes && /* @__PURE__ */ jsxs(Fragment, { children: [
245
+ /* @__PURE__ */ jsx("dt", { children: "Hoping to swap for" }),
246
+ /* @__PURE__ */ jsx("dd", { children: c.swap_wishes })
247
+ ] })
248
+ ] }),
249
+ /* @__PURE__ */ jsxs("aside", { className: "toy-recall-statement", children: [
250
+ /* @__PURE__ */ jsx("h2", { children: "Recall check \u2014 the owner's statement" }),
251
+ /* @__PURE__ */ jsxs("p", { children: [
252
+ c.recall_check.searched_cpsc ? /* @__PURE__ */ jsxs(Fragment, { children: [
253
+ "The owner reports searching the CPSC recall database",
254
+ c.recall_check.searched_at ? ` on ${new Date(c.recall_check.searched_at).toLocaleDateString()}` : "",
255
+ c.recall_check.search_terms ? ` for "${c.recall_check.search_terms}"` : "",
256
+ " and ",
257
+ c.recall_check.result === "no_match_found" ? "found no recall" : "was not sure",
258
+ "."
259
+ ] }) : "No recall search was reported.",
260
+ " ",
261
+ "An editor reviewed this listing before publication. ",
262
+ TOY_SAFETY.recall
263
+ ] }),
264
+ /* @__PURE__ */ jsx("p", { className: "toy-note", children: TOY_SAFETY.children })
265
+ ] }),
266
+ listing.owner && /* @__PURE__ */ jsxs("aside", { className: "toy-owner-card", children: [
267
+ /* @__PURE__ */ jsx("p", { className: "toy-context", children: "Offered by" }),
268
+ /* @__PURE__ */ jsx("h2", { children: /* @__PURE__ */ jsx("a", { href: `${paths.profiles}/${listing.owner.id}`, children: listing.owner.display_name }) }),
269
+ /* @__PURE__ */ jsxs("p", { children: [
270
+ listing.owner.community?.name ? `${listing.owner.community.name} \xB7 ` : "",
271
+ "member since ",
272
+ listing.owner.member_since,
273
+ " \xB7 ",
274
+ listing.owner.completed_exchanges,
275
+ " completed exchange",
276
+ listing.owner.completed_exchanges === 1 ? "" : "s"
277
+ ] }),
278
+ /* @__PURE__ */ jsx("small", { children: "Completed exchanges are participant-confirmed counts, not a rating." })
279
+ ] }),
280
+ /* @__PURE__ */ jsx(ToyRequestPanel, { listing, signedIn, ready, ownListings: own, apiPath: paths.api, accountUrl: paths.account, exchangeBasePath: paths.exchanges, listingPath, locationsAvailable: listing.exchange_locations_available !== false }),
281
+ /* @__PURE__ */ jsx("p", { className: "toy-safety", children: TOY_SAFETY.meeting }),
282
+ /* @__PURE__ */ jsx(ToyListingActions, { listing, signedIn, apiPath: paths.api, accountUrl: paths.account, listingPath }),
283
+ /* @__PURE__ */ jsxs("p", { children: [
284
+ /* @__PURE__ */ jsx("a", { href: canonicalOrigin + "/toys/" + listing.id, children: "Permanent link" }),
285
+ " \xB7 ",
286
+ /* @__PURE__ */ jsx("a", { href: paths.home, children: "More toys" })
287
+ ] })
288
+ ] })
289
+ ] }) });
290
+ }
291
+ async function ToyProfilePage({ scope, id, paths = publicationToyPaths, signedIn, embedded = false }) {
292
+ if (!TOY_UUID.test(id)) return null;
293
+ let profile = null;
294
+ try {
295
+ profile = await toyCollectionRequest({ ...scope, accessToken: void 0 }, "/profiles/" + id);
296
+ } catch (error) {
297
+ if (!(error && typeof error === "object" && "status" in error && error.status === 404)) throw error;
298
+ }
299
+ if (!profile) return null;
300
+ return /* @__PURE__ */ jsx(Shell, { paths, signedIn, embedded, children: /* @__PURE__ */ jsxs("section", { className: "toy-story", children: [
301
+ /* @__PURE__ */ jsx("p", { className: "toy-eyebrow", children: "Neighbor" }),
302
+ /* @__PURE__ */ jsx("h1", { children: profile.display_name }),
303
+ /* @__PURE__ */ jsxs("p", { children: [
304
+ profile.community?.name ? `${profile.community.name} \xB7 ` : "",
305
+ "member since ",
306
+ profile.member_since,
307
+ " \xB7 ",
308
+ profile.completed_exchanges,
309
+ " completed exchange",
310
+ profile.completed_exchanges === 1 ? "" : "s",
311
+ " (participant-confirmed)"
312
+ ] }),
313
+ /* @__PURE__ */ jsx("div", { className: "toy-grid", children: profile.listings.map((listing) => /* @__PURE__ */ jsx(ToyListingCard, { listing, href: `${paths.listing}/${listing.id}`, mediaUrl: toyMediaUrl(paths.api) }, listing.id)) }),
314
+ !profile.listings.length && /* @__PURE__ */ jsx("p", { className: "toy-note", children: "No available listings right now." })
315
+ ] }) });
316
+ }
317
+ async function ToyExchangePage({ scope, signedIn, id, paths = publicationToyPaths, embedded = false }) {
318
+ let body;
319
+ if (!signedIn) body = /* @__PURE__ */ jsx(ToySignIn, { returnTo: `${paths.exchanges}/${id}`, authPath: paths.auth });
320
+ else if (!TOY_UUID.test(id)) body = /* @__PURE__ */ jsx("p", { children: "This exchange is unavailable." });
321
+ else
322
+ try {
323
+ const thread = await toyCollectionRequest(scope, "/requests/" + id);
324
+ body = /* @__PURE__ */ jsx(ToyExchangeThread, { thread, apiPath: paths.api, listingBasePath: paths.listing, locationsUrl: paths.locations });
325
+ } catch (error) {
326
+ body = error && typeof error === "object" && "status" in error && error.status === 404 ? /* @__PURE__ */ jsx("p", { children: "This exchange is unavailable." }) : /* @__PURE__ */ jsx(LoadError, { error, retry: `${paths.exchanges}/${id}`, account: paths.account, auth: paths.auth });
327
+ }
328
+ return /* @__PURE__ */ jsx(Shell, { paths, signedIn, embedded, children: /* @__PURE__ */ jsxs("section", { className: "toy-story", children: [
329
+ /* @__PURE__ */ jsxs("p", { className: "toy-eyebrow", children: [
330
+ /* @__PURE__ */ jsx("a", { href: paths.account, children: "My toys" }),
331
+ " \xB7 Exchange"
332
+ ] }),
333
+ body
334
+ ] }) });
335
+ }
336
+ async function ToyLocationsPage({ scope, community, signedIn, paths = publicationToyPaths, embedded = false }) {
337
+ let body;
338
+ try {
339
+ const rules = await policy(scope);
340
+ const chosen = resolveToyCommunity(rules.areas, community);
341
+ const places = await toyCollectionRequest({ ...scope, accessToken: void 0 }, "/locations" + (chosen ? "?area_id=" + chosen.id : ""));
342
+ body = /* @__PURE__ */ jsxs(Fragment, { children: [
343
+ /* @__PURE__ */ jsxs("form", { method: "get", className: "toy-filters", children: [
344
+ /* @__PURE__ */ jsxs("label", { children: [
345
+ "Community",
346
+ /* @__PURE__ */ jsxs("select", { name: "community", defaultValue: chosen?.slug || "", children: [
347
+ /* @__PURE__ */ jsx("option", { value: "", children: "All communities" }),
348
+ rules.areas.map((a) => /* @__PURE__ */ jsx("option", { value: a.slug, children: a.name }, a.id))
349
+ ] })
350
+ ] }),
351
+ /* @__PURE__ */ jsx("button", { type: "submit", children: "Show places" })
352
+ ] }),
353
+ /* @__PURE__ */ jsx("p", { className: "toy-safety", children: places.note }),
354
+ places.items.length ? /* @__PURE__ */ jsx("div", { className: "toy-grid", children: places.items.map((location) => /* @__PURE__ */ jsx(ToyLocationCard, { location }, location.id)) }) : /* @__PURE__ */ jsxs("p", { className: "toy-note", children: [
355
+ "No checked meeting places are listed ",
356
+ chosen ? `for ${chosen.name}` : "yet",
357
+ ". Exchanges can't be scheduled until an editor adds one. You can suggest a public place below."
358
+ ] }),
359
+ signedIn ? /* @__PURE__ */ jsx(ToyLocationSuggestForm, { communities: rules.areas, apiPath: paths.api, defaultCommunity: chosen?.id }) : /* @__PURE__ */ jsx("p", { className: "toy-note", children: "Sign in to suggest a public meeting place for editorial review." })
360
+ ] });
361
+ } catch (error) {
362
+ body = /* @__PURE__ */ jsx(LoadError, { error, retry: paths.locations, account: paths.account, auth: paths.auth });
363
+ }
364
+ return /* @__PURE__ */ jsx(Shell, { paths, signedIn, embedded, children: /* @__PURE__ */ jsxs("section", { className: "toy-story", children: [
365
+ /* @__PURE__ */ jsx("p", { className: "toy-eyebrow", children: "Where exchanges happen" }),
366
+ /* @__PURE__ */ jsx("h1", { children: "Public meeting places" }),
367
+ /* @__PURE__ */ jsx("p", { children: "Every place here was checked by an editor against a public source. Listed hours and any facts about lighting, cameras or staffing come from that source and are not guarantees." }),
368
+ body
369
+ ] }) });
370
+ }
371
+ function ToyRulesPage({ paths = publicationToyPaths, signedIn = false, embedded = false }) {
372
+ return /* @__PURE__ */ jsx(Shell, { paths, signedIn, embedded, children: /* @__PURE__ */ jsxs("section", { className: "toy-story", children: [
373
+ /* @__PURE__ */ jsx("p", { className: "toy-eyebrow", children: "ToyRound" }),
374
+ /* @__PURE__ */ jsx("h1", { children: "Rules, prohibited items, privacy & safety" }),
375
+ /* @__PURE__ */ jsx(ToyRulesContent, {})
376
+ ] }) });
377
+ }
378
+
379
+ export { Shell, ToyAccountPage, ToyExchangePage, ToyListingPage, ToyLocationsPage, ToyProfilePage, ToyRulesPage, ToySubmitPage, fetchToyListing, publicationToyPaths, standaloneToyPaths, toyMediaUrl, toyReaderAuthenticated };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { getCookieConsent, DEFAULT_PREFERENCES_ACCEPTED, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_REJECTED } from './chunk-6S5FARL3.js';
2
+ import { effectivePreferences, getCookieConsent, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED } from './chunk-XWXPERLK.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -10,8 +10,7 @@ function CookieSettingsView({
10
10
  publicationSlug
11
11
  }) {
12
12
  const [preferences, setPreferences] = useState(() => {
13
- const existing = getCookieConsent();
14
- return existing ? existing.preferences : { ...DEFAULT_PREFERENCES_ACCEPTED };
13
+ return { ...effectivePreferences() };
15
14
  });
16
15
  const [expandedCategories, setExpandedCategories] = useState({
17
16
  essential: false,
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { hasConsented, rejectOptionalCookies, DEFAULT_CONSENT_HUB_URL, isGpcActive, COOKIE_CONSENT_EVENT, acceptAllCookies, recordConsentToCompliance } from './chunk-6S5FARL3.js';
2
+ import { hasConsented, rejectOptionalCookies, DEFAULT_CONSENT_HUB_URL, consentModel, isGpcActive, COOKIE_CONSENT_EVENT, acceptAllCookies, recordConsentToCompliance } from './chunk-XWXPERLK.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -18,6 +18,12 @@ function CookieConsentBanner({
18
18
  const [visible, setVisible] = useState(false);
19
19
  useEffect(() => {
20
20
  setMounted(true);
21
+ if (countryCode && /^[A-Za-z]{2}$/.test(countryCode)) {
22
+ try {
23
+ document.cookie = `sm_geo_country=${countryCode.toUpperCase()}; Path=/; SameSite=Lax`;
24
+ } catch {
25
+ }
26
+ }
21
27
  const url = new URL(window.location.href);
22
28
  if (url.searchParams.has("sm_consent")) {
23
29
  if (url.searchParams.get("sm_consent") === "0" && !hasConsented())
@@ -25,11 +31,12 @@ function CookieConsentBanner({
25
31
  url.searchParams.delete("sm_consent");
26
32
  window.history.replaceState(window.history.state, "", url);
27
33
  }
28
- setVisible(!hasConsented() && !isGpcActive());
34
+ const asks = forceShow || consentModel(countryCode) === "opt-in";
35
+ setVisible(asks && !hasConsented() && !isGpcActive());
29
36
  }, [countryCode, forceShow, consentHubUrl]);
30
37
  useEffect(() => {
31
38
  const onConsentChange = () => {
32
- setVisible(!hasConsented() && !isGpcActive());
39
+ setVisible((visible2) => visible2 && !hasConsented() && !isGpcActive());
33
40
  };
34
41
  window.addEventListener(COOKIE_CONSENT_EVENT, onConsentChange);
35
42
  return () => window.removeEventListener(COOKIE_CONSENT_EVENT, onConsentChange);
@@ -52,7 +59,7 @@ function CookieConsentBanner({
52
59
  });
53
60
  };
54
61
  const defaultText = /* @__PURE__ */ jsxs(Fragment, { children: [
55
- "With your permission, we measure reading activity and sponsor acknowledgments. You can read the publication with optional cookies turned off. View",
62
+ "We count page views without cookies or identifiers. With your permission, first-party cookies also let us measure reading sessions and sponsor acknowledgments. You can read the publication with optional cookies turned off. View",
56
63
  " ",
57
64
  /* @__PURE__ */ jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
58
65
  " ",
@@ -1,5 +1,5 @@
1
1
  import { recordConsentToCompliance as recordConsentToCompliance$1, getCookieConsent, decorateNetworkUrl } from '@scalemule/compliance';
2
- export { COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_CONSENT_HUB_URL, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, NETWORK_CONSENT_STORAGE_KEY, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, decorateNetworkUrl, getCookieConsent, hasConsented, isCategoryAllowed, isGeoConsentRequired, isGpcActive, queryNetworkConsentHub, rejectOptionalCookies, setCookieConsent, setNetworkConsentHub } from '@scalemule/compliance';
2
+ export { COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_CONSENT_HUB_URL, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, NETWORK_CONSENT_STORAGE_KEY, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, consentModel, decorateNetworkUrl, effectivePreferences, getCookieConsent, hasConsented, impliedPreferences, isCategoryAllowed, isGeoConsentRequired, isGpcActive, queryNetworkConsentHub, rejectOptionalCookies, setCookieConsent, setNetworkConsentHub } from '@scalemule/compliance';
3
3
 
4
4
  // src/cookies.ts
5
5
  async function recordConsentToCompliance(record, options) {
@@ -61,7 +61,7 @@ var COOKIE_CATEGORIES = [
61
61
  id: "performance",
62
62
  title: "Performance & Analytics Cookies",
63
63
  shortDescription: "Help us measure reader engagement, story completion, and site reliability.",
64
- fullDescription: "With your permission, first-party identifiers support page-visit and reading measurements. Aggregate reports help us improve the publication. Measurements are incomplete when readers decline consent and are not a count of unique people.",
64
+ fullDescription: "We always count page views and reading activity without cookies or any identifier stored on your device: the server derives a daily-rotating, non-reversible hash that cannot be linked back to you, and your IP address is truncated before it is stored. With these cookies turned on, first-party identifiers additionally let us recognise a return visit and measure sessions. In the United States and most other regions they are on by default and you can turn them off here; in the EU, EEA, UK and Switzerland they stay off until you accept them.",
65
65
  required: false,
66
66
  cookies: [
67
67
  {
@@ -157,6 +157,12 @@ function CookieConsentBanner({
157
157
  const [visible, setVisible] = react.useState(false);
158
158
  react.useEffect(() => {
159
159
  setMounted(true);
160
+ if (countryCode && /^[A-Za-z]{2}$/.test(countryCode)) {
161
+ try {
162
+ document.cookie = `sm_geo_country=${countryCode.toUpperCase()}; Path=/; SameSite=Lax`;
163
+ } catch {
164
+ }
165
+ }
160
166
  const url = new URL(window.location.href);
161
167
  if (url.searchParams.has("sm_consent")) {
162
168
  if (url.searchParams.get("sm_consent") === "0" && !compliance.hasConsented())
@@ -164,11 +170,12 @@ function CookieConsentBanner({
164
170
  url.searchParams.delete("sm_consent");
165
171
  window.history.replaceState(window.history.state, "", url);
166
172
  }
167
- setVisible(!compliance.hasConsented() && !compliance.isGpcActive());
173
+ const asks = forceShow || compliance.consentModel(countryCode) === "opt-in";
174
+ setVisible(asks && !compliance.hasConsented() && !compliance.isGpcActive());
168
175
  }, [countryCode, forceShow, consentHubUrl]);
169
176
  react.useEffect(() => {
170
177
  const onConsentChange = () => {
171
- setVisible(!compliance.hasConsented() && !compliance.isGpcActive());
178
+ setVisible((visible2) => visible2 && !compliance.hasConsented() && !compliance.isGpcActive());
172
179
  };
173
180
  window.addEventListener(compliance.COOKIE_CONSENT_EVENT, onConsentChange);
174
181
  return () => window.removeEventListener(compliance.COOKIE_CONSENT_EVENT, onConsentChange);
@@ -191,7 +198,7 @@ function CookieConsentBanner({
191
198
  });
192
199
  };
193
200
  const defaultText = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
194
- "With your permission, we measure reading activity and sponsor acknowledgments. You can read the publication with optional cookies turned off. View",
201
+ "We count page views without cookies or identifiers. With your permission, first-party cookies also let us measure reading sessions and sponsor acknowledgments. You can read the publication with optional cookies turned off. View",
195
202
  " ",
196
203
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
197
204
  " ",
@@ -15,7 +15,7 @@ interface CookieConsentBannerProps {
15
15
  complianceEndpoint?: string;
16
16
  /** Explicit ISO 3166-1 alpha-2 country code (e.g. from server headers or GeoIP) */
17
17
  countryCode?: string | null;
18
- /** Force display of banner regardless of visitor geography */
18
+ /** Force display of banner regardless of visitor geography (opt-out regions show no banner by default) */
19
19
  forceShow?: boolean;
20
20
  /** URL of the central network consent iframe bridge. Default: "https://bayareachronicle.com/consent-bridge.html" */
21
21
  consentHubUrl?: string;
@@ -15,7 +15,7 @@ interface CookieConsentBannerProps {
15
15
  complianceEndpoint?: string;
16
16
  /** Explicit ISO 3166-1 alpha-2 country code (e.g. from server headers or GeoIP) */
17
17
  countryCode?: string | null;
18
- /** Force display of banner regardless of visitor geography */
18
+ /** Force display of banner regardless of visitor geography (opt-out regions show no banner by default) */
19
19
  forceShow?: boolean;
20
20
  /** URL of the central network consent iframe bridge. Default: "https://bayareachronicle.com/consent-bridge.html" */
21
21
  consentHubUrl?: string;
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieConsentBanner } from './chunk-XPOZGZTM.js';
3
- import './chunk-6S5FARL3.js';
2
+ export { CookieConsentBanner } from './chunk-XSAVJJVW.js';
3
+ import './chunk-XWXPERLK.js';
@@ -170,7 +170,7 @@ var COOKIE_CATEGORIES = [
170
170
  id: "performance",
171
171
  title: "Performance & Analytics Cookies",
172
172
  shortDescription: "Help us measure reader engagement, story completion, and site reliability.",
173
- fullDescription: "With your permission, first-party identifiers support page-visit and reading measurements. Aggregate reports help us improve the publication. Measurements are incomplete when readers decline consent and are not a count of unique people.",
173
+ fullDescription: "We always count page views and reading activity without cookies or any identifier stored on your device: the server derives a daily-rotating, non-reversible hash that cannot be linked back to you, and your IP address is truncated before it is stored. With these cookies turned on, first-party identifiers additionally let us recognise a return visit and measure sessions. In the United States and most other regions they are on by default and you can turn them off here; in the EU, EEA, UK and Switzerland they stay off until you accept them.",
174
174
  required: false,
175
175
  cookies: [
176
176
  {
@@ -242,8 +242,7 @@ function CookieSettingsView({
242
242
  publicationSlug
243
243
  }) {
244
244
  const [preferences, setPreferences] = react.useState(() => {
245
- const existing = compliance.getCookieConsent();
246
- return existing ? existing.preferences : { ...compliance.DEFAULT_PREFERENCES_ACCEPTED };
245
+ return { ...compliance.effectivePreferences() };
247
246
  });
248
247
  const [expandedCategories, setExpandedCategories] = react.useState({
249
248
  essential: false,
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieSettingsView } from './chunk-GYYUJYI4.js';
3
- import './chunk-6S5FARL3.js';
2
+ export { CookieSettingsView } from './chunk-X7AU7DV6.js';
3
+ import './chunk-XWXPERLK.js';