Skip to content

Commit

Permalink
Added force delete button
Browse files Browse the repository at this point in the history
  • Loading branch information
cjmalloy committed Oct 3, 2024
1 parent afd117e commit eef9a05
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 48 deletions.
6 changes: 5 additions & 1 deletion src/app/component/blog/blog-entry/blog-entry.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ <h2 class="link blog-title" [class.remote]="!local">
<a *ngIf="responses" [routerLink]="['/ref', ref.url, 'responses']" [queryParams]="{ origin: nonLocalOrigin }" i18n>{responses, plural, =1 {1&thinsp;citation} other {{{ responses }}&thinsp;citations}}</a>
<a *ngIf="sources === 1" [routerLink]="['/ref', ref.sources![0]]" [queryParams]="{ origin: nonLocalOrigin }" i18n>parent</a>
<a *ngIf="sources > 1" [routerLink]="['/ref', ref.url, 'sources']" [queryParams]="{ origin: nonLocalOrigin }" i18n>{{ sources }}&thinsp;sources</a>
<app-confirm-action #action *ngIf="store.account.mod || writeAccess" [action]="delete$" i18n>delete</app-confirm-action>
@if (store.account.mod || taggingAccess) {
<app-confirm-action #action
[class.force-delete]="deleteAccess"
[action]="store.hotkey ? forceDelete$ : delete$" i18n>delete</app-confirm-action>
}
<app-inline-tag #action *ngIf="taggingAccess" [action]="tag$" i18n>tag</app-inline-tag>
<a *ngIf="writeAccess else source"
class="fake-link"
Expand Down
13 changes: 13 additions & 0 deletions src/app/component/blog/blog-entry/blog-entry.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export class BlogEntryComponent implements OnChanges, OnDestroy {
deleted = false;
writeAccess = false;
taggingAccess = false;
deleteAccess = false;
serverError: string[] = [];

submitting?: Subscription;
Expand Down Expand Up @@ -121,6 +122,7 @@ export class BlogEntryComponent implements OnChanges, OnDestroy {
this.actionComponents?.forEach(c => c.reset());
this.writeAccess = this.auth.writeAccess(this.ref);
this.taggingAccess = this.auth.taggingAccess(this.ref);
this.deleteAccess = this.auth.deleteAccess(this.ref);
this.icons = uniqueConfigs(sortOrder(this.admin.getIcons(this.ref.tags, this.ref.plugins, getScheme(this.ref.url))));
this.actions = uniqueConfigs(sortOrder(this.admin.getActions(this.ref.tags, this.ref.plugins)));
this.groupedActions = groupBy(this.actions.filter(a => this.showAction(a)), a => (a as any)[this.label(a)]);
Expand Down Expand Up @@ -394,6 +396,17 @@ export class BlogEntryComponent implements OnChanges, OnDestroy {
});
}

forceDelete$ = () => {
this.serverError = [];
return this.refs.delete(this.ref.url, this.ref.origin).pipe(
tap(() => this.deleted = true),
catchError((err: HttpErrorResponse) => {
this.serverError = printError(err);
return throwError(() => err);
}),
);
}

