@scalemule/news-publication 0.0.39 → 0.0.41
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/README.md +23 -0
- package/dist/community-maps-server.cjs +70 -11
- package/dist/community-maps-server.js +70 -11
- package/dist/community-maps-view.cjs +95 -46
- package/dist/community-maps-view.js +96 -47
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -153,3 +153,26 @@ Article integration uses the existing Blog metadata extension:
|
|
|
153
153
|
Other modes are `cards` and `features`; optional `area_id` / `feature_ids` select live
|
|
154
154
|
records. No copied feature snapshot is embedded in a story, so withdrawal takes effect
|
|
155
155
|
on the next read. Publishers should apply their caption policy to public media copy.
|
|
156
|
+
|
|
157
|
+
### Votes and moderated conversations
|
|
158
|
+
|
|
159
|
+
Set `config.voting_enabled` and `config.comments_enabled` on the canonical collection
|
|
160
|
+
in the Community Maps editor to enable these controls. Both default to off for
|
|
161
|
+
existing collections. Views share one place record, score, and discussion.
|
|
162
|
+
|
|
163
|
+
The server resolver should include the signed-in reader's `accessToken` from its
|
|
164
|
+
HttpOnly session cookie. Keep the publication API key on the server. Voting
|
|
165
|
+
requires a signed-in reader; guests can read totals and submit comments for review.
|
|
166
|
+
Each reader can upvote (`1`), downvote (`-1`), or clear (`0`) their place vote.
|
|
167
|
+
|
|
168
|
+
The public proxy allows `features/:id/vote` and `comments?feature_id=:id` under
|
|
169
|
+
`collections/:slug`. Without `feature_id`, comments belong to the guide. It strips
|
|
170
|
+
editorial query flags and uses the same signed anonymous identity and origin
|
|
171
|
+
checks as submissions. Comments and replies require the current publication
|
|
172
|
+
permission and always enter review, including comments from signed-in readers.
|
|
173
|
+
Only approved comments whose ancestors remain approved are public.
|
|
174
|
+
|
|
175
|
+
Moderate through **Newsroom → Community Maps → Comments**. Approval requires
|
|
176
|
+
explicit guideline review and the current revision. Existing pumpkin reactions
|
|
177
|
+
remain configurable alongside voting. The UI links to `/account?next=...`; the
|
|
178
|
+
publication must validate that return path and preserve it through sign-in.
|
|
@@ -69,11 +69,21 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
69
69
|
const slug = path[1];
|
|
70
70
|
const rest = path.slice(2);
|
|
71
71
|
const method = request.method;
|
|
72
|
-
const
|
|
72
|
+
const discussion = rest.length === 1 && rest[0] === "comments";
|
|
73
|
+
const voting = rest.length === 3 && rest[0] === "features" && UUID.test(rest[1]) && rest[2] === "vote";
|
|
74
|
+
const params = new URL(request.url).searchParams;
|
|
75
|
+
const featureId = params.get("feature_id") || void 0;
|
|
76
|
+
if (discussion && featureId && !UUID.test(featureId))
|
|
77
|
+
return json({ error: { message: "Invalid place" } }, 400);
|
|
78
|
+
const ownList = rest.length === 1 && rest[0] === "submissions";
|
|
79
|
+
const ownUpdate = method === "POST" && rest.length === 2 && rest[0] === "submissions" && UUID.test(rest[1]);
|
|
80
|
+
const ownMedia = rest.length === 4 && rest[0] === "submissions" && UUID.test(rest[1]) && rest[2] === "media" && UUID.test(rest[3]);
|
|
81
|
+
const read = method === "GET" && (ownList || ownMedia || discussion || voting || rest.length === 0 || rest[0] === "features" && (rest.length === 1 || rest.length === 2 && UUID.test(rest[1])) || rest[0] === "media" && rest.length === 2 && UUID.test(rest[1]));
|
|
73
82
|
const upload = method === "POST" && rest.length === 3 && rest[0] === "submissions" && UUID.test(rest[1]) && rest[2] === "media";
|
|
74
83
|
const submission = method === "POST" && rest.length === 1 && rest[0] === "submissions";
|
|
75
84
|
const action = method === "POST" && rest.length === 3 && rest[0] === "features" && UUID.test(rest[1]) && ["reactions", "reports"].includes(rest[2]);
|
|
76
|
-
|
|
85
|
+
const engagementWrite = method === "POST" && (discussion || voting);
|
|
86
|
+
if (!read && !upload && !submission && !ownUpdate && !action && !engagementWrite)
|
|
77
87
|
return json({ error: { message: "Route not found" } }, 404);
|
|
78
88
|
const scope = await options.resolve(request);
|
|
79
89
|
if (method !== "GET" && request.headers.get("origin") !== new URL(scope.origin).origin)
|
|
@@ -97,9 +107,9 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
97
107
|
headers: scope.accessToken ? { authorization: `Bearer ${scope.accessToken}` } : {}
|
|
98
108
|
};
|
|
99
109
|
let response;
|
|
100
|
-
if (read && rest[0] === "media") {
|
|
110
|
+
if (read && (rest[0] === "media" || ownMedia)) {
|
|
101
111
|
const upstream = await fetch(
|
|
102
|
-
`${gateway.origin}${ROOT}/collections/${slug}/media/${rest[1]}`,
|
|
112
|
+
`${gateway.origin}${ROOT}/collections/${slug}/${ownMedia ? `submissions/${rest[1]}/media/${rest[3]}` : `media/${rest[1]}`}`,
|
|
103
113
|
{
|
|
104
114
|
headers: {
|
|
105
115
|
...sdk.buildClientContextHeaders(clientContext),
|
|
@@ -150,7 +160,34 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
150
160
|
} else {
|
|
151
161
|
let result;
|
|
152
162
|
if (read) {
|
|
153
|
-
if (
|
|
163
|
+
if (ownList) {
|
|
164
|
+
const after = params.get("after") || void 0;
|
|
165
|
+
if (after && !UUID.test(after))
|
|
166
|
+
throw new ProxyProblem(400, "Invalid submission cursor.");
|
|
167
|
+
result = await sdk$1.communityMaps.listOwnSubmissions(
|
|
168
|
+
slug,
|
|
169
|
+
after,
|
|
170
|
+
callOptions
|
|
171
|
+
);
|
|
172
|
+
} else if (discussion)
|
|
173
|
+
result = await sdk$1.communityMaps.listComments(
|
|
174
|
+
slug,
|
|
175
|
+
{
|
|
176
|
+
feature_id: featureId,
|
|
177
|
+
page: Math.min(
|
|
178
|
+
1e4,
|
|
179
|
+
Math.max(1, Number(params.get("page")) || 1)
|
|
180
|
+
)
|
|
181
|
+
},
|
|
182
|
+
callOptions
|
|
183
|
+
);
|
|
184
|
+
else if (voting)
|
|
185
|
+
result = await sdk$1.communityMaps.getVote(
|
|
186
|
+
slug,
|
|
187
|
+
rest[1],
|
|
188
|
+
callOptions
|
|
189
|
+
);
|
|
190
|
+
else if (!rest.length)
|
|
154
191
|
result = await sdk$1.communityMaps.getCollection(slug, callOptions);
|
|
155
192
|
else if (rest.length === 2)
|
|
156
193
|
result = await sdk$1.communityMaps.getFeature(
|
|
@@ -160,7 +197,7 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
160
197
|
);
|
|
161
198
|
else {
|
|
162
199
|
const query = {};
|
|
163
|
-
const
|
|
200
|
+
const params2 = new URL(request.url).searchParams;
|
|
164
201
|
for (const key of [
|
|
165
202
|
"after",
|
|
166
203
|
"area_id",
|
|
@@ -169,15 +206,15 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
169
206
|
"active_at",
|
|
170
207
|
"bbox"
|
|
171
208
|
]) {
|
|
172
|
-
const value =
|
|
209
|
+
const value = params2.get(key);
|
|
173
210
|
if (value) query[key] = value;
|
|
174
211
|
}
|
|
175
|
-
const limit =
|
|
212
|
+
const limit = params2.get("limit");
|
|
176
213
|
if (limit)
|
|
177
214
|
query.limit = Math.min(200, Math.max(1, Number(limit) || 50));
|
|
178
215
|
const filters = { ...query };
|
|
179
216
|
for (const key of ["featured", "verified"]) {
|
|
180
|
-
const value =
|
|
217
|
+
const value = params2.get(key);
|
|
181
218
|
if (value === "true" || value === "false")
|
|
182
219
|
filters[key] = value === "true";
|
|
183
220
|
}
|
|
@@ -206,7 +243,8 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
206
243
|
caption: String(form.get("caption") || ""),
|
|
207
244
|
attribution: String(form.get("attribution") || ""),
|
|
208
245
|
rights_confirmed: form.get("rights_confirmed") === "true",
|
|
209
|
-
consent_version: String(form.get("consent_version") || "")
|
|
246
|
+
consent_version: String(form.get("consent_version") || ""),
|
|
247
|
+
...form.has("idempotency_key") ? { idempotency_key: String(form.get("idempotency_key")) } : {}
|
|
210
248
|
},
|
|
211
249
|
callOptions
|
|
212
250
|
);
|
|
@@ -224,7 +262,28 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
224
262
|
}
|
|
225
263
|
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
226
264
|
throw new ProxyProblem(400, "Invalid submission.");
|
|
227
|
-
if (
|
|
265
|
+
if (discussion)
|
|
266
|
+
result = await sdk$1.communityMaps.createComment(
|
|
267
|
+
slug,
|
|
268
|
+
body,
|
|
269
|
+
featureId,
|
|
270
|
+
callOptions
|
|
271
|
+
);
|
|
272
|
+
else if (voting)
|
|
273
|
+
result = await sdk$1.communityMaps.vote(
|
|
274
|
+
slug,
|
|
275
|
+
rest[1],
|
|
276
|
+
body.value,
|
|
277
|
+
callOptions
|
|
278
|
+
);
|
|
279
|
+
else if (ownUpdate)
|
|
280
|
+
result = await sdk$1.communityMaps.updateOwnSubmission(
|
|
281
|
+
slug,
|
|
282
|
+
rest[1],
|
|
283
|
+
body,
|
|
284
|
+
callOptions
|
|
285
|
+
);
|
|
286
|
+
else if (submission)
|
|
228
287
|
result = await sdk$1.communityMaps.createSubmission(
|
|
229
288
|
slug,
|
|
230
289
|
body,
|
|
@@ -67,11 +67,21 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
67
67
|
const slug = path[1];
|
|
68
68
|
const rest = path.slice(2);
|
|
69
69
|
const method = request.method;
|
|
70
|
-
const
|
|
70
|
+
const discussion = rest.length === 1 && rest[0] === "comments";
|
|
71
|
+
const voting = rest.length === 3 && rest[0] === "features" && UUID.test(rest[1]) && rest[2] === "vote";
|
|
72
|
+
const params = new URL(request.url).searchParams;
|
|
73
|
+
const featureId = params.get("feature_id") || void 0;
|
|
74
|
+
if (discussion && featureId && !UUID.test(featureId))
|
|
75
|
+
return json({ error: { message: "Invalid place" } }, 400);
|
|
76
|
+
const ownList = rest.length === 1 && rest[0] === "submissions";
|
|
77
|
+
const ownUpdate = method === "POST" && rest.length === 2 && rest[0] === "submissions" && UUID.test(rest[1]);
|
|
78
|
+
const ownMedia = rest.length === 4 && rest[0] === "submissions" && UUID.test(rest[1]) && rest[2] === "media" && UUID.test(rest[3]);
|
|
79
|
+
const read = method === "GET" && (ownList || ownMedia || discussion || voting || rest.length === 0 || rest[0] === "features" && (rest.length === 1 || rest.length === 2 && UUID.test(rest[1])) || rest[0] === "media" && rest.length === 2 && UUID.test(rest[1]));
|
|
71
80
|
const upload = method === "POST" && rest.length === 3 && rest[0] === "submissions" && UUID.test(rest[1]) && rest[2] === "media";
|
|
72
81
|
const submission = method === "POST" && rest.length === 1 && rest[0] === "submissions";
|
|
73
82
|
const action = method === "POST" && rest.length === 3 && rest[0] === "features" && UUID.test(rest[1]) && ["reactions", "reports"].includes(rest[2]);
|
|
74
|
-
|
|
83
|
+
const engagementWrite = method === "POST" && (discussion || voting);
|
|
84
|
+
if (!read && !upload && !submission && !ownUpdate && !action && !engagementWrite)
|
|
75
85
|
return json({ error: { message: "Route not found" } }, 404);
|
|
76
86
|
const scope = await options.resolve(request);
|
|
77
87
|
if (method !== "GET" && request.headers.get("origin") !== new URL(scope.origin).origin)
|
|
@@ -95,9 +105,9 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
95
105
|
headers: scope.accessToken ? { authorization: `Bearer ${scope.accessToken}` } : {}
|
|
96
106
|
};
|
|
97
107
|
let response;
|
|
98
|
-
if (read && rest[0] === "media") {
|
|
108
|
+
if (read && (rest[0] === "media" || ownMedia)) {
|
|
99
109
|
const upstream = await fetch(
|
|
100
|
-
`${gateway.origin}${ROOT}/collections/${slug}/media/${rest[1]}`,
|
|
110
|
+
`${gateway.origin}${ROOT}/collections/${slug}/${ownMedia ? `submissions/${rest[1]}/media/${rest[3]}` : `media/${rest[1]}`}`,
|
|
101
111
|
{
|
|
102
112
|
headers: {
|
|
103
113
|
...buildClientContextHeaders(clientContext),
|
|
@@ -148,7 +158,34 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
148
158
|
} else {
|
|
149
159
|
let result;
|
|
150
160
|
if (read) {
|
|
151
|
-
if (
|
|
161
|
+
if (ownList) {
|
|
162
|
+
const after = params.get("after") || void 0;
|
|
163
|
+
if (after && !UUID.test(after))
|
|
164
|
+
throw new ProxyProblem(400, "Invalid submission cursor.");
|
|
165
|
+
result = await sdk.communityMaps.listOwnSubmissions(
|
|
166
|
+
slug,
|
|
167
|
+
after,
|
|
168
|
+
callOptions
|
|
169
|
+
);
|
|
170
|
+
} else if (discussion)
|
|
171
|
+
result = await sdk.communityMaps.listComments(
|
|
172
|
+
slug,
|
|
173
|
+
{
|
|
174
|
+
feature_id: featureId,
|
|
175
|
+
page: Math.min(
|
|
176
|
+
1e4,
|
|
177
|
+
Math.max(1, Number(params.get("page")) || 1)
|
|
178
|
+
)
|
|
179
|
+
},
|
|
180
|
+
callOptions
|
|
181
|
+
);
|
|
182
|
+
else if (voting)
|
|
183
|
+
result = await sdk.communityMaps.getVote(
|
|
184
|
+
slug,
|
|
185
|
+
rest[1],
|
|
186
|
+
callOptions
|
|
187
|
+
);
|
|
188
|
+
else if (!rest.length)
|
|
152
189
|
result = await sdk.communityMaps.getCollection(slug, callOptions);
|
|
153
190
|
else if (rest.length === 2)
|
|
154
191
|
result = await sdk.communityMaps.getFeature(
|
|
@@ -158,7 +195,7 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
158
195
|
);
|
|
159
196
|
else {
|
|
160
197
|
const query = {};
|
|
161
|
-
const
|
|
198
|
+
const params2 = new URL(request.url).searchParams;
|
|
162
199
|
for (const key of [
|
|
163
200
|
"after",
|
|
164
201
|
"area_id",
|
|
@@ -167,15 +204,15 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
167
204
|
"active_at",
|
|
168
205
|
"bbox"
|
|
169
206
|
]) {
|
|
170
|
-
const value =
|
|
207
|
+
const value = params2.get(key);
|
|
171
208
|
if (value) query[key] = value;
|
|
172
209
|
}
|
|
173
|
-
const limit =
|
|
210
|
+
const limit = params2.get("limit");
|
|
174
211
|
if (limit)
|
|
175
212
|
query.limit = Math.min(200, Math.max(1, Number(limit) || 50));
|
|
176
213
|
const filters = { ...query };
|
|
177
214
|
for (const key of ["featured", "verified"]) {
|
|
178
|
-
const value =
|
|
215
|
+
const value = params2.get(key);
|
|
179
216
|
if (value === "true" || value === "false")
|
|
180
217
|
filters[key] = value === "true";
|
|
181
218
|
}
|
|
@@ -204,7 +241,8 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
204
241
|
caption: String(form.get("caption") || ""),
|
|
205
242
|
attribution: String(form.get("attribution") || ""),
|
|
206
243
|
rights_confirmed: form.get("rights_confirmed") === "true",
|
|
207
|
-
consent_version: String(form.get("consent_version") || "")
|
|
244
|
+
consent_version: String(form.get("consent_version") || ""),
|
|
245
|
+
...form.has("idempotency_key") ? { idempotency_key: String(form.get("idempotency_key")) } : {}
|
|
208
246
|
},
|
|
209
247
|
callOptions
|
|
210
248
|
);
|
|
@@ -222,7 +260,28 @@ function createPublicationCommunityMapsHandler(options) {
|
|
|
222
260
|
}
|
|
223
261
|
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
224
262
|
throw new ProxyProblem(400, "Invalid submission.");
|
|
225
|
-
if (
|
|
263
|
+
if (discussion)
|
|
264
|
+
result = await sdk.communityMaps.createComment(
|
|
265
|
+
slug,
|
|
266
|
+
body,
|
|
267
|
+
featureId,
|
|
268
|
+
callOptions
|
|
269
|
+
);
|
|
270
|
+
else if (voting)
|
|
271
|
+
result = await sdk.communityMaps.vote(
|
|
272
|
+
slug,
|
|
273
|
+
rest[1],
|
|
274
|
+
body.value,
|
|
275
|
+
callOptions
|
|
276
|
+
);
|
|
277
|
+
else if (ownUpdate)
|
|
278
|
+
result = await sdk.communityMaps.updateOwnSubmission(
|
|
279
|
+
slug,
|
|
280
|
+
rest[1],
|
|
281
|
+
body,
|
|
282
|
+
callOptions
|
|
283
|
+
);
|
|
284
|
+
else if (submission)
|
|
226
285
|
result = await sdk.communityMaps.createSubmission(
|
|
227
286
|
slug,
|
|
228
287
|
body,
|
|
@@ -40,53 +40,99 @@ function PublicationCommunityMapPage({
|
|
|
40
40
|
() => communityMaps.createCommunityMapsBrowserClient(apiPath),
|
|
41
41
|
[apiPath]
|
|
42
42
|
);
|
|
43
|
+
const signInHref = (f) => `/account?next=${encodeURIComponent(`${basePath}/places/${f.id}`)}`;
|
|
43
44
|
const featureHref = (f) => `${basePath}/places/${f.id}`;
|
|
44
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
45
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
46
|
+
"div",
|
|
47
|
+
{
|
|
48
|
+
className: "smcm",
|
|
49
|
+
style: {
|
|
50
|
+
"--smcm-paper": "var(--pub-paper, Canvas)",
|
|
51
|
+
"--smcm-ink": "var(--pub-ink, CanvasText)",
|
|
52
|
+
"--smcm-border": "var(--pub-rule, currentColor)"
|
|
53
|
+
},
|
|
54
|
+
children: [
|
|
55
|
+
mode !== "guide" && /* @__PURE__ */ jsxRuntime.jsx("p", { children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: basePath, children: [
|
|
56
|
+
"\u2190 ",
|
|
57
|
+
collection.config.title
|
|
58
|
+
] }) }),
|
|
59
|
+
mode === "submit" ? correctionFor ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
60
|
+
communityMaps.CommunitySubmissionForm,
|
|
61
|
+
{
|
|
62
|
+
client,
|
|
63
|
+
collection,
|
|
64
|
+
initialAreaId: areaId || collection.areas.find((a) => a.slug === atlas?.areaSlug)?.id,
|
|
65
|
+
correctionFor,
|
|
66
|
+
guidelinesHref: "/editorial-standards",
|
|
67
|
+
termsHref: "/terms"
|
|
68
|
+
}
|
|
69
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
70
|
+
communityMaps.CommunityStreetCapture,
|
|
71
|
+
{
|
|
72
|
+
client,
|
|
73
|
+
collection,
|
|
74
|
+
initialAreaId: areaId || collection.areas.find((a) => a.slug === atlas?.areaSlug)?.id,
|
|
75
|
+
featureHref: (id) => `${basePath}/places/${id}`,
|
|
76
|
+
guidelinesHref: "/editorial-standards",
|
|
77
|
+
termsHref: "/terms"
|
|
78
|
+
}
|
|
79
|
+
) : mode === "feature" && feature ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
80
|
+
communityMaps.CommunityFeatureDetail,
|
|
81
|
+
{
|
|
82
|
+
client,
|
|
83
|
+
collection,
|
|
84
|
+
feature,
|
|
85
|
+
signInHref: signInHref(feature),
|
|
86
|
+
correctionHref: `${basePath}/submit?correction=${feature.id}&area=${feature.area_id}`
|
|
87
|
+
}
|
|
88
|
+
) : atlas ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
89
|
+
communityMaps.CommunityCityGuide,
|
|
90
|
+
{
|
|
91
|
+
client,
|
|
92
|
+
collection,
|
|
93
|
+
atlas,
|
|
94
|
+
featureHref,
|
|
95
|
+
signInHref,
|
|
96
|
+
communityHref: (id) => `${basePath}?area=${id}`,
|
|
97
|
+
submitHref: `${basePath}/submit?area=${collection.areas.find((a) => a.slug === atlas.areaSlug)?.id || ""}`
|
|
98
|
+
}
|
|
99
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
100
|
+
communityMaps.CommunityGuide,
|
|
101
|
+
{
|
|
102
|
+
client,
|
|
103
|
+
collection,
|
|
104
|
+
featureHref,
|
|
105
|
+
signInHref,
|
|
106
|
+
communityHref: (a) => `${basePath}/map?area=${a.id}`,
|
|
107
|
+
submitHref: `${basePath}/submit${areaId ? `?area=${areaId}` : ""}`,
|
|
108
|
+
initialAreaId: areaId,
|
|
109
|
+
showMap: mode === "map"
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
(mode === "guide" || mode === "map") && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "smcm-contribute-links", children: [
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
114
|
+
"a",
|
|
115
|
+
{
|
|
116
|
+
className: "smcm-button",
|
|
117
|
+
href: `${basePath}/submit${areaId ? `?area=${areaId}` : ""}`,
|
|
118
|
+
children: "Take photos and share a street"
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
" ",
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: `${basePath}/submit#my-submissions`, children: "My submissions" })
|
|
123
|
+
] }),
|
|
124
|
+
(mode === "guide" || mode === "map") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
125
|
+
communityMaps.CommunityDiscussion,
|
|
126
|
+
{
|
|
127
|
+
client,
|
|
128
|
+
collection,
|
|
129
|
+
guidelinesHref: "/editorial-standards",
|
|
130
|
+
termsHref: "/terms"
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
);
|
|
90
136
|
}
|
|
91
137
|
function CommunityMapArticleEmbeds({
|
|
92
138
|
metadata,
|
|
@@ -154,6 +200,9 @@ function ArticleMap({
|
|
|
154
200
|
communityMaps.CommunityFeatureCard,
|
|
155
201
|
{
|
|
156
202
|
feature: f,
|
|
203
|
+
client,
|
|
204
|
+
collection: data.collection,
|
|
205
|
+
signInHref: `/account?next=${encodeURIComponent(`${path}/places/${f.id}`)}`,
|
|
157
206
|
href: `${path}/places/${f.id}`,
|
|
158
207
|
photoUrl: f.media[0] ? client.mediaUrl(data.collection.slug, f.media[0].id) : void 0
|
|
159
208
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { communityMapEmbeds } from './chunk-IMYBSG7J.js';
|
|
3
3
|
export { communityMapEmbeds } from './chunk-IMYBSG7J.js';
|
|
4
4
|
import { useMemo, useState, useEffect } from 'react';
|
|
5
|
-
import { createCommunityMapsBrowserClient, CommunitySubmissionForm, CommunityFeatureDetail, CommunityCityGuide, CommunityGuide, CommunityCards, CommunityMap, CommunityFeatureCard } from '@scalemule/react/community-maps';
|
|
5
|
+
import { createCommunityMapsBrowserClient, CommunitySubmissionForm, CommunityStreetCapture, CommunityFeatureDetail, CommunityCityGuide, CommunityGuide, CommunityDiscussion, CommunityCards, CommunityMap, CommunityFeatureCard } from '@scalemule/react/community-maps';
|
|
6
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
|
|
8
8
|
function PublicationCommunityMapPage({
|
|
@@ -19,53 +19,99 @@ function PublicationCommunityMapPage({
|
|
|
19
19
|
() => createCommunityMapsBrowserClient(apiPath),
|
|
20
20
|
[apiPath]
|
|
21
21
|
);
|
|
22
|
+
const signInHref = (f) => `/account?next=${encodeURIComponent(`${basePath}/places/${f.id}`)}`;
|
|
22
23
|
const featureHref = (f) => `${basePath}/places/${f.id}`;
|
|
23
|
-
return /* @__PURE__ */ jsxs(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
24
|
+
return /* @__PURE__ */ jsxs(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
className: "smcm",
|
|
28
|
+
style: {
|
|
29
|
+
"--smcm-paper": "var(--pub-paper, Canvas)",
|
|
30
|
+
"--smcm-ink": "var(--pub-ink, CanvasText)",
|
|
31
|
+
"--smcm-border": "var(--pub-rule, currentColor)"
|
|
32
|
+
},
|
|
33
|
+
children: [
|
|
34
|
+
mode !== "guide" && /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsxs("a", { href: basePath, children: [
|
|
35
|
+
"\u2190 ",
|
|
36
|
+
collection.config.title
|
|
37
|
+
] }) }),
|
|
38
|
+
mode === "submit" ? correctionFor ? /* @__PURE__ */ jsx(
|
|
39
|
+
CommunitySubmissionForm,
|
|
40
|
+
{
|
|
41
|
+
client,
|
|
42
|
+
collection,
|
|
43
|
+
initialAreaId: areaId || collection.areas.find((a) => a.slug === atlas?.areaSlug)?.id,
|
|
44
|
+
correctionFor,
|
|
45
|
+
guidelinesHref: "/editorial-standards",
|
|
46
|
+
termsHref: "/terms"
|
|
47
|
+
}
|
|
48
|
+
) : /* @__PURE__ */ jsx(
|
|
49
|
+
CommunityStreetCapture,
|
|
50
|
+
{
|
|
51
|
+
client,
|
|
52
|
+
collection,
|
|
53
|
+
initialAreaId: areaId || collection.areas.find((a) => a.slug === atlas?.areaSlug)?.id,
|
|
54
|
+
featureHref: (id) => `${basePath}/places/${id}`,
|
|
55
|
+
guidelinesHref: "/editorial-standards",
|
|
56
|
+
termsHref: "/terms"
|
|
57
|
+
}
|
|
58
|
+
) : mode === "feature" && feature ? /* @__PURE__ */ jsx(
|
|
59
|
+
CommunityFeatureDetail,
|
|
60
|
+
{
|
|
61
|
+
client,
|
|
62
|
+
collection,
|
|
63
|
+
feature,
|
|
64
|
+
signInHref: signInHref(feature),
|
|
65
|
+
correctionHref: `${basePath}/submit?correction=${feature.id}&area=${feature.area_id}`
|
|
66
|
+
}
|
|
67
|
+
) : atlas ? /* @__PURE__ */ jsx(
|
|
68
|
+
CommunityCityGuide,
|
|
69
|
+
{
|
|
70
|
+
client,
|
|
71
|
+
collection,
|
|
72
|
+
atlas,
|
|
73
|
+
featureHref,
|
|
74
|
+
signInHref,
|
|
75
|
+
communityHref: (id) => `${basePath}?area=${id}`,
|
|
76
|
+
submitHref: `${basePath}/submit?area=${collection.areas.find((a) => a.slug === atlas.areaSlug)?.id || ""}`
|
|
77
|
+
}
|
|
78
|
+
) : /* @__PURE__ */ jsx(
|
|
79
|
+
CommunityGuide,
|
|
80
|
+
{
|
|
81
|
+
client,
|
|
82
|
+
collection,
|
|
83
|
+
featureHref,
|
|
84
|
+
signInHref,
|
|
85
|
+
communityHref: (a) => `${basePath}/map?area=${a.id}`,
|
|
86
|
+
submitHref: `${basePath}/submit${areaId ? `?area=${areaId}` : ""}`,
|
|
87
|
+
initialAreaId: areaId,
|
|
88
|
+
showMap: mode === "map"
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
(mode === "guide" || mode === "map") && /* @__PURE__ */ jsxs("p", { className: "smcm-contribute-links", children: [
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
"a",
|
|
94
|
+
{
|
|
95
|
+
className: "smcm-button",
|
|
96
|
+
href: `${basePath}/submit${areaId ? `?area=${areaId}` : ""}`,
|
|
97
|
+
children: "Take photos and share a street"
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
" ",
|
|
101
|
+
/* @__PURE__ */ jsx("a", { href: `${basePath}/submit#my-submissions`, children: "My submissions" })
|
|
102
|
+
] }),
|
|
103
|
+
(mode === "guide" || mode === "map") && /* @__PURE__ */ jsx(
|
|
104
|
+
CommunityDiscussion,
|
|
105
|
+
{
|
|
106
|
+
client,
|
|
107
|
+
collection,
|
|
108
|
+
guidelinesHref: "/editorial-standards",
|
|
109
|
+
termsHref: "/terms"
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
);
|
|
69
115
|
}
|
|
70
116
|
function CommunityMapArticleEmbeds({
|
|
71
117
|
metadata,
|
|
@@ -133,6 +179,9 @@ function ArticleMap({
|
|
|
133
179
|
CommunityFeatureCard,
|
|
134
180
|
{
|
|
135
181
|
feature: f,
|
|
182
|
+
client,
|
|
183
|
+
collection: data.collection,
|
|
184
|
+
signInHref: `/account?next=${encodeURIComponent(`${path}/places/${f.id}`)}`,
|
|
136
185
|
href: `${path}/places/${f.id}`,
|
|
137
186
|
photoUrl: f.media[0] ? client.mediaUrl(data.collection.slug, f.media[0].id) : void 0
|
|
138
187
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalemule/news-publication",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.41",
|
|
4
4
|
"description": "Shared server framework for ScaleMule local news publications. Walnut Creek Times is the broadsheet default; each paper supplies its own identity and feature flags.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -109,8 +109,8 @@
|
|
|
109
109
|
"story-review.css"
|
|
110
110
|
],
|
|
111
111
|
"peerDependencies": {
|
|
112
|
-
"react": "
|
|
113
|
-
"
|
|
112
|
+
"@scalemule/react": "0.0.18",
|
|
113
|
+
"react": ">=18.0.0"
|
|
114
114
|
},
|
|
115
115
|
"peerDependenciesMeta": {
|
|
116
116
|
"react": {
|
|
@@ -129,6 +129,7 @@
|
|
|
129
129
|
"prepublishOnly": "node -e \"const v=require('./package.json').version; if(!/^0\\.(0|1)\\.\\d+$/.test(v)){console.error('ERR: SDK '+v+' violates patch-only policy (0.0.x or 0.1.x only). See CONTRIBUTING.md SDK versioning section.');process.exit(1)}\" && npm run lint && npm test && npm run build"
|
|
130
130
|
},
|
|
131
131
|
"devDependencies": {
|
|
132
|
+
"@scalemule/react": "0.0.18",
|
|
132
133
|
"@testing-library/react": "^16.3.3",
|
|
133
134
|
"@types/node": "^22.15.0",
|
|
134
135
|
"@types/react": "^19.1.0",
|
|
@@ -138,8 +139,7 @@
|
|
|
138
139
|
"react-dom": "^19.3.0",
|
|
139
140
|
"tsup": "^8.5.0",
|
|
140
141
|
"typescript": "^5.8.3",
|
|
141
|
-
"vitest": "^3.2.4"
|
|
142
|
-
"@scalemule/react": "^0.0.13"
|
|
142
|
+
"vitest": "^3.2.4"
|
|
143
143
|
},
|
|
144
144
|
"keywords": [
|
|
145
145
|
"scalemule",
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
},
|
|
163
163
|
"dependencies": {
|
|
164
164
|
"@scalemule/compliance": "^0.0.5",
|
|
165
|
-
"@scalemule/
|
|
166
|
-
"@scalemule/
|
|
165
|
+
"@scalemule/react": "0.0.18",
|
|
166
|
+
"@scalemule/sdk": "0.0.75"
|
|
167
167
|
}
|
|
168
168
|
}
|