Merge branch 'master' of ssh://git.iqser.com:2222/red/ui

This commit is contained in:
Timo Bejan 2022-02-11 13:43:33 +02:00
commit f92baa8170
24 changed files with 173 additions and 161 deletions

View File

@ -13,12 +13,12 @@ import { AnnotationWrapper } from './annotation.wrapper';
import * as moment from 'moment';
import { BehaviorSubject } from 'rxjs';
import { RedactionLogEntry } from './redaction-log.entry';
import { MISSING_TYPES_ERROR } from '@utils/constants';
export class FileDataModel {
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
allAnnotations: AnnotationWrapper[] = [];
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
missingTypes = new Set<string>();
constructor(
private readonly _file: File,
@ -77,7 +77,6 @@ export class FileDataModel {
private _convertData(): RedactionLogEntry[] {
let result: RedactionLogEntry[] = [];
const missingTypes = new Set<string>();
const reasonAnnotationIds: { [key: string]: RedactionLogEntry[] } = {};
this.redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
@ -85,7 +84,7 @@ export class FileDataModel {
const changeLogValues = this.#getChangeLogValues(redactionLogEntry);
if (!this._dictionaryData[redactionLogEntry.type]) {
missingTypes.add(redactionLogEntry.type);
this.missingTypes.add(redactionLogEntry.type);
return;
}
@ -95,7 +94,7 @@ export class FileDataModel {
changeLogValues.isChangeLogEntry,
changeLogValues.hidden,
this.redactionLog.legalBasis,
this._dictionaryData[redactionLogEntry.type].hint,
!!this._dictionaryData[redactionLogEntry.type]?.hint,
);
if (
@ -130,10 +129,6 @@ export class FileDataModel {
result = result.filter(r => !r.hidden);
if (missingTypes.size > 0) {
throw new Error(MISSING_TYPES_ERROR);
}
return result;
}

View File

@ -34,7 +34,7 @@
<iqser-circle-button
(action)="hardDelete()"
*ngIf="listingService.areSomeSelected$ | async"
*ngIf="canHardDeleteSelected$ | async"
[tooltip]="'trash.bulk.delete' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:trash"
@ -88,6 +88,7 @@
<iqser-circle-button
(action)="hardDelete([entity])"
*ngIf="entity.canHardDelete"
[tooltip]="'trash.action.delete' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:trash"

View File

@ -20,9 +20,11 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
import { getLeftDateTime } from '@utils/functions';
import { RouterHistoryService } from '@services/router-history.service';
import { IDossier } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
interface DossierListItem extends IDossier, IListable {
readonly canRestore: boolean;
readonly canHardDelete: boolean;
readonly restoreDate: string;
}
@ -36,6 +38,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
readonly circleButtonTypes = CircleButtonTypes;
readonly tableHeaderLabel = _('trash.table-header.title');
readonly canRestoreSelected$ = this._canRestoreSelected$;
readonly canHardDeleteSelected$ = this._canHardDeleteSelected$;
readonly tableColumnConfigs: TableColumnConfig<DossierListItem>[] = [
{ label: _('trash.table-col-names.name'), sortByKey: 'searchKey' },
{ label: _('trash.table-col-names.owner'), class: 'user-column' },
@ -46,6 +49,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
constructor(
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _permissionsService: PermissionsService,
private readonly _dossiersService: DossiersService,
readonly routerHistoryService: RouterHistoryService,
private readonly _configService: ConfigService,
@ -61,6 +65,13 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
);
}
private get _canHardDeleteSelected$(): Observable<boolean> {
return this.listingService.selectedEntities$.pipe(
map(entities => entities.length && !entities.find(dossier => !dossier.canHardDelete)),
distinctUntilChanged(),
);
}
disabledFn = (dossier: DossierListItem) => !dossier.canRestore;
async ngOnInit(): Promise<void> {
@ -118,6 +129,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
searchKey: dossier.dossierName,
restoreDate,
canRestore: this._canRestoreDossier(restoreDate),
canHardDelete: this._permissionsService.canDeleteDossier(dossier),
// Because of migrations, for some this is not set
softDeletedTime: dossier.softDeletedTime || '-',
};

View File

@ -40,7 +40,7 @@ export class FileWorkloadComponent {
}
get redactionColor() {
return this._getDictionaryColor('dossier_redaction');
return this._getDictionaryColor('redaction');
}
private _getDictionaryColor(type: string) {

View File

@ -23,6 +23,6 @@ export class DossierWorkloadColumnComponent {
}
get redactionColor() {
return this._appStateService.getDictionaryColor('dossier_redaction', this.dossier.dossierTemplateId);
return this._appStateService.getDictionaryColor('redaction', this.dossier.dossierTemplateId);
}
}

View File

@ -1,12 +1,6 @@
<iqser-status-bar *ngIf="stats" [configs]="statusBarConfig"></iqser-status-bar>
<div
(longPress)="forceReanalysisAction($event)"
class="action-buttons"
redactionLongPress
iqserHelpMode="dossier-features"
[updateElementPosition]="false"
>
<div (longPress)="forceReanalysisAction($event)" class="action-buttons" redactionLongPress>
<iqser-circle-button
(action)="openEditDossierDialog($event, dossier.dossierId)"
*ngIf="currentUser.isUser"

View File

@ -14,7 +14,6 @@
[noMatchText]="'dossier-listing.no-match.title' | translate"
[showNoDataButton]="currentUser.isManager"
[tableColumnConfigs]="tableColumnConfigs"
helpModeKey="dossier-list"
noDataIcon="red:folder"
></iqser-table>
</div>

View File

@ -1,4 +1,8 @@
<div *ngIf="canPerformAnnotationActions && annotationPermissions" [class.always-visible]="alwaysVisible" class="annotation-actions">
<div
*ngIf="canPerformAnnotationActions && annotationPermissions"
[class.always-visible]="alwaysVisible || (helpModeService.isHelpModeActive$ | async)"
class="annotation-actions"
>
<!-- Resize Mode for annotation -> only resize accept and deny actions are available-->
<ng-container *ngIf="resizing">
<iqser-circle-button

View File

@ -8,6 +8,7 @@ import { UserService } from '@services/user.service';
import { AnnotationReferencesService } from '../../services/annotation-references.service';
import { MultiSelectService } from '../../services/multi-select.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { HelpModeService } from '../../../../../../../../../../libs/common-ui/src';
export const AnnotationButtonTypes = {
dark: 'dark',
@ -31,12 +32,13 @@ export class AnnotationActionsComponent implements OnChanges {
annotationPermissions: AnnotationPermissions;
constructor(
private readonly _userService: UserService,
readonly multiSelectService: MultiSelectService,
private readonly _state: FilePreviewStateService,
private readonly _permissionsService: PermissionsService,
readonly annotationActionsService: AnnotationActionsService,
readonly annotationReferencesService: AnnotationReferencesService,
readonly helpModeService: HelpModeService,
private readonly _userService: UserService,
private readonly _state: FilePreviewStateService,
private readonly _permissionsService: PermissionsService,
) {}
private _annotations: AnnotationWrapper[];

View File

@ -4,7 +4,6 @@
[attr.annotation-id]="annotation.id"
[attr.annotation-page]="activeViewerPage"
[class.active]="isSelected(annotation.annotationId)"
[class.help-mode]="helpModeService.isHelpModeActive$ | async"
[class.multi-select-active]="multiSelectService.active$ | async"
class="annotation-wrapper"
>

View File

@ -1,11 +1,10 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, TemplateRef } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FilterService, HelpModeService, IqserEventTarget } from '@iqser/common-ui';
import { FilterService, IqserEventTarget } from '@iqser/common-ui';
import { MultiSelectService } from '../../services/multi-select.service';
import { AnnotationReferencesService } from '../../services/annotation-references.service';
import { ViewModeService } from '../../services/view-mode.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'redaction-annotations-list',
@ -26,7 +25,6 @@ export class AnnotationsListComponent implements OnChanges {
constructor(
readonly multiSelectService: MultiSelectService,
readonly viewModeService: ViewModeService,
readonly helpModeService: HelpModeService,
readonly annotationReferencesService: AnnotationReferencesService,
private readonly _filterService: FilterService,
private readonly _state: FilePreviewStateService,
@ -38,8 +36,7 @@ export class AnnotationsListComponent implements OnChanges {
}
}
async annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): Promise<void> {
console.log(annotation);
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
if (($event?.target as IqserEventTarget)?.localName === 'input') {
return;
}
@ -53,7 +50,7 @@ export class AnnotationsListComponent implements OnChanges {
if (this.isSelected(annotation.annotationId)) {
this.deselectAnnotations.emit([annotation]);
} else {
const canMultiSelect = await firstValueFrom(this._state.isWritable$);
const canMultiSelect = this.multiSelectService.isEnabled;
if (canMultiSelect && ($event?.ctrlKey || $event?.metaKey) && this.selectedAnnotations.length > 0) {
this.multiSelectService.activate();
}
@ -61,8 +58,8 @@ export class AnnotationsListComponent implements OnChanges {
}
}
async referenceClicked(annotation: AnnotationWrapper): Promise<void> {
await this.annotationClicked(annotation, null);
referenceClicked(annotation: AnnotationWrapper): void {
this.annotationClicked(annotation, null);
if (this._filterService.filtersEnabled('primaryFilters')) {
this._filterService.toggleFilter('primaryFilters', annotation.superType, true);
}

View File

@ -14,7 +14,7 @@
<div>
<div
(click)="multiSelectService.activate()"
*ngIf="(multiSelectInactive$ | async) && state.isWritable$ | async"
*ngIf="(multiSelectService.enabled$ | async) && (multiSelectInactive$ | async)"
class="all-caps-label primary pointer"
iqserHelpMode="bulk-select-annotations"
translate="file-preview.tabs.annotations.select"
@ -207,7 +207,6 @@
[annotationActionsTemplate]="annotationActionsTemplate"
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
[selectedAnnotations]="selectedAnnotations"
iqserHelpMode="workload-annotations-list"
></redaction-annotations-list>
</div>

View File

@ -242,10 +242,14 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
await this._configureTextPopup();
this.annotationManager.addEventListener('annotationSelected', (annotations: Annotation[], action) => {
this.annotationSelected.emit(this.annotationManager.getSelectedAnnotations().map(ann => ann.Id));
const nextAnnotations = this.multiSelectService.isEnabled ? this.annotationManager.getSelectedAnnotations() : annotations;
this.annotationSelected.emit(nextAnnotations.map(ann => ann.Id));
if (action === 'deselected') {
this._toggleRectangleAnnotationAction(true);
} else {
if (!this.multiSelectService.isEnabled) {
this.utils.deselectAnnotations(this.annotations.filter(wrapper => !nextAnnotations.find(ann => ann.Id === wrapper.id)));
}
this._configureAnnotationSpecificActions(annotations);
this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly);
}
@ -289,11 +293,18 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
}
});
this.documentViewer.addEventListener('textSelected', async (quads, selectedText) => {
this.documentViewer.addEventListener('textSelected', async (quads, selectedText, pageNumber: number) => {
this._selectedText = selectedText;
const textActions = [dataElements.ADD_DICTIONARY, dataElements.ADD_FALSE_POSITIVE];
const file = await this.stateService.file;
if (this.viewModeService.isCompare && pageNumber % 2 === 0) {
this.instance.UI.disableElements(['textPopup']);
} else {
this.instance.UI.enableElements(['textPopup']);
}
if (selectedText.length > 2 && this.canPerformActions && !this.utils.isCurrentPageExcluded(file)) {
this.instance.UI.enableElements(textActions);
} else {
@ -450,7 +461,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.instance.UI.annotationPopup.update([]);
if (annotationWrappers.length === 0) {
this._configureRectangleAnnotationPopup();
this._configureRectangleAnnotationPopup(viewerAnnotations[0]);
return;
}
@ -490,16 +501,18 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.instance.UI.annotationPopup.add(actions);
}
private _configureRectangleAnnotationPopup() {
this.instance.UI.annotationPopup.add([
{
type: 'actionButton',
dataElement: dataElements.ADD_RECTANGLE,
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION', this.dossier)),
onClick: () => this._addRectangleManualRedaction(),
},
]);
private _configureRectangleAnnotationPopup(annotation: Annotation) {
if (!this.viewModeService.isCompare || annotation.getPageNumber() % 2 === 1) {
this.instance.UI.annotationPopup.add([
{
type: 'actionButton',
dataElement: dataElements.ADD_RECTANGLE,
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION', this.dossier)),
onClick: () => this._addRectangleManualRedaction(),
},
]);
}
}
private _addRectangleManualRedaction() {

View File

@ -206,6 +206,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._loadingService.start();
await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId);
this._subscribeToFileUpdates();
this.viewModeService.viewMode$.pipe(tap(() => this.#deactivateMultiSelect())).subscribe();
const file = await this.stateService.file;
if (file?.analysisRequired && !file.excludedFromAutomaticAnalysis) {
@ -427,6 +428,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
download(data, file.filename);
}
#deactivateMultiSelect(): void {
this.multiSelectService.deactivate();
this.viewerComponent?.utils?.deselectAllAnnotations();
this.handleAnnotationSelected([]);
}
private _setActiveViewerPage() {
const currentPage = this._instance?.Core.documentViewer?.getCurrentPage();
if (!currentPage) {

View File

@ -1,15 +1,28 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { boolFactory } from '@iqser/common-ui';
import { ViewModeService } from './view-mode.service';
import { map, tap } from 'rxjs/operators';
import { FilePreviewStateService } from './file-preview-state.service';
@Injectable()
export class MultiSelectService {
readonly enabled$: Observable<boolean>;
readonly active$: Observable<boolean>;
readonly inactive$: Observable<boolean>;
readonly #active$ = new BehaviorSubject(false);
readonly #enabled$ = new BehaviorSubject(true);
constructor() {
constructor(private readonly _viewModeService: ViewModeService, private readonly _state: FilePreviewStateService) {
[this.active$, this.inactive$] = boolFactory(this.#active$.asObservable());
this.enabled$ = combineLatest([this._viewModeService.isStandard$, _state.isWritable$]).pipe(
map((result: boolean[]) => !result.some(res => !res)),
tap(enabled => this.#enabled$.next(enabled)),
);
}
get isEnabled() {
return this.#enabled$.value;
}
get isActive() {
@ -17,14 +30,12 @@ export class MultiSelectService {
}
activate() {
this.#active$.next(true);
if (this.isEnabled) {
this.#active$.next(true);
}
}
deactivate() {
this.#active$.next(false);
}
toggle() {
this.#active$.next(!this.#active$.value);
}
}

View File

@ -10,7 +10,6 @@ import { AppStateService } from '@state/app-state.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { FilePreviewStateService } from '../screens/file-preview-screen/services/file-preview-state.service';
import { Toaster } from '@iqser/common-ui';
import { MISSING_TYPES_ERROR } from '@utils/constants';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Injectable()
@ -38,20 +37,19 @@ export class PdfViewerDataService {
return forkJoin([redactionLog$, viewedPages$]).pipe(
map((data: [redactionLog: IRedactionLog, viewedPages: IViewedPage[]]) => {
try {
return new FileDataModel(
newFile,
...data,
this._appStateService.dictionaryData[this._stateService.dossierTemplateId],
this._userPreferenceService.areDevFeaturesEnabled,
);
} catch (error) {
if (error.message === MISSING_TYPES_ERROR) {
this._toaster.error(_('error.missing-types'), { disableTimeOut: true });
} else {
throw error;
}
const fileDataModel = new FileDataModel(
newFile,
...data,
this._appStateService.dictionaryData[this._stateService.dossierTemplateId],
this._userPreferenceService.areDevFeaturesEnabled,
);
if (fileDataModel.missingTypes.size > 0) {
this._toaster.error(_('error.missing-types'), {
disableTimeOut: true,
params: { missingTypes: Array.from(fileDataModel.missingTypes).join(', ') },
});
}
return fileDataModel;
}),
);
}

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { UserService } from './user.service';
import { Dossier, File, IComment } from '@red/domain';
import { Dossier, File, IComment, IDossier } from '@red/domain';
import { DossiersService } from './entity-services/dossiers.service';
@Injectable({
@ -127,7 +127,7 @@ export class PermissionsService {
return this.isApprover(dossier) && files.reduce((prev, file) => prev && file.isApproved, true);
}
canDeleteDossier(dossier: Dossier): boolean {
canDeleteDossier(dossier: IDossier): boolean {
return dossier.ownerId === this._userService.currentUser.id;
}

View File

@ -350,7 +350,7 @@ export class AppStateService {
dictionaryData['redaction'] = new Dictionary(
{
hexColor: colors.previewColor || FALLBACK_COLOR,
hexColor: colors.manualRedactionColor || FALLBACK_COLOR,
type: 'redaction',
},
true,

View File

@ -1,2 +1 @@
export const CHANGED_CHECK_INTERVAL = 5000;
export const MISSING_TYPES_ERROR = 'MISSING_TYPES_ERROR';

View File

@ -1,18 +1,24 @@
{
"open-usermenu": {
"en": "/en/23200-user-menu-and-account.html",
"assign-reviewer": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"filter-dossier-list": {
"en": "/en/26024-features-and-actions-in-the-dossier-overview.html",
"bulk-select-annotations": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"filter-document-list": {
"en": "/en/create-and-manage-dossier/dossier-overview/dossier-list.html",
"delta-view": {
"en": "/en/editing-documents-in-the-editor/views-in-the-editor/delta-view.html",
"de": "",
"it": "",
"fr": ""
},
"document-features": {
"en": "",
"de": "",
"it": "",
"fr": ""
@ -23,32 +29,20 @@
"it": "",
"fr": ""
},
"search-in-entire-application": {
"en": "/en/15632-searching-the-application.html",
"de": "",
"it": "",
"fr": ""
},
"open-notifications": {
"en": "/en/15471-notifications.html",
"de": "",
"it": "",
"fr": ""
},
"dossier-list": {
"en": "/en/20941-dossier-liste.html",
"de": "",
"it": "",
"fr": ""
},
"document-list": {
"edit-dossier-attributes": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"new-dossier-button": {
"en": "/en/creating-and-managing-dossiers/dossier-overview/creating-a-new-dossier.html",
"edit-dossier-from-list": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"edit-dossier-member": {
"en": "",
"de": "",
"it": "",
"fr": ""
@ -59,12 +53,24 @@
"it": "",
"fr": ""
},
"edit-dossier-member": {
"edit-reason": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"filter-document-list": {
"en": "/en/create-and-manage-dossier/dossier-overview/dossier-list.html",
"de": "",
"it": "",
"fr": ""
},
"filter-dossier-list": {
"en": "/en/26024-features-and-actions-in-the-dossier-overview.html",
"de": "",
"it": "",
"fr": ""
},
"filter-for-editing-notes": {
"en": "",
"de": "",
@ -77,26 +83,26 @@
"it": "",
"fr": ""
},
"edit-dossier": {
"navigate-in-breadcrumbs": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"edit-dossier-from-list": {
"en": "",
"new-dossier-button": {
"en": "/en/creating-and-managing-dossiers/dossier-overview/creating-a-new-dossier.html",
"de": "",
"it": "",
"fr": ""
},
"standard-view": {
"en": "/en/editing-documents-in-the-editor/views-in-the-editor/standard-view.html",
"open-notifications": {
"en": "/en/15471-notifications.html",
"de": "",
"it": "",
"fr": ""
},
"delta-view": {
"en": "/en/editing-documents-in-the-editor/views-in-the-editor/delta-view.html",
"open-usermenu": {
"en": "/en/23200-user-menu-and-account.html",
"de": "",
"it": "",
"fr": ""
@ -107,55 +113,7 @@
"it": "",
"fr": ""
},
"bulk-select-annotations": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"workload-filter": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"workload-annotations-list": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"assign-reviewer": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"navigate-in-breadcrumbs": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"reset-filters": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"edit-reason": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"redaction-remove-only-here": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"redaction-remove-from-dictionary": {
"recommendation-accept-or-reject": {
"en": "",
"de": "",
"it": "",
@ -167,13 +125,37 @@
"it": "",
"fr": ""
},
"recommendation-accept-or-reject": {
"redaction-remove-from-dictionary": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"document-features": {
"redaction-remove-only-here": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"reset-filters": {
"en": "",
"de": "",
"it": "",
"fr": ""
},
"search-in-entire-application": {
"en": "/en/15632-searching-the-application.html",
"de": "",
"it": "",
"fr": ""
},
"standard-view": {
"en": "/en/editing-documents-in-the-editor/views-in-the-editor/standard-view.html",
"de": "",
"it": "",
"fr": ""
},
"workload-filter": {
"en": "",
"de": "",
"it": "",

View File

@ -1001,7 +1001,7 @@
"http": {
"generic": "Action failed with code {status}"
},
"missing-types": "The dossier template has missing types. Data might not be displayed correctly.",
"missing-types": "The dossier template has missing types ({missingTypes}). Data might not be displayed correctly.",
"offline": "Disconnected",
"online": "Reconnected",
"reload": "Reload",

@ -1 +1 @@
Subproject commit 6b4fe281cb6e23fb541ce75ead740a5678c968b8
Subproject commit 0bab4584767b868f9d72a4de7ae23d0dd19daad3

View File

@ -1,6 +1,6 @@
{
"name": "redaction",
"version": "3.218.0",
"version": "3.226.0",
"private": true,
"license": "MIT",
"scripts": {

Binary file not shown.