delete$ = () => {
this.serverError = [];
return (this.admin.getPlugin('plugin/delete')
Expand Down
2 changes: 1 addition & 1 deletion src/app/component/bulk/bulk.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<app-inline-url [action]="thumbnail$" [value]="defaultThumbnail" i18n>thumbnail</app-inline-url>
<app-confirm-action [action]="copy$" i18n>copy</app-confirm-action>
}
<app-confirm-action [action]="delete$" i18n>delete</app-confirm-action>
<app-confirm-action class="force-delete" [action]="store.hotkey ? forceDelete$ : delete$" i18n>delete</app-confirm-action>
@if (type === 'ref') {
@if (admin.getPlugin('plugin/invoice')) {
<a routerLink="/submit/invoice" [queryParams]="{url: urls}" i18n>invoice</a>
Expand Down
8 changes: 8 additions & 0 deletions src/app/component/bulk/bulk.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ export class BulkComponent implements OnChanges, OnDestroy {
}
}

forceDelete$ = () => {
if (this.type === 'ref') {
return this.batch$<Ref>(ref => this.refs.delete(ref.url, ref.origin));
} else {
return this.delete$();
}
}

copy$ = () => {
return this.batch$<Ref>(ref => {
if (ref.origin === this.store.account.origin) return of(null);
Expand Down
50 changes: 32 additions & 18 deletions src/app/component/chat/chat-entry/chat-entry.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,40 @@
} @else {
<span>{{ title }}</span>
}
<app-md *ngIf="focused && bareRef?.comment"
[origin]="ref.origin!"
[text]="bareRef!.comment!"
[plugins]="bareRef!.tags!"></app-md>
@if (focused && bareRef?.comment) {
<app-md [origin]="ref.origin!"
[text]="bareRef!.comment!"
[plugins]="bareRef!.tags!"></app-md>
}
<app-viewer [ref]="noComment"></app-viewer>
</div>
<div class="actions"
*ngIf="ref.created"
[style.pointer-events]="allowActions ? 'auto' : 'none'">
<a *ngIf="comments"
[routerLink]="['/ref', ref.url, 'comments']" [queryParams]="{ origin: nonLocalOrigin }" i18n>{comments, plural, =1 {1&thinsp;comment} other {{{ comments }}&thinsp;comments}}</a>
<a *ngIf="!comments"
[routerLink]="['/ref', ref.url]" [queryParams]="{ origin: nonLocalOrigin }" i18n>permalink</a>
<app-confirm-action #action *ngIf="store.account.mod || writeAccess" [action]="delete$" i18n>delete</app-confirm-action>
<app-inline-tag #action *ngIf="taggingAccess" [action]="tag$" i18n>tag</app-inline-tag>
<a *ngIf="!ref.origin && store.account.mod && !approved && !locked"
class="fake-link"
(click)="approve()" i18n>approve</a>
<span class="approved icon" *ngIf="store.account.mod && approved" i18n-title title="Moderated" i18n>✔️</span>
</div>
@if (ref.created) {
<div class="actions" [style.pointer-events]="allowActions ? 'auto' : 'none'">
@if (comments) {
<a [routerLink]="['/ref', ref.url, 'comments']"
[queryParams]="{ origin: nonLocalOrigin }"
i18n>{comments, plural, =1 {1&thinsp;comment} other {{{ comments }}&thinsp;comments}}</a>
} @else {
<a [routerLink]="['/ref', ref.url]"
[queryParams]="{ origin: nonLocalOrigin }"
i18n>permalink</a>
}
@if (store.account.mod || taggingAccess) {
<app-confirm-action #action
[class.force-delete]="deleteAccess"
[action]="store.hotkey ? forceDelete$ : delete$" i18n>delete</app-confirm-action>
}
@if (taggingAccess) {
<app-inline-tag #action [action]="tag$" i18n>tag</app-inline-tag>
}
@if (!ref.origin && store.account.mod && !approved && !locked) {
<a class="fake-link" (click)="approve()" i18n>approve</a>
}
@if (store.account.mod && approved) {
<span class="approved icon" i18n-title title="Moderated" i18n>✔️</span>
}
</div>
}
</span>
@for (e of serverError; track e) {
<span><!-- Unexpected Error --></span>
Expand Down
13 changes: 13 additions & 0 deletions src/app/component/chat/chat-entry/chat-entry.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class ChatEntryComponent implements OnChanges {
deleted = false;
writeAccess = false;
taggingAccess = false;
deleteAccess = false;
serverError: string[] = [];

private _allowActions = false;
Expand All @@ -61,6 +62,7 @@ export class ChatEntryComponent implements OnChanges {
this.actionComponents?.forEach(c => c.reset());
this.writeAccess = this.auth.writeAccess(this.ref);
this.taggingAccess = this.auth.taggingAccess(this.ref);
this.deleteAccess = this.auth.deleteAccess(this.ref);
if (this.ref && this.bareRepost && !this.repostRef) {
this.refs.getCurrent(this.url).pipe(
catchError(err => err.status === 404 ? of(undefined) : throwError(() => err)),
Expand Down Expand Up @@ -242,6 +244,17 @@ export class ChatEntryComponent implements OnChanges {
});
}

forceDelete$ = () => {
this.serverError = [];
return this.refs.delete(this.ref.url, this.ref.origin).pipe(
tap(() => this.deleted = true),
catchError((err: HttpErrorResponse) => {
this.serverError = printError(err);
return throwError(() => err);
}),
);
}

delete$ = () => {
this.serverError = [];
return (this.admin.getPlugin('plugin/delete')
Expand Down
76 changes: 50 additions & 26 deletions src/app/component/comment/comment.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,39 +86,63 @@
[ref]="ref">{{ icon.label }}</span>
</ng-container>
</div>
<app-viewer *ngIf="!editing"
class="comment-body"
[expand]="false"
[ref]="ref"></app-viewer>
<app-comment-edit *ngIf="editing"
[ref]="ref"
[commentEdited$]="commentEdited$"></app-comment-edit>
@if (editing) {
<app-comment-edit [ref]="ref"
[commentEdited$]="commentEdited$"></app-comment-edit>
} @else {
<app-viewer class="comment-body"
[expand]="false"
[ref]="ref"></app-viewer>
}
<div class="actions">
<a [routerLink]="['/ref', ref.url, 'comments']" [queryParams]="{ origin: nonLocalOrigin }" i18n>permalink</a>
<a class="fake-link" (click)="replying = !replying" i18n>reply</a>
<a *ngIf="responses" [routerLink]="['/ref', ref.url, 'responses']" [queryParams]="{ origin: nonLocalOrigin }" i18n>{responses, plural, =1 {1&thinsp;citation} other {{{ responses }}&thinsp;citations}}</a>
<a *ngIf="sources > 2" [routerLink]="['/ref', ref.url, 'sources']" [queryParams]="{ origin: nonLocalOrigin }" i18n>{{ sources }}&thinsp;sources</a>
<a [routerLink]="['/ref', ref.url, 'comments']"
[queryParams]="{ origin: nonLocalOrigin }"
i18n>permalink</a>
<a class="fake-link"
*ngIf="writeAccess"
(click)="editing = !editing" i18n>edit</a>
<app-confirm-action #action *ngIf="store.account.mod || writeAccess" [action]="delete$" i18n>delete</app-confirm-action>
<app-inline-tag #action *ngIf="taggingAccess" [action]="tag$" i18n>tag</app-inline-tag>
<ng-container *ngIf="canInvoice">
(click)="replying = !replying"
i18n>reply</a>
@if(responses) {
<a [routerLink]="['/ref', ref.url, 'responses']"
[queryParams]="{ origin: nonLocalOrigin }"
i18n>{responses, plural, =1 {1&thinsp;citation} other {{{ responses }}&thinsp;citations}}</a>
}
@if (sources > 2) {
<a [routerLink]="['/ref', ref.url, 'sources']"
[queryParams]="{ origin: nonLocalOrigin }"
i18n>{{ sources }}&thinsp;sources</a>
}
@if (writeAccess) {
<a class="fake-link"
(click)="editing = !editing" i18n>edit</a>
}
@if (store.account.mod || taggingAccess) {
<app-confirm-action #action
[class.force-delete]="deleteAccess"
[action]="store.hotkey ? forceDelete$ : delete$" i18n>delete</app-confirm-action>
}
@if (taggingAccess) {
<app-inline-tag #action
[action]="tag$"
i18n>tag</app-inline-tag>
}
@if (canInvoice) {
<a [routerLink]="['/submit/invoice']"
[queryParams]="{ url: ref.url }" i18n>invoice</a>
</ng-container>
[queryParams]="{ url: ref.url }"
i18n>invoice</a>
}
<app-action-list [ref]="ref"
[showDownload]="false"
[groupedActions]="groupedActions"></app-action-list>
</div>
</div>
</div>
<app-comment-reply *ngIf="replying"
[autofocus]="true"
[to]="ref"
[showCancel]="true"
[tags]="replyTags"
(save)="newComments$.next($event)"></app-comment-reply>
@if (replying) {
<app-comment-reply [autofocus]="true"
[to]="ref"
[showCancel]="true"
[tags]="replyTags"
(save)="newComments$.next($event)"></app-comment-reply>
}
<div class="comment-children"
[class.hidden-without-removing]="collapsed">
<app-comment-thread [source]="ref"
Expand All @@ -127,7 +151,7 @@
[context]="context + 1"
[newComments$]="newComments$"></app-comment-thread>
</div>
<ng-container *ngIf="(depth === 1 && comments) || moreComments">
@if ((depth === 1 && comments) || moreComments) {
<span class="fake-link load-more no-select"
*ngIf="!collapsed && context < maxContext"
(click)="loadMore()" i18n>
Expand All @@ -138,7 +162,7 @@
[routerLink]="['/ref', ref.url, 'comments']" [queryParams]="{ origin: nonLocalOrigin }" i18n>
continue this thread
</a>
</ng-container>
}

@for (e of serverError; track e) {
<div class="error">{{ e }}</div>
Expand Down
9 changes: 9 additions & 0 deletions src/app/component/comment/comment.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class CommentComponent implements OnInit, AfterViewInit, OnChanges, OnDes
editing = false;
writeAccess = false;
taggingAccess = false;
deleteAccess = false;
serverError: string[] = [];

constructor(
Expand Down Expand Up @@ -143,6 +144,7 @@ export class CommentComponent implements OnInit, AfterViewInit, OnChanges, OnDes
this.collapsed = !this.store.local.isRefToggled('comment:' + this.ref.url, true);
this.writeAccess = this.auth.writeAccess(this.ref);
this.taggingAccess = this.auth.taggingAccess(this.ref);
this.deleteAccess = this.auth.deleteAccess(this.ref);
this.icons = uniqueConfigs(sortOrder(this.admin.getIcons(this.ref.tags, this.ref.plugins, getScheme(this.ref.url))));
this.actions = uniqueConfigs(sortOrder(this.admin.getActions(this.ref.tags, this.ref.plugins)));
this.groupedActions = groupBy(this.actions.filter(a => this.showAction(a)), a => (a as any)[this.label(a)]);
Expand Down Expand Up @@ -377,6 +379,13 @@ export class CommentComponent implements OnInit, AfterViewInit, OnChanges, OnDes
}
}

forceDelete$ = () => {
const deleted = deleteNotice(this.ref);
deleted.sources = this.ref.sources;
deleted.tags = ['plugin/comment', 'plugin/delete', 'internal'];
return this.store.eventBus.runAndReload$(this.refs.delete(this.ref.url, this.ref.origin), deleted);
}

delete$ = () => {
const deleted = deleteNotice(this.ref);
deleted.sources = this.ref.sources;
Expand Down
6 changes: 5 additions & 1 deletion src/app/component/ref/ref.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,11 @@
<a class="fake-link" (click)="viewSource = !viewSource" i18n>source</a>
}
}
<app-confirm-action #action *ngIf="store.account.mod || taggingAccess" [action]="delete$" i18n>delete</app-confirm-action>
@if (store.account.mod || taggingAccess) {
<app-confirm-action #action
[class.force-delete]="deleteAccess"
[action]="store.hotkey ? forceDelete$ : delete$" i18n>delete</app-confirm-action>
}
}
@if (ref.upload) {
@if (ref.exists) {
Expand Down
18 changes: 17 additions & 1 deletion src/app/component/ref/ref.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export class RefComponent implements OnChanges, AfterViewInit, OnDestroy {
replying = false;
writeAccess = false;
taggingAccess = false;
deleteAccess = false;
serverError: string[] = [];
publishChanged = false;

Expand Down Expand Up @@ -219,6 +220,7 @@ export class RefComponent implements OnChanges, AfterViewInit, OnDestroy {
if (this.ref?.upload) this.editForm.get('url')!.enable();
this.writeAccess = this.auth.writeAccess(this.ref);
this.taggingAccess = this.auth.taggingAccess(this.ref);
this.deleteAccess = this.auth.deleteAccess(this.ref);
this.initFields(this.ref);

this.expandPlugins = this.admin.getEmbeds(this.ref);
Expand Down Expand Up @@ -1016,6 +1018,17 @@ export class RefComponent implements OnChanges, AfterViewInit, OnDestroy {
)), ref);
}

forceDelete$ = () => {
this.serverError = [];
return this.refs.delete(this.ref.url, this.ref.origin).pipe(
tap(() => this.deleted = true),
catchError((err: HttpErrorResponse) => {
this.serverError = printError(err);
return throwError(() => err);
}),
);
}

delete$ = () => {
this.serverError = [];
return (this.local && hasTag('locked', this.ref)
Expand All @@ -1024,7 +1037,10 @@ export class RefComponent implements OnChanges, AfterViewInit, OnDestroy {
? this.refs.update(deleteNotice(this.ref)).pipe(map(() => {}))
: this.refs.delete(this.ref.url, this.ref.origin)
).pipe(
tap(() => this.deleted = true),
tap((cursor: any) => {
if (cursor)
this.deleted = true;
}),
catchError((err: HttpErrorResponse) => {
this.serverError = printError(err);
return throwError(() => err);
Expand Down
7 changes: 7 additions & 0 deletions src/app/service/authz.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ export class AuthzService {
return !!capturesAny(this.store.account.access.writeAccess, qualifyTags(ref.tags, ref.origin));
}

deleteAccess(ref: Ref): boolean {
if (!this.store.account.signedIn) return false;
if (this.store.account.mod) return true;
if (ref.origin !== this.store.account.origin) return false;
return this.taggingAccess(ref);
}

queryReadAccess(query?: string): boolean {
if (!query) return false;
for (const part of query.split(/[-|:!()\s]+/)) {
Expand Down
4 changes: 4 additions & 0 deletions src/theme/common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,10 @@ formly-field-radio, formly-field-multicheckbox {
}
}

.hotkey .action.force-delete .fake-link {
color: var(--active) !important;
}

.ref.parent-ref {
margin-left: 20px;
.thumbnail {
Expand Down

0 comments on commit eef9a05

Please sign in to comment.