@mintplayer/ng-spark 22.23.0 → 22.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -0
- package/fesm2022/mintplayer-ng-spark-attribute-description.mjs +100 -47
- package/fesm2022/mintplayer-ng-spark-attribute-description.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs +310 -329
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-column-filter.mjs +175 -135
- package/fesm2022/mintplayer-ng-spark-column-filter.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-contributions.mjs +721 -0
- package/fesm2022/mintplayer-ng-spark-contributions.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-grid.mjs +2385 -1118
- package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-history.mjs +598 -0
- package/fesm2022/mintplayer-ng-spark-history.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-icon.mjs +64 -27
- package/fesm2022/mintplayer-ng-spark-icon.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-models.mjs +405 -762
- package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-moderation.mjs +1212 -0
- package/fesm2022/mintplayer-ng-spark-moderation.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-panels.mjs +60 -0
- package/fesm2022/mintplayer-ng-spark-panels.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-pipes.mjs +1158 -765
- package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-create.mjs +273 -169
- package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +795 -410
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs +1196 -280
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +2244 -1224
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +571 -303
- package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-renderers.mjs +44 -55
- package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +216 -177
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-routes.mjs +24 -28
- package/fesm2022/mintplayer-ng-spark-routes.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +625 -640
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-shell.mjs +1031 -397
- package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +294 -0
- package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark.mjs +14 -48
- package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
- package/package.json +23 -2
- package/types/mintplayer-ng-spark-attribute-description.d.ts +25 -18
- package/types/mintplayer-ng-spark-client-operations.d.ts +139 -160
- package/types/mintplayer-ng-spark-column-filter.d.ts +73 -55
- package/types/mintplayer-ng-spark-contributions.d.ts +231 -0
- package/types/mintplayer-ng-spark-grid.d.ts +917 -448
- package/types/mintplayer-ng-spark-history.d.ts +216 -0
- package/types/mintplayer-ng-spark-icon.d.ts +15 -13
- package/types/mintplayer-ng-spark-models.d.ts +681 -573
- package/types/mintplayer-ng-spark-moderation.d.ts +402 -0
- package/types/mintplayer-ng-spark-panels.d.ts +143 -0
- package/types/mintplayer-ng-spark-pipes.d.ts +131 -149
- package/types/mintplayer-ng-spark-po-create.d.ts +46 -37
- package/types/mintplayer-ng-spark-po-detail.d.ts +180 -117
- package/types/mintplayer-ng-spark-po-edit.d.ts +367 -64
- package/types/mintplayer-ng-spark-po-form.d.ts +558 -373
- package/types/mintplayer-ng-spark-query-list.d.ts +143 -82
- package/types/mintplayer-ng-spark-renderers.d.ts +121 -85
- package/types/mintplayer-ng-spark-retry-action-modal.d.ts +31 -34
- package/types/mintplayer-ng-spark-routes.d.ts +7 -11
- package/types/mintplayer-ng-spark-services.d.ts +377 -261
- package/types/mintplayer-ng-spark-shell.d.ts +228 -132
- package/types/mintplayer-ng-spark-soft-delete.d.ts +90 -0
- package/types/mintplayer-ng-spark.d.ts +19 -12
|
@@ -0,0 +1,1212 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, Injectable, InjectionToken, computed, effect, inject, input, signal } from "@angular/core";
|
|
3
|
+
import { toSignal } from "@angular/core/rxjs-interop";
|
|
4
|
+
import { ActivatedRoute, NavigationEnd, Router, RouterLink } from "@angular/router";
|
|
5
|
+
import { EMPTY, filter } from "rxjs";
|
|
6
|
+
import { SparkLanguageService, SparkService } from "@mintplayer/ng-spark/services";
|
|
7
|
+
import { TranslateKeyPipe } from "@mintplayer/ng-spark/pipes";
|
|
8
|
+
import { BsCardComponent, BsCardHeaderComponent } from "@mintplayer/ng-bootstrap/card";
|
|
9
|
+
import { DatePipe } from "@angular/common";
|
|
10
|
+
import { provideSparkDetailActions, provideSparkDetailPanels } from "@mintplayer/ng-spark/panels";
|
|
11
|
+
var SparkModerationService = class SparkModerationService {
|
|
12
|
+
spark = inject(SparkService);
|
|
13
|
+
pending = /* @__PURE__ */ new Map();
|
|
14
|
+
flushScheduled = false;
|
|
15
|
+
router = inject(Router, { optional: true });
|
|
16
|
+
#votesCast = signal(0, ...ngDevMode ? [{ debugName: "#votesCast" }] : /* istanbul ignore next */ []);
|
|
17
|
+
votesCast = this.#votesCast.asReadonly();
|
|
18
|
+
navigated = toSignal(this.router?.events.pipe(filter((e) => e instanceof NavigationEnd)) ?? EMPTY, { initialValue: null });
|
|
19
|
+
ownReputationChanged = computed(() => ({
|
|
20
|
+
navigation: this.navigated(),
|
|
21
|
+
votes: this.#votesCast()
|
|
22
|
+
}), ...ngDevMode ? [{ debugName: "ownReputationChanged" }] : /* istanbul ignore next */ []);
|
|
23
|
+
#ownInFlight = null;
|
|
24
|
+
ownReputation() {
|
|
25
|
+
return this.#ownInFlight ??= this.reputation().finally(() => this.#ownInFlight = null);
|
|
26
|
+
}
|
|
27
|
+
async vote(type, id, direction) {
|
|
28
|
+
const state = await this.spark.postEnvelope("/moderation/vote", {
|
|
29
|
+
objectTypeId: type,
|
|
30
|
+
id,
|
|
31
|
+
direction
|
|
32
|
+
});
|
|
33
|
+
this.#votesCast.update((n) => n + 1);
|
|
34
|
+
return state;
|
|
35
|
+
}
|
|
36
|
+
voteState(type, id) {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
let byId = this.pending.get(type);
|
|
39
|
+
if (!byId) this.pending.set(type, byId = /* @__PURE__ */ new Map());
|
|
40
|
+
const waiting = byId.get(id) ?? [];
|
|
41
|
+
waiting.push(resolve);
|
|
42
|
+
byId.set(id, waiting);
|
|
43
|
+
if (!this.flushScheduled) {
|
|
44
|
+
this.flushScheduled = true;
|
|
45
|
+
setTimeout(() => this.flush(), 0);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
votes(type, ids) {
|
|
50
|
+
return this.spark.postEnvelope("/moderation/votes", {
|
|
51
|
+
objectTypeId: type,
|
|
52
|
+
ids
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
flag(type, id, reason) {
|
|
56
|
+
return this.spark.postEnvelope("/moderation/flag", {
|
|
57
|
+
objectTypeId: type,
|
|
58
|
+
id,
|
|
59
|
+
reason
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
lock(type, id, reason) {
|
|
63
|
+
return this.spark.postEnvelope("/moderation/lock", {
|
|
64
|
+
objectTypeId: type,
|
|
65
|
+
id,
|
|
66
|
+
reason
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
unlock(type, id) {
|
|
70
|
+
return this.spark.postEnvelope("/moderation/unlock", {
|
|
71
|
+
objectTypeId: type,
|
|
72
|
+
id
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
status(type, id) {
|
|
76
|
+
return this.spark.postEnvelope("/moderation/status", {
|
|
77
|
+
objectTypeId: type,
|
|
78
|
+
id
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
reputation(userId) {
|
|
82
|
+
return this.spark.postEnvelope("/moderation/reputation", userId ? { userId } : {});
|
|
83
|
+
}
|
|
84
|
+
history(take = 100) {
|
|
85
|
+
return this.spark.postEnvelope("/moderation/reputation/history", { take });
|
|
86
|
+
}
|
|
87
|
+
cases(status = "open", skip = 0, take = 50) {
|
|
88
|
+
return this.spark.postEnvelope("/moderation/cases", {
|
|
89
|
+
status,
|
|
90
|
+
skip,
|
|
91
|
+
take
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
case(caseId) {
|
|
95
|
+
return this.spark.postEnvelope("/moderation/case", { caseId });
|
|
96
|
+
}
|
|
97
|
+
decide(caseId, decision, accountId, reason) {
|
|
98
|
+
return this.spark.postEnvelope("/moderation/case/decide", {
|
|
99
|
+
caseId,
|
|
100
|
+
decision,
|
|
101
|
+
accountId,
|
|
102
|
+
reason
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
suspend(userId, days, reason) {
|
|
106
|
+
return this.spark.postEnvelope("/moderation/suspend", {
|
|
107
|
+
userId,
|
|
108
|
+
days: days ?? null,
|
|
109
|
+
reason
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
unsuspend(userId) {
|
|
113
|
+
return this.spark.postEnvelope("/moderation/unsuspend", { userId });
|
|
114
|
+
}
|
|
115
|
+
audit(skip = 0, take = 50) {
|
|
116
|
+
return this.spark.postEnvelope("/moderation/audit", {
|
|
117
|
+
skip,
|
|
118
|
+
take
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
flush() {
|
|
122
|
+
this.flushScheduled = false;
|
|
123
|
+
const batches = [...this.pending.entries()];
|
|
124
|
+
this.pending.clear();
|
|
125
|
+
for (const [type, byId] of batches) {
|
|
126
|
+
const ids = [...byId.keys()];
|
|
127
|
+
for (let i = 0; i < ids.length; i += 100) {
|
|
128
|
+
const chunk = ids.slice(i, i + 100);
|
|
129
|
+
this.votes(type, chunk).then((states) => {
|
|
130
|
+
const found = new Map(states.map((s) => [s.id, s]));
|
|
131
|
+
for (const id of chunk) byId.get(id)?.forEach((resolve) => resolve(found.get(id) ?? null));
|
|
132
|
+
}, () => {
|
|
133
|
+
for (const id of chunk) byId.get(id)?.forEach((resolve) => resolve(null));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
139
|
+
minVersion: "12.0.0",
|
|
140
|
+
version: "22.2.0",
|
|
141
|
+
ngImport: i0,
|
|
142
|
+
type: SparkModerationService,
|
|
143
|
+
deps: [],
|
|
144
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
145
|
+
});
|
|
146
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
147
|
+
minVersion: "12.0.0",
|
|
148
|
+
version: "22.2.0",
|
|
149
|
+
ngImport: i0,
|
|
150
|
+
type: SparkModerationService,
|
|
151
|
+
providedIn: "root"
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
155
|
+
minVersion: "12.0.0",
|
|
156
|
+
version: "22.2.0",
|
|
157
|
+
ngImport: i0,
|
|
158
|
+
type: SparkModerationService,
|
|
159
|
+
decorators: [{
|
|
160
|
+
type: Injectable,
|
|
161
|
+
args: [{ providedIn: "root" }]
|
|
162
|
+
}]
|
|
163
|
+
});
|
|
164
|
+
const SPARK_MODERATION_REVIEW_PATH = new InjectionToken("SparkModerationReviewPath", { factory: () => "/moderation/review" });
|
|
165
|
+
var SparkVoteComponent = class SparkVoteComponent {
|
|
166
|
+
moderation = inject(SparkModerationService);
|
|
167
|
+
value = input(...ngDevMode ? [void 0, { debugName: "value" }] : /* istanbul ignore next */ []);
|
|
168
|
+
attribute = input(...ngDevMode ? [void 0, { debugName: "attribute" }] : /* istanbul ignore next */ []);
|
|
169
|
+
column = input(...ngDevMode ? [void 0, { debugName: "column" }] : /* istanbul ignore next */ []);
|
|
170
|
+
options = input(...ngDevMode ? [void 0, { debugName: "options" }] : /* istanbul ignore next */ []);
|
|
171
|
+
item = input(...ngDevMode ? [void 0, { debugName: "item" }] : /* istanbul ignore next */ []);
|
|
172
|
+
state = signal(null, ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
|
|
173
|
+
busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
|
|
174
|
+
error = signal(null, ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
175
|
+
target = computed(() => {
|
|
176
|
+
const item = this.item();
|
|
177
|
+
const id = item?.["id"];
|
|
178
|
+
const type = item?.["objectTypeId"] ?? this.options()?.["type"];
|
|
179
|
+
return id && type ? {
|
|
180
|
+
id,
|
|
181
|
+
type
|
|
182
|
+
} : null;
|
|
183
|
+
}, ...ngDevMode ? [{ debugName: "target" }] : /* istanbul ignore next */ []);
|
|
184
|
+
constructor() {
|
|
185
|
+
effect(() => {
|
|
186
|
+
const target = this.target();
|
|
187
|
+
if (!target) return;
|
|
188
|
+
this.moderation.voteState(target.type, target.id).then((s) => this.state.set(s));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async cast(direction) {
|
|
192
|
+
const target = this.target();
|
|
193
|
+
const current = this.state();
|
|
194
|
+
if (!target || !current || this.busy()) return;
|
|
195
|
+
const next = current.myVote === direction ? 0 : direction;
|
|
196
|
+
this.busy.set(true);
|
|
197
|
+
this.error.set(null);
|
|
198
|
+
try {
|
|
199
|
+
this.state.set(await this.moderation.vote(target.type, target.id, next));
|
|
200
|
+
} catch (e) {
|
|
201
|
+
this.error.set(describeModerationError(e));
|
|
202
|
+
} finally {
|
|
203
|
+
this.busy.set(false);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
207
|
+
minVersion: "12.0.0",
|
|
208
|
+
version: "22.2.0",
|
|
209
|
+
ngImport: i0,
|
|
210
|
+
type: SparkVoteComponent,
|
|
211
|
+
deps: [],
|
|
212
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
213
|
+
});
|
|
214
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
215
|
+
minVersion: "17.0.0",
|
|
216
|
+
version: "22.2.0",
|
|
217
|
+
type: SparkVoteComponent,
|
|
218
|
+
isStandalone: true,
|
|
219
|
+
selector: "spark-vote",
|
|
220
|
+
inputs: {
|
|
221
|
+
value: {
|
|
222
|
+
classPropertyName: "value",
|
|
223
|
+
publicName: "value",
|
|
224
|
+
isSignal: true,
|
|
225
|
+
isRequired: false,
|
|
226
|
+
transformFunction: null
|
|
227
|
+
},
|
|
228
|
+
attribute: {
|
|
229
|
+
classPropertyName: "attribute",
|
|
230
|
+
publicName: "attribute",
|
|
231
|
+
isSignal: true,
|
|
232
|
+
isRequired: false,
|
|
233
|
+
transformFunction: null
|
|
234
|
+
},
|
|
235
|
+
column: {
|
|
236
|
+
classPropertyName: "column",
|
|
237
|
+
publicName: "column",
|
|
238
|
+
isSignal: true,
|
|
239
|
+
isRequired: false,
|
|
240
|
+
transformFunction: null
|
|
241
|
+
},
|
|
242
|
+
options: {
|
|
243
|
+
classPropertyName: "options",
|
|
244
|
+
publicName: "options",
|
|
245
|
+
isSignal: true,
|
|
246
|
+
isRequired: false,
|
|
247
|
+
transformFunction: null
|
|
248
|
+
},
|
|
249
|
+
item: {
|
|
250
|
+
classPropertyName: "item",
|
|
251
|
+
publicName: "item",
|
|
252
|
+
isSignal: true,
|
|
253
|
+
isRequired: false,
|
|
254
|
+
transformFunction: null
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
ngImport: i0,
|
|
258
|
+
template: `
|
|
259
|
+
@if (state(); as s) {
|
|
260
|
+
<span class="spark-vote d-inline-flex align-items-center gap-1" [class.spark-vote-locked]="s.locked">
|
|
261
|
+
<button type="button" class="btn btn-sm spark-vote-up" [class.btn-primary]="s.myVote === 1" [class.btn-outline-secondary]="s.myVote !== 1"
|
|
262
|
+
[disabled]="busy() || s.locked || (s.canUpvote === false && s.myVote !== 1)" [attr.aria-pressed]="s.myVote === 1" [attr.aria-label]="'moderation.upvote' | t" (click)="cast(1)">▲</button>
|
|
263
|
+
<span class="spark-vote-score fw-bold" [attr.aria-label]="'moderation.score' | t">{{ s.score }}</span>
|
|
264
|
+
<button type="button" class="btn btn-sm spark-vote-down" [class.btn-danger]="s.myVote === -1" [class.btn-outline-secondary]="s.myVote !== -1"
|
|
265
|
+
[disabled]="busy() || s.locked || (s.canDownvote === false && s.myVote !== -1)" [attr.aria-pressed]="s.myVote === -1" [attr.aria-label]="'moderation.downvote' | t" (click)="cast(-1)">▼</button>
|
|
266
|
+
@if (error(); as message) {
|
|
267
|
+
<span class="text-danger small spark-vote-error" role="alert">{{ message }}</span>
|
|
268
|
+
}
|
|
269
|
+
</span>
|
|
270
|
+
}
|
|
271
|
+
`,
|
|
272
|
+
isInline: true,
|
|
273
|
+
dependencies: [{
|
|
274
|
+
kind: "pipe",
|
|
275
|
+
type: TranslateKeyPipe,
|
|
276
|
+
name: "t"
|
|
277
|
+
}],
|
|
278
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
282
|
+
minVersion: "12.0.0",
|
|
283
|
+
version: "22.2.0",
|
|
284
|
+
ngImport: i0,
|
|
285
|
+
type: SparkVoteComponent,
|
|
286
|
+
decorators: [{
|
|
287
|
+
type: Component,
|
|
288
|
+
args: [{
|
|
289
|
+
selector: "spark-vote",
|
|
290
|
+
imports: [TranslateKeyPipe],
|
|
291
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
292
|
+
template: `
|
|
293
|
+
@if (state(); as s) {
|
|
294
|
+
<span class="spark-vote d-inline-flex align-items-center gap-1" [class.spark-vote-locked]="s.locked">
|
|
295
|
+
<button type="button" class="btn btn-sm spark-vote-up" [class.btn-primary]="s.myVote === 1" [class.btn-outline-secondary]="s.myVote !== 1"
|
|
296
|
+
[disabled]="busy() || s.locked || (s.canUpvote === false && s.myVote !== 1)" [attr.aria-pressed]="s.myVote === 1" [attr.aria-label]="'moderation.upvote' | t" (click)="cast(1)">▲</button>
|
|
297
|
+
<span class="spark-vote-score fw-bold" [attr.aria-label]="'moderation.score' | t">{{ s.score }}</span>
|
|
298
|
+
<button type="button" class="btn btn-sm spark-vote-down" [class.btn-danger]="s.myVote === -1" [class.btn-outline-secondary]="s.myVote !== -1"
|
|
299
|
+
[disabled]="busy() || s.locked || (s.canDownvote === false && s.myVote !== -1)" [attr.aria-pressed]="s.myVote === -1" [attr.aria-label]="'moderation.downvote' | t" (click)="cast(-1)">▼</button>
|
|
300
|
+
@if (error(); as message) {
|
|
301
|
+
<span class="text-danger small spark-vote-error" role="alert">{{ message }}</span>
|
|
302
|
+
}
|
|
303
|
+
</span>
|
|
304
|
+
}
|
|
305
|
+
`
|
|
306
|
+
}]
|
|
307
|
+
}],
|
|
308
|
+
ctorParameters: () => [],
|
|
309
|
+
propDecorators: {
|
|
310
|
+
value: [{
|
|
311
|
+
type: i0.Input,
|
|
312
|
+
args: [{
|
|
313
|
+
isSignal: true,
|
|
314
|
+
alias: "value",
|
|
315
|
+
required: false
|
|
316
|
+
}]
|
|
317
|
+
}],
|
|
318
|
+
attribute: [{
|
|
319
|
+
type: i0.Input,
|
|
320
|
+
args: [{
|
|
321
|
+
isSignal: true,
|
|
322
|
+
alias: "attribute",
|
|
323
|
+
required: false
|
|
324
|
+
}]
|
|
325
|
+
}],
|
|
326
|
+
column: [{
|
|
327
|
+
type: i0.Input,
|
|
328
|
+
args: [{
|
|
329
|
+
isSignal: true,
|
|
330
|
+
alias: "column",
|
|
331
|
+
required: false
|
|
332
|
+
}]
|
|
333
|
+
}],
|
|
334
|
+
options: [{
|
|
335
|
+
type: i0.Input,
|
|
336
|
+
args: [{
|
|
337
|
+
isSignal: true,
|
|
338
|
+
alias: "options",
|
|
339
|
+
required: false
|
|
340
|
+
}]
|
|
341
|
+
}],
|
|
342
|
+
item: [{
|
|
343
|
+
type: i0.Input,
|
|
344
|
+
args: [{
|
|
345
|
+
isSignal: true,
|
|
346
|
+
alias: "item",
|
|
347
|
+
required: false
|
|
348
|
+
}]
|
|
349
|
+
}]
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
function describeModerationError(e) {
|
|
353
|
+
const err = e;
|
|
354
|
+
const body = err?.error;
|
|
355
|
+
const first = body?.result?.errors?.[0]?.errorMessage;
|
|
356
|
+
if (typeof first === "string") return first;
|
|
357
|
+
if (first && typeof first === "object") {
|
|
358
|
+
const values = Object.values(first);
|
|
359
|
+
if (values.length) return values[0];
|
|
360
|
+
}
|
|
361
|
+
return body?.result?.error ?? err?.message ?? "Failed";
|
|
362
|
+
}
|
|
363
|
+
var SparkFlagButtonComponent = class SparkFlagButtonComponent {
|
|
364
|
+
moderation = inject(SparkModerationService);
|
|
365
|
+
lang = inject(SparkLanguageService);
|
|
366
|
+
context = input.required(...ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []);
|
|
367
|
+
busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
|
|
368
|
+
done = signal(false, ...ngDevMode ? [{ debugName: "done" }] : /* istanbul ignore next */ []);
|
|
369
|
+
error = signal(null, ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
370
|
+
async flag() {
|
|
371
|
+
const reason = prompt(this.lang.t("moderation.flagReason"));
|
|
372
|
+
if (!reason?.trim()) return;
|
|
373
|
+
const ctx = this.context();
|
|
374
|
+
this.busy.set(true);
|
|
375
|
+
this.error.set(null);
|
|
376
|
+
try {
|
|
377
|
+
await this.moderation.flag(ctx.type, ctx.id, reason.trim());
|
|
378
|
+
this.done.set(true);
|
|
379
|
+
} catch (e) {
|
|
380
|
+
this.error.set(describeModerationError(e));
|
|
381
|
+
} finally {
|
|
382
|
+
this.busy.set(false);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
386
|
+
minVersion: "12.0.0",
|
|
387
|
+
version: "22.2.0",
|
|
388
|
+
ngImport: i0,
|
|
389
|
+
type: SparkFlagButtonComponent,
|
|
390
|
+
deps: [],
|
|
391
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
392
|
+
});
|
|
393
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
394
|
+
minVersion: "17.0.0",
|
|
395
|
+
version: "22.2.0",
|
|
396
|
+
type: SparkFlagButtonComponent,
|
|
397
|
+
isStandalone: true,
|
|
398
|
+
selector: "spark-flag-button",
|
|
399
|
+
inputs: { context: {
|
|
400
|
+
classPropertyName: "context",
|
|
401
|
+
publicName: "context",
|
|
402
|
+
isSignal: true,
|
|
403
|
+
isRequired: true,
|
|
404
|
+
transformFunction: null
|
|
405
|
+
} },
|
|
406
|
+
ngImport: i0,
|
|
407
|
+
template: `
|
|
408
|
+
@if (context().deleted !== 'only') {
|
|
409
|
+
<span class="d-inline-flex gap-2 align-items-center">
|
|
410
|
+
<button type="button" class="btn btn-outline-warning spark-flag" [disabled]="busy() || done()" (click)="flag()">
|
|
411
|
+
{{ 'moderation.flag' | t }}
|
|
412
|
+
</button>
|
|
413
|
+
@if (done()) {
|
|
414
|
+
<span class="small text-success spark-flag-done">{{ 'moderation.flagged' | t }}</span>
|
|
415
|
+
}
|
|
416
|
+
@if (error(); as message) {
|
|
417
|
+
<span class="small text-danger spark-flag-error" role="alert">{{ message }}</span>
|
|
418
|
+
}
|
|
419
|
+
</span>
|
|
420
|
+
}
|
|
421
|
+
`,
|
|
422
|
+
isInline: true,
|
|
423
|
+
dependencies: [{
|
|
424
|
+
kind: "pipe",
|
|
425
|
+
type: TranslateKeyPipe,
|
|
426
|
+
name: "t"
|
|
427
|
+
}],
|
|
428
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
429
|
+
});
|
|
430
|
+
};
|
|
431
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
432
|
+
minVersion: "12.0.0",
|
|
433
|
+
version: "22.2.0",
|
|
434
|
+
ngImport: i0,
|
|
435
|
+
type: SparkFlagButtonComponent,
|
|
436
|
+
decorators: [{
|
|
437
|
+
type: Component,
|
|
438
|
+
args: [{
|
|
439
|
+
selector: "spark-flag-button",
|
|
440
|
+
imports: [TranslateKeyPipe],
|
|
441
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
442
|
+
template: `
|
|
443
|
+
@if (context().deleted !== 'only') {
|
|
444
|
+
<span class="d-inline-flex gap-2 align-items-center">
|
|
445
|
+
<button type="button" class="btn btn-outline-warning spark-flag" [disabled]="busy() || done()" (click)="flag()">
|
|
446
|
+
{{ 'moderation.flag' | t }}
|
|
447
|
+
</button>
|
|
448
|
+
@if (done()) {
|
|
449
|
+
<span class="small text-success spark-flag-done">{{ 'moderation.flagged' | t }}</span>
|
|
450
|
+
}
|
|
451
|
+
@if (error(); as message) {
|
|
452
|
+
<span class="small text-danger spark-flag-error" role="alert">{{ message }}</span>
|
|
453
|
+
}
|
|
454
|
+
</span>
|
|
455
|
+
}
|
|
456
|
+
`
|
|
457
|
+
}]
|
|
458
|
+
}],
|
|
459
|
+
propDecorators: { context: [{
|
|
460
|
+
type: i0.Input,
|
|
461
|
+
args: [{
|
|
462
|
+
isSignal: true,
|
|
463
|
+
alias: "context",
|
|
464
|
+
required: true
|
|
465
|
+
}]
|
|
466
|
+
}] }
|
|
467
|
+
});
|
|
468
|
+
var SparkModeratorPanelComponent = class SparkModeratorPanelComponent {
|
|
469
|
+
moderation = inject(SparkModerationService);
|
|
470
|
+
lang = inject(SparkLanguageService);
|
|
471
|
+
reviewPath = inject(SPARK_MODERATION_REVIEW_PATH);
|
|
472
|
+
context = input.required(...ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []);
|
|
473
|
+
status = signal(null, ...ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []);
|
|
474
|
+
busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
|
|
475
|
+
error = signal(null, ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
476
|
+
constructor() {
|
|
477
|
+
effect(() => {
|
|
478
|
+
const ctx = this.context();
|
|
479
|
+
if (ctx.deleted === "only") {
|
|
480
|
+
this.status.set(null);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
this.moderation.status(ctx.type, ctx.id).then((s) => this.status.set(s), () => this.status.set(null));
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
async lock() {
|
|
487
|
+
const reason = prompt(this.lang.t("moderation.lockReason")) ?? void 0;
|
|
488
|
+
await this.run(() => this.moderation.lock(this.context().type, this.context().id, reason));
|
|
489
|
+
}
|
|
490
|
+
async unlock() {
|
|
491
|
+
await this.run(() => this.moderation.unlock(this.context().type, this.context().id));
|
|
492
|
+
}
|
|
493
|
+
async suspendAuthor(userId) {
|
|
494
|
+
const answer = prompt(this.lang.t("moderation.suspendDays"));
|
|
495
|
+
if (answer === null) return;
|
|
496
|
+
const days = answer.trim() === "" ? null : Number(answer);
|
|
497
|
+
if (days !== null && !(days > 0)) return;
|
|
498
|
+
await this.run(() => this.moderation.suspend(userId, days));
|
|
499
|
+
}
|
|
500
|
+
async run(work) {
|
|
501
|
+
this.busy.set(true);
|
|
502
|
+
this.error.set(null);
|
|
503
|
+
try {
|
|
504
|
+
await work();
|
|
505
|
+
const ctx = this.context();
|
|
506
|
+
this.status.set(await this.moderation.status(ctx.type, ctx.id));
|
|
507
|
+
await ctx.reload();
|
|
508
|
+
} catch (e) {
|
|
509
|
+
this.error.set(describeModerationError(e));
|
|
510
|
+
} finally {
|
|
511
|
+
this.busy.set(false);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
515
|
+
minVersion: "12.0.0",
|
|
516
|
+
version: "22.2.0",
|
|
517
|
+
ngImport: i0,
|
|
518
|
+
type: SparkModeratorPanelComponent,
|
|
519
|
+
deps: [],
|
|
520
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
521
|
+
});
|
|
522
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
523
|
+
minVersion: "17.0.0",
|
|
524
|
+
version: "22.2.0",
|
|
525
|
+
type: SparkModeratorPanelComponent,
|
|
526
|
+
isStandalone: true,
|
|
527
|
+
selector: "spark-moderator-panel",
|
|
528
|
+
inputs: { context: {
|
|
529
|
+
classPropertyName: "context",
|
|
530
|
+
publicName: "context",
|
|
531
|
+
isSignal: true,
|
|
532
|
+
isRequired: true,
|
|
533
|
+
transformFunction: null
|
|
534
|
+
} },
|
|
535
|
+
ngImport: i0,
|
|
536
|
+
template: `
|
|
537
|
+
@if (status(); as s) {
|
|
538
|
+
@if (s.canLock || s.canReview || s.canSuspend) {
|
|
539
|
+
<bs-card class="d-block spark-moderator-panel">
|
|
540
|
+
<bs-card-header>{{ 'moderation.panel' | t }}</bs-card-header>
|
|
541
|
+
<div class="p-3 d-flex flex-wrap gap-2 align-items-center">
|
|
542
|
+
@if (s.locked) {
|
|
543
|
+
<span class="badge bg-secondary spark-moderation-locked">{{ 'moderation.locked' | t }}</span>
|
|
544
|
+
@if (s.lockReason) { <span class="small text-muted">{{ s.lockReason }}</span> }
|
|
545
|
+
}
|
|
546
|
+
@if (s.canLock) {
|
|
547
|
+
@if (s.locked) {
|
|
548
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-unlock" [disabled]="busy()" (click)="unlock()">{{ 'moderation.unlock' | t }}</button>
|
|
549
|
+
} @else {
|
|
550
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-lock" [disabled]="busy()" (click)="lock()">{{ 'moderation.lock' | t }}</button>
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
@if (s.canReview && s.openFlags > 0) {
|
|
554
|
+
<a class="btn btn-sm btn-outline-warning spark-open-flags" [routerLink]="reviewPath" [queryParams]="{ case: s.openCaseId }">
|
|
555
|
+
{{ 'moderation.openFlags' | t }}: {{ s.openFlags }}
|
|
556
|
+
</a>
|
|
557
|
+
}
|
|
558
|
+
@if (s.canSuspend && s.authorId) {
|
|
559
|
+
<button type="button" class="btn btn-sm btn-outline-danger spark-suspend-author" [disabled]="busy()" (click)="suspendAuthor(s.authorId)">{{ 'moderation.suspendAuthor' | t }}</button>
|
|
560
|
+
}
|
|
561
|
+
@if (error(); as message) {
|
|
562
|
+
<span class="small text-danger" role="alert">{{ message }}</span>
|
|
563
|
+
}
|
|
564
|
+
</div>
|
|
565
|
+
</bs-card>
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
`,
|
|
569
|
+
isInline: true,
|
|
570
|
+
dependencies: [
|
|
571
|
+
{
|
|
572
|
+
kind: "component",
|
|
573
|
+
type: BsCardComponent,
|
|
574
|
+
selector: "bs-card",
|
|
575
|
+
inputs: ["color", "outline"]
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
kind: "component",
|
|
579
|
+
type: BsCardHeaderComponent,
|
|
580
|
+
selector: "bs-card-header",
|
|
581
|
+
inputs: ["color", "navStyle"]
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
kind: "directive",
|
|
585
|
+
type: RouterLink,
|
|
586
|
+
selector: "[routerLink]",
|
|
587
|
+
inputs: [
|
|
588
|
+
"target",
|
|
589
|
+
"queryParams",
|
|
590
|
+
"fragment",
|
|
591
|
+
"queryParamsHandling",
|
|
592
|
+
"state",
|
|
593
|
+
"info",
|
|
594
|
+
"relativeTo",
|
|
595
|
+
"preserveFragment",
|
|
596
|
+
"skipLocationChange",
|
|
597
|
+
"replaceUrl",
|
|
598
|
+
"browserUrl",
|
|
599
|
+
"routerLink"
|
|
600
|
+
]
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
kind: "pipe",
|
|
604
|
+
type: TranslateKeyPipe,
|
|
605
|
+
name: "t"
|
|
606
|
+
}
|
|
607
|
+
],
|
|
608
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
609
|
+
});
|
|
610
|
+
};
|
|
611
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
612
|
+
minVersion: "12.0.0",
|
|
613
|
+
version: "22.2.0",
|
|
614
|
+
ngImport: i0,
|
|
615
|
+
type: SparkModeratorPanelComponent,
|
|
616
|
+
decorators: [{
|
|
617
|
+
type: Component,
|
|
618
|
+
args: [{
|
|
619
|
+
selector: "spark-moderator-panel",
|
|
620
|
+
imports: [
|
|
621
|
+
BsCardComponent,
|
|
622
|
+
BsCardHeaderComponent,
|
|
623
|
+
TranslateKeyPipe,
|
|
624
|
+
RouterLink
|
|
625
|
+
],
|
|
626
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
627
|
+
template: `
|
|
628
|
+
@if (status(); as s) {
|
|
629
|
+
@if (s.canLock || s.canReview || s.canSuspend) {
|
|
630
|
+
<bs-card class="d-block spark-moderator-panel">
|
|
631
|
+
<bs-card-header>{{ 'moderation.panel' | t }}</bs-card-header>
|
|
632
|
+
<div class="p-3 d-flex flex-wrap gap-2 align-items-center">
|
|
633
|
+
@if (s.locked) {
|
|
634
|
+
<span class="badge bg-secondary spark-moderation-locked">{{ 'moderation.locked' | t }}</span>
|
|
635
|
+
@if (s.lockReason) { <span class="small text-muted">{{ s.lockReason }}</span> }
|
|
636
|
+
}
|
|
637
|
+
@if (s.canLock) {
|
|
638
|
+
@if (s.locked) {
|
|
639
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-unlock" [disabled]="busy()" (click)="unlock()">{{ 'moderation.unlock' | t }}</button>
|
|
640
|
+
} @else {
|
|
641
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-lock" [disabled]="busy()" (click)="lock()">{{ 'moderation.lock' | t }}</button>
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
@if (s.canReview && s.openFlags > 0) {
|
|
645
|
+
<a class="btn btn-sm btn-outline-warning spark-open-flags" [routerLink]="reviewPath" [queryParams]="{ case: s.openCaseId }">
|
|
646
|
+
{{ 'moderation.openFlags' | t }}: {{ s.openFlags }}
|
|
647
|
+
</a>
|
|
648
|
+
}
|
|
649
|
+
@if (s.canSuspend && s.authorId) {
|
|
650
|
+
<button type="button" class="btn btn-sm btn-outline-danger spark-suspend-author" [disabled]="busy()" (click)="suspendAuthor(s.authorId)">{{ 'moderation.suspendAuthor' | t }}</button>
|
|
651
|
+
}
|
|
652
|
+
@if (error(); as message) {
|
|
653
|
+
<span class="small text-danger" role="alert">{{ message }}</span>
|
|
654
|
+
}
|
|
655
|
+
</div>
|
|
656
|
+
</bs-card>
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
`
|
|
660
|
+
}]
|
|
661
|
+
}],
|
|
662
|
+
ctorParameters: () => [],
|
|
663
|
+
propDecorators: { context: [{
|
|
664
|
+
type: i0.Input,
|
|
665
|
+
args: [{
|
|
666
|
+
isSignal: true,
|
|
667
|
+
alias: "context",
|
|
668
|
+
required: true
|
|
669
|
+
}]
|
|
670
|
+
}] }
|
|
671
|
+
});
|
|
672
|
+
var SparkReputationBadgeComponent = class SparkReputationBadgeComponent {
|
|
673
|
+
moderation = inject(SparkModerationService);
|
|
674
|
+
userId = input(...ngDevMode ? [void 0, { debugName: "userId" }] : /* istanbul ignore next */ []);
|
|
675
|
+
reputation = signal(null, ...ngDevMode ? [{ debugName: "reputation" }] : /* istanbul ignore next */ []);
|
|
676
|
+
constructor() {
|
|
677
|
+
effect(() => {
|
|
678
|
+
const userId = this.userId() ?? void 0;
|
|
679
|
+
(userId ? this.moderation.reputation(userId) : (this.moderation.ownReputationChanged(), this.moderation.ownReputation())).then((r) => this.reputation.set(r), () => this.reputation.set(null));
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
683
|
+
minVersion: "12.0.0",
|
|
684
|
+
version: "22.2.0",
|
|
685
|
+
ngImport: i0,
|
|
686
|
+
type: SparkReputationBadgeComponent,
|
|
687
|
+
deps: [],
|
|
688
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
689
|
+
});
|
|
690
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
691
|
+
minVersion: "17.0.0",
|
|
692
|
+
version: "22.2.0",
|
|
693
|
+
type: SparkReputationBadgeComponent,
|
|
694
|
+
isStandalone: true,
|
|
695
|
+
selector: "spark-reputation-badge",
|
|
696
|
+
inputs: { userId: {
|
|
697
|
+
classPropertyName: "userId",
|
|
698
|
+
publicName: "userId",
|
|
699
|
+
isSignal: true,
|
|
700
|
+
isRequired: false,
|
|
701
|
+
transformFunction: null
|
|
702
|
+
} },
|
|
703
|
+
ngImport: i0,
|
|
704
|
+
template: `
|
|
705
|
+
@if (reputation(); as r) {
|
|
706
|
+
<span class="badge bg-body-tertiary text-body border spark-reputation-badge" [attr.title]="'moderation.reputation' | t">
|
|
707
|
+
{{ r.total }}
|
|
708
|
+
@if (!userId() && r.pending) {
|
|
709
|
+
<!-- A received downvote makes the pending part negative: "(-2 pending)", never "(+-2 pending)". -->
|
|
710
|
+
<span class="text-muted spark-reputation-pending">({{ r.pending > 0 ? '+' : '' }}{{ r.pending }} {{ 'moderation.pending' | t }})</span>
|
|
711
|
+
}
|
|
712
|
+
</span>
|
|
713
|
+
}
|
|
714
|
+
`,
|
|
715
|
+
isInline: true,
|
|
716
|
+
dependencies: [{
|
|
717
|
+
kind: "pipe",
|
|
718
|
+
type: TranslateKeyPipe,
|
|
719
|
+
name: "t"
|
|
720
|
+
}],
|
|
721
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
722
|
+
});
|
|
723
|
+
};
|
|
724
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
725
|
+
minVersion: "12.0.0",
|
|
726
|
+
version: "22.2.0",
|
|
727
|
+
ngImport: i0,
|
|
728
|
+
type: SparkReputationBadgeComponent,
|
|
729
|
+
decorators: [{
|
|
730
|
+
type: Component,
|
|
731
|
+
args: [{
|
|
732
|
+
selector: "spark-reputation-badge",
|
|
733
|
+
imports: [TranslateKeyPipe],
|
|
734
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
735
|
+
template: `
|
|
736
|
+
@if (reputation(); as r) {
|
|
737
|
+
<span class="badge bg-body-tertiary text-body border spark-reputation-badge" [attr.title]="'moderation.reputation' | t">
|
|
738
|
+
{{ r.total }}
|
|
739
|
+
@if (!userId() && r.pending) {
|
|
740
|
+
<!-- A received downvote makes the pending part negative: "(-2 pending)", never "(+-2 pending)". -->
|
|
741
|
+
<span class="text-muted spark-reputation-pending">({{ r.pending > 0 ? '+' : '' }}{{ r.pending }} {{ 'moderation.pending' | t }})</span>
|
|
742
|
+
}
|
|
743
|
+
</span>
|
|
744
|
+
}
|
|
745
|
+
`
|
|
746
|
+
}]
|
|
747
|
+
}],
|
|
748
|
+
ctorParameters: () => [],
|
|
749
|
+
propDecorators: { userId: [{
|
|
750
|
+
type: i0.Input,
|
|
751
|
+
args: [{
|
|
752
|
+
isSignal: true,
|
|
753
|
+
alias: "userId",
|
|
754
|
+
required: false
|
|
755
|
+
}]
|
|
756
|
+
}] }
|
|
757
|
+
});
|
|
758
|
+
var SparkReviewQueueComponent = class SparkReviewQueueComponent {
|
|
759
|
+
moderation = inject(SparkModerationService);
|
|
760
|
+
route = inject(ActivatedRoute);
|
|
761
|
+
router = inject(Router);
|
|
762
|
+
spark = inject(SparkService);
|
|
763
|
+
cases = signal([], ...ngDevMode ? [{ debugName: "cases" }] : /* istanbul ignore next */ []);
|
|
764
|
+
targets = signal({}, ...ngDevMode ? [{ debugName: "targets" }] : /* istanbul ignore next */ []);
|
|
765
|
+
selected = signal(null, ...ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []);
|
|
766
|
+
busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
|
|
767
|
+
error = signal(null, ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
768
|
+
async ngOnInit() {
|
|
769
|
+
await this.load();
|
|
770
|
+
const requested = this.route.snapshot.queryParamMap.get("case");
|
|
771
|
+
if (requested) await this.open(requested);
|
|
772
|
+
}
|
|
773
|
+
async open(caseId) {
|
|
774
|
+
await this.run(async () => {
|
|
775
|
+
this.selected.set(await this.moderation.case(caseId));
|
|
776
|
+
await this.router.navigate([], {
|
|
777
|
+
relativeTo: this.route,
|
|
778
|
+
queryParams: { case: caseId },
|
|
779
|
+
replaceUrl: true
|
|
780
|
+
});
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
async decide(decision, accountId) {
|
|
784
|
+
const current = this.selected();
|
|
785
|
+
if (!current) return;
|
|
786
|
+
await this.run(async () => {
|
|
787
|
+
await this.moderation.decide(current.case.id, decision, accountId);
|
|
788
|
+
this.selected.set(await this.moderation.case(current.case.id));
|
|
789
|
+
await this.load();
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
async load() {
|
|
793
|
+
await this.run(async () => this.cases.set(await this.moderation.cases("open")));
|
|
794
|
+
this.resolveTargets(this.cases());
|
|
795
|
+
}
|
|
796
|
+
async resolveTargets(cases) {
|
|
797
|
+
const pending = cases.filter((c) => c.targetId && c.targetType && !(c.id in this.targets()));
|
|
798
|
+
if (!pending.length) return;
|
|
799
|
+
let types = [];
|
|
800
|
+
try {
|
|
801
|
+
types = await this.spark.getEntityTypes();
|
|
802
|
+
} catch {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
await Promise.all(pending.map(async (c) => {
|
|
806
|
+
const type = types.find((t) => t.name === c.targetType || t.id === c.targetType || t.alias === c.targetType);
|
|
807
|
+
const route = type ? type.alias || type.id : null;
|
|
808
|
+
let title = null;
|
|
809
|
+
if (route) try {
|
|
810
|
+
title = (await this.spark.get(route, c.targetId)).breadcrumb || null;
|
|
811
|
+
} catch {}
|
|
812
|
+
const link = route && title ? [
|
|
813
|
+
"/po",
|
|
814
|
+
route,
|
|
815
|
+
c.targetId
|
|
816
|
+
] : null;
|
|
817
|
+
this.targets.update((t) => ({
|
|
818
|
+
...t,
|
|
819
|
+
[c.id]: {
|
|
820
|
+
title,
|
|
821
|
+
link
|
|
822
|
+
}
|
|
823
|
+
}));
|
|
824
|
+
}));
|
|
825
|
+
}
|
|
826
|
+
async run(work) {
|
|
827
|
+
this.busy.set(true);
|
|
828
|
+
this.error.set(null);
|
|
829
|
+
try {
|
|
830
|
+
await work();
|
|
831
|
+
} catch (e) {
|
|
832
|
+
this.error.set(describeModerationError(e));
|
|
833
|
+
} finally {
|
|
834
|
+
this.busy.set(false);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
838
|
+
minVersion: "12.0.0",
|
|
839
|
+
version: "22.2.0",
|
|
840
|
+
ngImport: i0,
|
|
841
|
+
type: SparkReviewQueueComponent,
|
|
842
|
+
deps: [],
|
|
843
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
844
|
+
});
|
|
845
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
846
|
+
minVersion: "17.0.0",
|
|
847
|
+
version: "22.2.0",
|
|
848
|
+
type: SparkReviewQueueComponent,
|
|
849
|
+
isStandalone: true,
|
|
850
|
+
selector: "spark-review-queue",
|
|
851
|
+
ngImport: i0,
|
|
852
|
+
template: `
|
|
853
|
+
<div class="container-fluid spark-review-queue">
|
|
854
|
+
<h1 class="h3">{{ 'moderation.reviewQueue' | t }}</h1>
|
|
855
|
+
@if (error(); as message) { <div class="alert alert-danger" role="alert">{{ message }}</div> }
|
|
856
|
+
<div class="row g-3">
|
|
857
|
+
<div class="col-md-4">
|
|
858
|
+
@for (c of cases(); track c.id) {
|
|
859
|
+
<button type="button" class="list-group-item list-group-item-action w-100 text-start p-2 border mb-1 spark-case"
|
|
860
|
+
[class.active]="selected()?.case?.id === c.id" (click)="open(c.id)">
|
|
861
|
+
<strong>{{ c.kind }}</strong>
|
|
862
|
+
@if (c.flagCount) { <span class="badge text-bg-warning ms-1">{{ c.flagCount }}</span> }
|
|
863
|
+
@if (targets()[c.id]?.title; as title) { <div class="small fw-semibold text-truncate spark-case-target">{{ title }}</div> }
|
|
864
|
+
<div class="small">{{ c.summary }}</div>
|
|
865
|
+
<div class="small text-muted">{{ c.openedAtUtc | date: 'short' }}</div>
|
|
866
|
+
</button>
|
|
867
|
+
} @empty {
|
|
868
|
+
<p class="text-muted spark-no-cases">{{ 'moderation.noCases' | t }}</p>
|
|
869
|
+
}
|
|
870
|
+
</div>
|
|
871
|
+
<div class="col-md-8">
|
|
872
|
+
@if (selected(); as d) {
|
|
873
|
+
<bs-card class="d-block spark-case-detail">
|
|
874
|
+
<bs-card-header>{{ 'moderation.rule' | t }}: {{ d.case.kind }} — {{ d.case.summary }}</bs-card-header>
|
|
875
|
+
<div class="p-3">
|
|
876
|
+
@if (targets()[d.case.id]; as target) {
|
|
877
|
+
<p class="spark-case-target-link">
|
|
878
|
+
@if (target.link) { <a [routerLink]="target.link">{{ target.title ?? d.case.targetId }}</a> }
|
|
879
|
+
@else { {{ target.title ?? d.case.targetId }} }
|
|
880
|
+
</p>
|
|
881
|
+
}
|
|
882
|
+
@if (d.accounts.length) {
|
|
883
|
+
<h2 class="h6">{{ 'moderation.accounts' | t }}</h2>
|
|
884
|
+
<table class="table table-sm spark-case-accounts">
|
|
885
|
+
<thead><tr><th></th><th>{{ 'moderation.ageDays' | t }}</th><th></th><th>{{ 'moderation.sharesNetwork' | t }}</th><th>{{ 'moderation.repBefore' | t }}</th><th>{{ 'moderation.repAfter' | t }}</th></tr></thead>
|
|
886
|
+
<tbody>
|
|
887
|
+
@for (a of d.accounts; track a.id) {
|
|
888
|
+
<tr><td>{{ a.id }}</td><td>{{ a.ageDays }}</td><td>{{ a.registrationMethod }}</td><td>{{ a.sharesNetworkWith }}</td><td>{{ a.reputationBefore }}</td><td>{{ a.reputationAfter }}</td></tr>
|
|
889
|
+
}
|
|
890
|
+
</tbody>
|
|
891
|
+
</table>
|
|
892
|
+
}
|
|
893
|
+
@if (d.matrix.length) {
|
|
894
|
+
<h2 class="h6">{{ 'moderation.matrix' | t }}</h2>
|
|
895
|
+
<ul class="small spark-case-matrix">
|
|
896
|
+
@for (m of d.matrix; track m.voterId + m.authorId) { <li>{{ m.voterId }} → {{ m.authorId }}: {{ m.votes }}</li> }
|
|
897
|
+
</ul>
|
|
898
|
+
}
|
|
899
|
+
@if (d.timeline.length) {
|
|
900
|
+
<h2 class="h6">{{ 'moderation.timeline' | t }}</h2>
|
|
901
|
+
<table class="table table-sm small spark-case-timeline">
|
|
902
|
+
<tbody>
|
|
903
|
+
@for (t of d.timeline; track t.voteId) {
|
|
904
|
+
<tr><td>{{ t.castAtUtc | date: 'short' }}</td><td>{{ t.voterId }} → {{ t.authorId }}</td><td>{{ t.direction > 0 ? '▲' : t.direction < 0 ? '▼' : '·' }}</td><td>{{ t.secondsAfterPost ?? '' }} s</td></tr>
|
|
905
|
+
}
|
|
906
|
+
</tbody>
|
|
907
|
+
</table>
|
|
908
|
+
}
|
|
909
|
+
@if (d.flags.length) {
|
|
910
|
+
<ul class="small spark-case-flags">
|
|
911
|
+
@for (f of d.flags; track f.flaggerId) { <li>{{ f.reason }} <span class="text-muted">({{ f.raisedAtUtc | date: 'short' }})</span></li> }
|
|
912
|
+
</ul>
|
|
913
|
+
}
|
|
914
|
+
@if (d.case.status === 'open') {
|
|
915
|
+
<div class="d-flex flex-wrap gap-2">
|
|
916
|
+
@if (d.case.kind === 'flag') {
|
|
917
|
+
<button type="button" class="btn btn-sm btn-success spark-decide-uphold" [disabled]="busy()" (click)="decide('uphold')">{{ 'moderation.uphold' | t }}</button>
|
|
918
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-decide-decline" [disabled]="busy()" (click)="decide('decline')">{{ 'moderation.decline' | t }}</button>
|
|
919
|
+
} @else {
|
|
920
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-decide-dismiss" [disabled]="busy()" (click)="decide('dismiss')">{{ 'moderation.dismiss' | t }}</button>
|
|
921
|
+
<button type="button" class="btn btn-sm btn-warning spark-decide-reverse" [disabled]="busy()" (click)="decide('reverse')">{{ 'moderation.reverse' | t }}</button>
|
|
922
|
+
@for (a of d.case.accountIds; track a) {
|
|
923
|
+
<button type="button" class="btn btn-sm btn-outline-danger spark-decide-merge" [disabled]="busy()" (click)="decide('merge', a)">{{ 'moderation.merge' | t }}: {{ a }}</button>
|
|
924
|
+
<button type="button" class="btn btn-sm btn-danger spark-decide-suspend" [disabled]="busy()" (click)="decide('suspend', a)">{{ 'moderation.suspend' | t }}: {{ a }}</button>
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
</div>
|
|
928
|
+
}
|
|
929
|
+
</div>
|
|
930
|
+
</bs-card>
|
|
931
|
+
}
|
|
932
|
+
</div>
|
|
933
|
+
</div>
|
|
934
|
+
</div>
|
|
935
|
+
`,
|
|
936
|
+
isInline: true,
|
|
937
|
+
dependencies: [
|
|
938
|
+
{
|
|
939
|
+
kind: "component",
|
|
940
|
+
type: BsCardComponent,
|
|
941
|
+
selector: "bs-card",
|
|
942
|
+
inputs: ["color", "outline"]
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
kind: "component",
|
|
946
|
+
type: BsCardHeaderComponent,
|
|
947
|
+
selector: "bs-card-header",
|
|
948
|
+
inputs: ["color", "navStyle"]
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
kind: "directive",
|
|
952
|
+
type: RouterLink,
|
|
953
|
+
selector: "[routerLink]",
|
|
954
|
+
inputs: [
|
|
955
|
+
"target",
|
|
956
|
+
"queryParams",
|
|
957
|
+
"fragment",
|
|
958
|
+
"queryParamsHandling",
|
|
959
|
+
"state",
|
|
960
|
+
"info",
|
|
961
|
+
"relativeTo",
|
|
962
|
+
"preserveFragment",
|
|
963
|
+
"skipLocationChange",
|
|
964
|
+
"replaceUrl",
|
|
965
|
+
"browserUrl",
|
|
966
|
+
"routerLink"
|
|
967
|
+
]
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
kind: "pipe",
|
|
971
|
+
type: TranslateKeyPipe,
|
|
972
|
+
name: "t"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
kind: "pipe",
|
|
976
|
+
type: DatePipe,
|
|
977
|
+
name: "date"
|
|
978
|
+
}
|
|
979
|
+
],
|
|
980
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
981
|
+
});
|
|
982
|
+
};
|
|
983
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
984
|
+
minVersion: "12.0.0",
|
|
985
|
+
version: "22.2.0",
|
|
986
|
+
ngImport: i0,
|
|
987
|
+
type: SparkReviewQueueComponent,
|
|
988
|
+
decorators: [{
|
|
989
|
+
type: Component,
|
|
990
|
+
args: [{
|
|
991
|
+
selector: "spark-review-queue",
|
|
992
|
+
imports: [
|
|
993
|
+
BsCardComponent,
|
|
994
|
+
BsCardHeaderComponent,
|
|
995
|
+
RouterLink,
|
|
996
|
+
TranslateKeyPipe,
|
|
997
|
+
DatePipe
|
|
998
|
+
],
|
|
999
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1000
|
+
template: `
|
|
1001
|
+
<div class="container-fluid spark-review-queue">
|
|
1002
|
+
<h1 class="h3">{{ 'moderation.reviewQueue' | t }}</h1>
|
|
1003
|
+
@if (error(); as message) { <div class="alert alert-danger" role="alert">{{ message }}</div> }
|
|
1004
|
+
<div class="row g-3">
|
|
1005
|
+
<div class="col-md-4">
|
|
1006
|
+
@for (c of cases(); track c.id) {
|
|
1007
|
+
<button type="button" class="list-group-item list-group-item-action w-100 text-start p-2 border mb-1 spark-case"
|
|
1008
|
+
[class.active]="selected()?.case?.id === c.id" (click)="open(c.id)">
|
|
1009
|
+
<strong>{{ c.kind }}</strong>
|
|
1010
|
+
@if (c.flagCount) { <span class="badge text-bg-warning ms-1">{{ c.flagCount }}</span> }
|
|
1011
|
+
@if (targets()[c.id]?.title; as title) { <div class="small fw-semibold text-truncate spark-case-target">{{ title }}</div> }
|
|
1012
|
+
<div class="small">{{ c.summary }}</div>
|
|
1013
|
+
<div class="small text-muted">{{ c.openedAtUtc | date: 'short' }}</div>
|
|
1014
|
+
</button>
|
|
1015
|
+
} @empty {
|
|
1016
|
+
<p class="text-muted spark-no-cases">{{ 'moderation.noCases' | t }}</p>
|
|
1017
|
+
}
|
|
1018
|
+
</div>
|
|
1019
|
+
<div class="col-md-8">
|
|
1020
|
+
@if (selected(); as d) {
|
|
1021
|
+
<bs-card class="d-block spark-case-detail">
|
|
1022
|
+
<bs-card-header>{{ 'moderation.rule' | t }}: {{ d.case.kind }} — {{ d.case.summary }}</bs-card-header>
|
|
1023
|
+
<div class="p-3">
|
|
1024
|
+
@if (targets()[d.case.id]; as target) {
|
|
1025
|
+
<p class="spark-case-target-link">
|
|
1026
|
+
@if (target.link) { <a [routerLink]="target.link">{{ target.title ?? d.case.targetId }}</a> }
|
|
1027
|
+
@else { {{ target.title ?? d.case.targetId }} }
|
|
1028
|
+
</p>
|
|
1029
|
+
}
|
|
1030
|
+
@if (d.accounts.length) {
|
|
1031
|
+
<h2 class="h6">{{ 'moderation.accounts' | t }}</h2>
|
|
1032
|
+
<table class="table table-sm spark-case-accounts">
|
|
1033
|
+
<thead><tr><th></th><th>{{ 'moderation.ageDays' | t }}</th><th></th><th>{{ 'moderation.sharesNetwork' | t }}</th><th>{{ 'moderation.repBefore' | t }}</th><th>{{ 'moderation.repAfter' | t }}</th></tr></thead>
|
|
1034
|
+
<tbody>
|
|
1035
|
+
@for (a of d.accounts; track a.id) {
|
|
1036
|
+
<tr><td>{{ a.id }}</td><td>{{ a.ageDays }}</td><td>{{ a.registrationMethod }}</td><td>{{ a.sharesNetworkWith }}</td><td>{{ a.reputationBefore }}</td><td>{{ a.reputationAfter }}</td></tr>
|
|
1037
|
+
}
|
|
1038
|
+
</tbody>
|
|
1039
|
+
</table>
|
|
1040
|
+
}
|
|
1041
|
+
@if (d.matrix.length) {
|
|
1042
|
+
<h2 class="h6">{{ 'moderation.matrix' | t }}</h2>
|
|
1043
|
+
<ul class="small spark-case-matrix">
|
|
1044
|
+
@for (m of d.matrix; track m.voterId + m.authorId) { <li>{{ m.voterId }} → {{ m.authorId }}: {{ m.votes }}</li> }
|
|
1045
|
+
</ul>
|
|
1046
|
+
}
|
|
1047
|
+
@if (d.timeline.length) {
|
|
1048
|
+
<h2 class="h6">{{ 'moderation.timeline' | t }}</h2>
|
|
1049
|
+
<table class="table table-sm small spark-case-timeline">
|
|
1050
|
+
<tbody>
|
|
1051
|
+
@for (t of d.timeline; track t.voteId) {
|
|
1052
|
+
<tr><td>{{ t.castAtUtc | date: 'short' }}</td><td>{{ t.voterId }} → {{ t.authorId }}</td><td>{{ t.direction > 0 ? '▲' : t.direction < 0 ? '▼' : '·' }}</td><td>{{ t.secondsAfterPost ?? '' }} s</td></tr>
|
|
1053
|
+
}
|
|
1054
|
+
</tbody>
|
|
1055
|
+
</table>
|
|
1056
|
+
}
|
|
1057
|
+
@if (d.flags.length) {
|
|
1058
|
+
<ul class="small spark-case-flags">
|
|
1059
|
+
@for (f of d.flags; track f.flaggerId) { <li>{{ f.reason }} <span class="text-muted">({{ f.raisedAtUtc | date: 'short' }})</span></li> }
|
|
1060
|
+
</ul>
|
|
1061
|
+
}
|
|
1062
|
+
@if (d.case.status === 'open') {
|
|
1063
|
+
<div class="d-flex flex-wrap gap-2">
|
|
1064
|
+
@if (d.case.kind === 'flag') {
|
|
1065
|
+
<button type="button" class="btn btn-sm btn-success spark-decide-uphold" [disabled]="busy()" (click)="decide('uphold')">{{ 'moderation.uphold' | t }}</button>
|
|
1066
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-decide-decline" [disabled]="busy()" (click)="decide('decline')">{{ 'moderation.decline' | t }}</button>
|
|
1067
|
+
} @else {
|
|
1068
|
+
<button type="button" class="btn btn-sm btn-outline-secondary spark-decide-dismiss" [disabled]="busy()" (click)="decide('dismiss')">{{ 'moderation.dismiss' | t }}</button>
|
|
1069
|
+
<button type="button" class="btn btn-sm btn-warning spark-decide-reverse" [disabled]="busy()" (click)="decide('reverse')">{{ 'moderation.reverse' | t }}</button>
|
|
1070
|
+
@for (a of d.case.accountIds; track a) {
|
|
1071
|
+
<button type="button" class="btn btn-sm btn-outline-danger spark-decide-merge" [disabled]="busy()" (click)="decide('merge', a)">{{ 'moderation.merge' | t }}: {{ a }}</button>
|
|
1072
|
+
<button type="button" class="btn btn-sm btn-danger spark-decide-suspend" [disabled]="busy()" (click)="decide('suspend', a)">{{ 'moderation.suspend' | t }}: {{ a }}</button>
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
</div>
|
|
1076
|
+
}
|
|
1077
|
+
</div>
|
|
1078
|
+
</bs-card>
|
|
1079
|
+
}
|
|
1080
|
+
</div>
|
|
1081
|
+
</div>
|
|
1082
|
+
</div>
|
|
1083
|
+
`
|
|
1084
|
+
}]
|
|
1085
|
+
}]
|
|
1086
|
+
});
|
|
1087
|
+
var SparkReviewQueueLinkComponent = class SparkReviewQueueLinkComponent {
|
|
1088
|
+
moderation = inject(SparkModerationService);
|
|
1089
|
+
linkClass = input("", ...ngDevMode ? [{ debugName: "linkClass" }] : /* istanbul ignore next */ []);
|
|
1090
|
+
canReview = signal(false, ...ngDevMode ? [{ debugName: "canReview" }] : /* istanbul ignore next */ []);
|
|
1091
|
+
constructor() {
|
|
1092
|
+
effect(() => {
|
|
1093
|
+
this.moderation.ownReputationChanged();
|
|
1094
|
+
this.moderation.ownReputation().then((r) => this.canReview.set(r.canReview === true), () => this.canReview.set(false));
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1098
|
+
minVersion: "12.0.0",
|
|
1099
|
+
version: "22.2.0",
|
|
1100
|
+
ngImport: i0,
|
|
1101
|
+
type: SparkReviewQueueLinkComponent,
|
|
1102
|
+
deps: [],
|
|
1103
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
1104
|
+
});
|
|
1105
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1106
|
+
minVersion: "17.0.0",
|
|
1107
|
+
version: "22.2.0",
|
|
1108
|
+
type: SparkReviewQueueLinkComponent,
|
|
1109
|
+
isStandalone: true,
|
|
1110
|
+
selector: "spark-review-queue-link",
|
|
1111
|
+
inputs: { linkClass: {
|
|
1112
|
+
classPropertyName: "linkClass",
|
|
1113
|
+
publicName: "linkClass",
|
|
1114
|
+
isSignal: true,
|
|
1115
|
+
isRequired: false,
|
|
1116
|
+
transformFunction: null
|
|
1117
|
+
} },
|
|
1118
|
+
host: { properties: { "style.display": "canReview() ? null : \"none\"" } },
|
|
1119
|
+
ngImport: i0,
|
|
1120
|
+
template: `
|
|
1121
|
+
@if (canReview()) {
|
|
1122
|
+
<a class="spark-review-queue-link" [class]="linkClass()" routerLink="/moderation/review">{{ 'moderation.reviewQueue' | t }}</a>
|
|
1123
|
+
}
|
|
1124
|
+
`,
|
|
1125
|
+
isInline: true,
|
|
1126
|
+
dependencies: [{
|
|
1127
|
+
kind: "directive",
|
|
1128
|
+
type: RouterLink,
|
|
1129
|
+
selector: "[routerLink]",
|
|
1130
|
+
inputs: [
|
|
1131
|
+
"target",
|
|
1132
|
+
"queryParams",
|
|
1133
|
+
"fragment",
|
|
1134
|
+
"queryParamsHandling",
|
|
1135
|
+
"state",
|
|
1136
|
+
"info",
|
|
1137
|
+
"relativeTo",
|
|
1138
|
+
"preserveFragment",
|
|
1139
|
+
"skipLocationChange",
|
|
1140
|
+
"replaceUrl",
|
|
1141
|
+
"browserUrl",
|
|
1142
|
+
"routerLink"
|
|
1143
|
+
]
|
|
1144
|
+
}, {
|
|
1145
|
+
kind: "pipe",
|
|
1146
|
+
type: TranslateKeyPipe,
|
|
1147
|
+
name: "t"
|
|
1148
|
+
}],
|
|
1149
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
1150
|
+
});
|
|
1151
|
+
};
|
|
1152
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1153
|
+
minVersion: "12.0.0",
|
|
1154
|
+
version: "22.2.0",
|
|
1155
|
+
ngImport: i0,
|
|
1156
|
+
type: SparkReviewQueueLinkComponent,
|
|
1157
|
+
decorators: [{
|
|
1158
|
+
type: Component,
|
|
1159
|
+
args: [{
|
|
1160
|
+
selector: "spark-review-queue-link",
|
|
1161
|
+
imports: [RouterLink, TranslateKeyPipe],
|
|
1162
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1163
|
+
host: { "[style.display]": "canReview() ? null : \"none\"" },
|
|
1164
|
+
template: `
|
|
1165
|
+
@if (canReview()) {
|
|
1166
|
+
<a class="spark-review-queue-link" [class]="linkClass()" routerLink="/moderation/review">{{ 'moderation.reviewQueue' | t }}</a>
|
|
1167
|
+
}
|
|
1168
|
+
`
|
|
1169
|
+
}]
|
|
1170
|
+
}],
|
|
1171
|
+
ctorParameters: () => [],
|
|
1172
|
+
propDecorators: { linkClass: [{
|
|
1173
|
+
type: i0.Input,
|
|
1174
|
+
args: [{
|
|
1175
|
+
isSignal: true,
|
|
1176
|
+
alias: "linkClass",
|
|
1177
|
+
required: false
|
|
1178
|
+
}]
|
|
1179
|
+
}] }
|
|
1180
|
+
});
|
|
1181
|
+
function provideSparkModeration(options) {
|
|
1182
|
+
return [
|
|
1183
|
+
...provideSparkDetailActions({
|
|
1184
|
+
id: "spark-moderation-flag",
|
|
1185
|
+
component: SparkFlagButtonComponent,
|
|
1186
|
+
priority: 90
|
|
1187
|
+
}),
|
|
1188
|
+
...provideSparkDetailPanels({
|
|
1189
|
+
id: "spark-moderation-panel",
|
|
1190
|
+
component: SparkModeratorPanelComponent,
|
|
1191
|
+
order: options?.panelOrder ?? 90
|
|
1192
|
+
}),
|
|
1193
|
+
{
|
|
1194
|
+
provide: SPARK_MODERATION_REVIEW_PATH,
|
|
1195
|
+
useValue: options?.reviewPath ?? "/moderation/review"
|
|
1196
|
+
}
|
|
1197
|
+
];
|
|
1198
|
+
}
|
|
1199
|
+
const sparkModerationRenderers = [{
|
|
1200
|
+
name: "spark-vote",
|
|
1201
|
+
detailComponent: SparkVoteComponent,
|
|
1202
|
+
columnComponent: SparkVoteComponent
|
|
1203
|
+
}];
|
|
1204
|
+
function sparkModerationRoutes(path = "moderation/review") {
|
|
1205
|
+
return [{
|
|
1206
|
+
path,
|
|
1207
|
+
component: SparkReviewQueueComponent
|
|
1208
|
+
}];
|
|
1209
|
+
}
|
|
1210
|
+
export { SPARK_MODERATION_REVIEW_PATH, SparkFlagButtonComponent, SparkModerationService, SparkModeratorPanelComponent, SparkReputationBadgeComponent, SparkReviewQueueComponent, SparkReviewQueueLinkComponent, SparkVoteComponent, describeModerationError, provideSparkModeration, sparkModerationRenderers, sparkModerationRoutes };
|
|
1211
|
+
|
|
1212
|
+
//# sourceMappingURL=mintplayer-ng-spark-moderation.mjs.map
|