mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-25 02:04:15 +02:00
adding missing translations, improving phone settings phone view
This commit is contained in:
parent
4c507f286d
commit
9516fba85b
@ -14,7 +14,7 @@
|
||||
"pretest": "tsc",
|
||||
"test": "ng test && mocha --recursive test/backend/unit && mocha --recursive test/backend/integration && mocha --recursive test/common/unit ",
|
||||
"start": "node ./src/backend/index",
|
||||
"run-dev": "ng build --aot --watch --output-path=./dist --i18n-locale en --i18n-file src/frontend/translate/messages.en.xlf --i18n-missing-translation warning",
|
||||
"run-dev": "ng build --aot --watch --output-path=./dist --i18n-locale hu --i18n-file src/frontend/translate/messages.hu.xlf --i18n-missing-translation warning",
|
||||
"build-stats": "ng build --aot --prod --stats-json --output-path=./dist --i18n-locale en --i18n-file src/frontend/translate/messages.en.xlf --i18n-missing-translation warning",
|
||||
"merge-new-translation": "gulp merge-new-translation",
|
||||
"add-translation": "gulp add-translation"
|
||||
|
@ -5,7 +5,7 @@ import {Config} from '../../../common/config/private/Config';
|
||||
import {JobProgressDTO, JobProgressStates} from '../../../common/entities/job/JobProgressDTO';
|
||||
|
||||
export class JobProgressManager {
|
||||
private static readonly VERSION = 2;
|
||||
private static readonly VERSION = 3;
|
||||
private db: {
|
||||
version: number,
|
||||
progresses: { [key: string]: { progress: JobProgressDTO, timestamp: number } }
|
||||
|
@ -43,7 +43,7 @@ export abstract class Job<T = void> implements IJob<T> {
|
||||
Logger.info(LOG_TAG, 'Running job ' + (soloRun === true ? 'solo' : '') + ': ' + this.Name);
|
||||
this.soloRun = soloRun;
|
||||
this.config = config;
|
||||
this.progress = new JobProgress(JobDTO.getHashName(this.Name, this.config));
|
||||
this.progress = new JobProgress(this.Name, JobDTO.getHashName(this.Name, this.config));
|
||||
this.progress.OnChange = this.jobListener.onProgressUpdate;
|
||||
const pr = new Promise<void>((resolve) => {
|
||||
this.prResolve = resolve;
|
||||
|
@ -16,7 +16,7 @@ export class JobProgress {
|
||||
private logs: { id: number, timestamp: string, comment: string }[] = [];
|
||||
|
||||
|
||||
constructor(public readonly HashName: string) {
|
||||
constructor(public readonly jobName: string, public readonly HashName: string) {
|
||||
}
|
||||
|
||||
set OnChange(val: (progress: JobProgress) => void) {
|
||||
@ -90,6 +90,7 @@ export class JobProgress {
|
||||
|
||||
toDTO(): JobProgressDTO {
|
||||
return {
|
||||
jobName: this.jobName,
|
||||
HashName: this.HashName,
|
||||
state: this.state,
|
||||
time: {
|
||||
|
@ -10,6 +10,7 @@ export interface JobProgressLogDTO {
|
||||
}
|
||||
|
||||
export interface JobProgressDTO {
|
||||
jobName: string;
|
||||
HashName: string;
|
||||
steps: {
|
||||
all: number,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||
import {backendText, backendTexts} from '../../../common/BackendTexts';
|
||||
import {DefaultsJobs} from '../../../common/entities/job/JobDTO';
|
||||
|
||||
@Injectable()
|
||||
export class BackendtextService {
|
||||
@ -23,4 +24,26 @@ export class BackendtextService {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public getJobName(job: DefaultsJobs | string): string {
|
||||
if (typeof job === 'string') {
|
||||
job = DefaultsJobs[<any>job];
|
||||
}
|
||||
switch (job as DefaultsJobs) {
|
||||
case DefaultsJobs.Indexing:
|
||||
return this.i18n('Indexing');
|
||||
case DefaultsJobs['Database Reset']:
|
||||
return this.i18n('Database Reset');
|
||||
case DefaultsJobs['Thumbnail Generation']:
|
||||
return this.i18n('Thumbnail Generation');
|
||||
case DefaultsJobs['Photo Converting']:
|
||||
return this.i18n('Photo Converting');
|
||||
case DefaultsJobs['Video Converting']:
|
||||
return this.i18n('Video Converting');
|
||||
case DefaultsJobs['Temp Folder Cleaning']:
|
||||
return this.i18n('Temp Folder Cleaning');
|
||||
default:
|
||||
return DefaultsJobs[job as DefaultsJobs];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,11 @@
|
||||
<div class="col-md-10">
|
||||
<input type="text" class="form-control" placeholder="db"
|
||||
[(ngModel)]="settings.dbFolder" id="dbFolder" name="dbFolder" required>
|
||||
<small class="form-text text-muted" i18n>
|
||||
All file-based data will be stored here (sqlite database, user database in case of memory db, job history
|
||||
data)
|
||||
</small>
|
||||
</div>
|
||||
<small class="form-text text-muted" i18n>
|
||||
All file-based data will be stored here (sqlite database, user database in case of memory db, job history data)
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="settings.type == DatabaseType.mysql">
|
||||
|
@ -117,7 +117,7 @@
|
||||
[jobName]="indexingJobName"></app-settings-job-button>
|
||||
|
||||
|
||||
<app-settings-job-button class="ml-2"
|
||||
<app-settings-job-button class="ml-md-2 mt-2 mt-md-0"
|
||||
danger="true"
|
||||
[soloRun]="true"
|
||||
(error)="error=$event"
|
||||
|
@ -4,7 +4,7 @@
|
||||
*ngIf="!Running"
|
||||
[disabled]="disabled || jobsService.jobStartingStopping[jobName]"
|
||||
(click)="start();">
|
||||
<span class="mr-2" *ngIf="!shortName">Run {{jobName}} now</span>
|
||||
<span class="mr-2" *ngIf="!shortName"><ng-container i18n>Run now</ng-container>: {{backendTextService.getJobName(jobName)}}</span>
|
||||
<span class="oi oi-media-play"></span>
|
||||
</button>
|
||||
<button class="btn btn-secondary"
|
||||
@ -12,5 +12,5 @@
|
||||
[disabled]="disabled || jobsService.jobStartingStopping[jobName] || Progress.state !== JobProgressStates.running"
|
||||
(click)="stop();">
|
||||
<span class="oi oi-media-stop"></span>
|
||||
<span class="ml-2" *ngIf="!shortName">Cancel {{jobName}}</span>
|
||||
<span class="ml-2" *ngIf="!shortName"><ng-container i18n>Cancel</ng-container>: {{backendTextService.getJobName(jobName)}}</span>
|
||||
</button>
|
||||
|
@ -5,6 +5,7 @@ import {ScheduledJobsService} from '../../scheduled-jobs.service';
|
||||
import {NotificationService} from '../../../../model/notification.service';
|
||||
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||
import {JobDTO} from '../../../../../../common/entities/job/JobDTO';
|
||||
import {BackendtextService} from '../../../../model/backendtext.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings-job-button',
|
||||
@ -23,6 +24,7 @@ export class JobButtonComponent {
|
||||
|
||||
constructor(private notification: NotificationService,
|
||||
public jobsService: ScheduledJobsService,
|
||||
public backendTextService: BackendtextService,
|
||||
private i18n: I18n) {
|
||||
}
|
||||
|
||||
@ -39,7 +41,7 @@ export class JobButtonComponent {
|
||||
this.error.emit('');
|
||||
try {
|
||||
await this.jobsService.start(this.jobName, this.config, this.soloRun);
|
||||
this.notification.info(this.i18n('Job') + ' ' + this.jobName + ' ' + this.i18n('started'));
|
||||
this.notification.info(this.i18n('Job started') + ': ' + this.jobName);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
@ -55,7 +57,7 @@ export class JobButtonComponent {
|
||||
this.error.emit('');
|
||||
try {
|
||||
await this.jobsService.stop(this.jobName);
|
||||
this.notification.info(this.i18n('Job') + ' ' + this.jobName + ' ' + this.i18n('stopped'));
|
||||
this.notification.info(this.i18n('Job stopped') + ': ' + this.jobName);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
|
@ -24,15 +24,15 @@
|
||||
*ngSwitchCase="JobTriggerType.scheduled">{{schedule.trigger.time | date:"medium"}}</ng-container>
|
||||
<ng-container *ngSwitchCase="JobTriggerType.never" i18n>never</ng-container>
|
||||
<ng-container *ngSwitchCase="JobTriggerType.after">
|
||||
<ng-container i18n>after</ng-container>
|
||||
<ng-container i18n>after</ng-container>:
|
||||
{{schedule.trigger.afterScheduleName}}
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-danger job-control-button" (click)="remove(schedule)"><span class="oi oi-trash"></span>
|
||||
<button class="btn btn-danger job-control-button ml-0" (click)="remove(schedule)"><span class="oi oi-trash"></span>
|
||||
</button>
|
||||
<app-settings-job-button class="job-control-button ml-2"
|
||||
<app-settings-job-button class="job-control-button ml-md-2 mt-2 mt-md-0"
|
||||
(error)="error=$event"
|
||||
[jobName]="schedule.jobName" [config]="schedule.config"
|
||||
[shortName]="true"></app-settings-job-button>
|
||||
@ -44,11 +44,18 @@
|
||||
<div class="card-body" [hidden]="!showDetails[schedule.name]">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-9">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label" [for]="'jobName'+i" i18n>Job:</label>
|
||||
<div class="col-md-10">
|
||||
{{schedule.jobName}}
|
||||
<div class="col-md-4">
|
||||
{{backendTextService.getJobName(schedule.jobName)}}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<app-settings-job-button class="float-right"
|
||||
[jobName]="schedule.jobName"
|
||||
(error)="error=$event"
|
||||
[config]="schedule.config"></app-settings-job-button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
@ -116,12 +123,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<app-settings-job-button class="float-right"
|
||||
[jobName]="schedule.jobName"
|
||||
(error)="error=$event"
|
||||
[config]="schedule.config"></app-settings-job-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="getConfigTemplate(schedule.jobName) ">
|
||||
@ -130,7 +132,7 @@
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"
|
||||
[for]="configEntry.id+'_'+i">{{backendtextService.get(configEntry.name)}}:</label>
|
||||
[for]="configEntry.id+'_'+i">{{backendTextService.get(configEntry.name)}}:</label>
|
||||
<div class="col-md-10">
|
||||
<ng-container [ngSwitch]="configEntry.type">
|
||||
<ng-container *ngSwitchCase="'boolean'">
|
||||
@ -172,7 +174,7 @@
|
||||
<small class="form-text text-muted">
|
||||
<ng-container *ngIf="configEntry.type == 'number-array'" i18n>';' separated integers.
|
||||
</ng-container>
|
||||
{{backendtextService.get(configEntry.description)}}
|
||||
{{backendTextService.get(configEntry.description)}}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -223,7 +225,7 @@
|
||||
<select class="form-control" (change)="jobTypeChanged(newSchedule)" [(ngModel)]="newSchedule.jobName"
|
||||
name="newJobName" required>
|
||||
<option *ngFor="let availableJob of _settingsService.availableJobs | async"
|
||||
[ngValue]="availableJob.Name">{{availableJob.Name}}
|
||||
[ngValue]="availableJob.Name">{{backendTextService.getJobName(availableJob.Name)}}
|
||||
</option>
|
||||
</select>
|
||||
<small class="form-text text-muted"
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
PeriodicJobTrigger,
|
||||
ScheduledJobTrigger
|
||||
} from '../../../../../common/entities/job/JobScheduleDTO';
|
||||
import {Utils} from '../../../../../common/Utils';
|
||||
import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig';
|
||||
import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO';
|
||||
import {ModalDirective} from 'ngx-bootstrap/modal';
|
||||
@ -51,7 +50,7 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
||||
_navigation: NavigationService,
|
||||
_settingsService: JobsSettingsService,
|
||||
public jobsService: ScheduledJobsService,
|
||||
public backendtextService: BackendtextService,
|
||||
public backendTextService: BackendtextService,
|
||||
notification: NotificationService,
|
||||
i18n: I18n) {
|
||||
|
||||
@ -64,7 +63,12 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
||||
s => s.Server.Jobs);
|
||||
|
||||
this.hasAvailableSettings = !this.simplifiedMode;
|
||||
this.JobTriggerTypeMap = Utils.enumToArray(JobTriggerType);
|
||||
this.JobTriggerTypeMap = [
|
||||
{key: JobTriggerType.after, value: this.i18n('after')},
|
||||
{key: JobTriggerType.never, value: this.i18n('never')},
|
||||
{key: JobTriggerType.periodic, value: this.i18n('periodic')},
|
||||
{key: JobTriggerType.scheduled, value: this.i18n('scheduled')},
|
||||
];
|
||||
this.periods = [this.i18n('Monday'), // 0
|
||||
this.i18n('Tuesday'), // 1
|
||||
this.i18n('Wednesday'), // 2
|
||||
@ -166,7 +170,7 @@ export class JobsSettingsComponent extends SettingsComponent<ServerConfig.JobCon
|
||||
addNewJob() {
|
||||
const jobName = this.newSchedule.jobName;
|
||||
const count = this.settings.scheduled.filter(s => s.jobName === jobName).length;
|
||||
this.newSchedule.name = count === 0 ? jobName : jobName + ' ' + (count + 1);
|
||||
this.newSchedule.name = count === 0 ? jobName : this.backendTextService.getJobName(jobName) + ' ' + (count + 1);
|
||||
this.settings.scheduled.push(this.newSchedule);
|
||||
this.jobModal.hide();
|
||||
}
|
||||
|
@ -7,13 +7,13 @@
|
||||
{{progress.time.start | date:'medium'}} - {{progress.time.end | date:'mediumTime'}}
|
||||
</div>
|
||||
<div class="col-md-2 col-4"
|
||||
title="processed:{{progress.steps.processed}}+ skipped:{{progress.steps.skipped}} / all:{{progress.steps.all}}">
|
||||
[title]="ProgressTitle">
|
||||
<span class="oi oi-check" aria-hidden="true"></span>
|
||||
{{progress.steps.processed + progress.steps.skipped}}/{{progress.steps.all}}
|
||||
</div>
|
||||
<div class="col-md-2 col-4" title="Status" i18n-title>
|
||||
<span class="oi oi-pulse" aria-hidden="true"></span>
|
||||
{{JobProgressStates[progress.state]}}
|
||||
{{State}}
|
||||
</div>
|
||||
<div class="col-md-2 col-4">
|
||||
<button class="btn btn-secondary float-right" (click)="openModal(template)">
|
||||
@ -29,7 +29,7 @@
|
||||
[ngModel]="progress.logs[progress.logs.length-1].comment" name="details">
|
||||
<input
|
||||
*ngIf="progress.state === JobProgressStates.cancelling" type="text" class="form-control" disabled
|
||||
value="Cancelling: {{progress.logs[progress.logs.length-1].comment}}"
|
||||
value="Cancelling..."
|
||||
i18n-value name="details">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" (click)="openModal(template)">
|
||||
@ -45,7 +45,7 @@
|
||||
<div class="col-6 col-md-2 col-lg-1 order-md-2 text-right text-md-left" title="time left"
|
||||
i18n-title>{{TimeAll| duration:':'}}</div>
|
||||
<div class="progress col-md-8 col-lg-10 order-md-1"
|
||||
title="processed:{{progress.steps.processed}}+ skipped:{{progress.steps.skipped}} / all:{{progress.steps.all}}">
|
||||
[title]="ProgressTitle">
|
||||
<div
|
||||
*ngIf="progress.steps.all >0"
|
||||
class="progress-bar clickable d-inline-block progress-bar-success {{progress.state === JobProgressStates.cancelling ? 'bg-secondary' : ''}}"
|
||||
@ -78,11 +78,11 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="row justify-content-between">
|
||||
<div>Processed: {{progress.steps.processed}}</div>
|
||||
<div>Skipped: {{progress.steps.skipped}}</div>
|
||||
<div>Left: {{progress.steps.all - progress.steps.skipped - progress.steps.processed}}</div>
|
||||
<div>All: {{progress.steps.all}}</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-6"><ng-container i18n>Processed</ng-container>: {{progress.steps.processed}}</div>
|
||||
<div class="col-md-3 col-6"><ng-container i18n>Skipped</ng-container>: {{progress.steps.skipped}}</div>
|
||||
<div class="col-md-3 col-6"><ng-container i18n>Left</ng-container>: {{progress.steps.all - progress.steps.skipped - progress.steps.processed}}</div>
|
||||
<div class="col-md-3 col-6"><ng-container i18n>All</ng-container>: {{progress.steps.all}}</div>
|
||||
</div>
|
||||
<div class="row mt-3 mb-3">
|
||||
<div class="progress col-12">
|
||||
|
@ -2,6 +2,8 @@ import {Component, Input, OnChanges, OnDestroy, TemplateRef} from '@angular/core
|
||||
import {JobProgressDTO, JobProgressStates} from '../../../../../../common/entities/job/JobProgressDTO';
|
||||
import {Subscription, timer} from 'rxjs';
|
||||
import {BsModalRef, BsModalService} from 'ngx-bootstrap';
|
||||
import {I18n} from '@ngx-translate/i18n-polyfill';
|
||||
import {BackendtextService} from '../../../../model/backendtext.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings-job-progress',
|
||||
@ -16,18 +18,28 @@ export class JobProgressComponent implements OnDestroy, OnChanges {
|
||||
modalRef: BsModalRef;
|
||||
private timerSub: Subscription;
|
||||
|
||||
constructor(private modalService: BsModalService) {
|
||||
constructor(private modalService: BsModalService,
|
||||
public backendTextService: BackendtextService,
|
||||
private i18n: I18n) {
|
||||
}
|
||||
|
||||
get ProgressTitle(): string {
|
||||
if (!this.progress) {
|
||||
return '';
|
||||
}
|
||||
return this.i18n('processed') + ':' + this.progress.steps.processed + ' + ' + this.i18n('skipped') + ':'
|
||||
+ this.progress.steps.skipped + ' / ' + this.i18n('all') + ':' + this.progress.steps.all;
|
||||
}
|
||||
|
||||
get Name(): string {
|
||||
if (!this.progress) {
|
||||
return '';
|
||||
}
|
||||
return this.progress.HashName;
|
||||
return this.backendTextService.getJobName(this.progress.jobName);
|
||||
}
|
||||
|
||||
get TimeAll(): number {
|
||||
if (!this.progress) {
|
||||
if (!this.progress || this.progress.steps.processed === 0) {
|
||||
return 0;
|
||||
}
|
||||
return (this.progress.time.end - this.progress.time.start) /
|
||||
@ -56,6 +68,25 @@ export class JobProgressComponent implements OnDestroy, OnChanges {
|
||||
return (this.timeCurrentCopy - this.progress.time.start);
|
||||
}
|
||||
|
||||
get State(): string {
|
||||
if (!this.progress) {
|
||||
return '';
|
||||
}
|
||||
switch (this.progress.state) {
|
||||
case JobProgressStates.running:
|
||||
return this.i18n('running');
|
||||
case JobProgressStates.cancelling:
|
||||
return this.i18n('cancelling');
|
||||
case JobProgressStates.canceled:
|
||||
return this.i18n('canceled');
|
||||
case JobProgressStates.interrupted:
|
||||
return this.i18n('interrupted');
|
||||
case JobProgressStates.finished:
|
||||
return this.i18n('finished');
|
||||
default:
|
||||
return 'unknown state';
|
||||
}
|
||||
}
|
||||
|
||||
openModal(template: TemplateRef<any>) {
|
||||
this.modalRef = this.modalService.show(template, {class: 'modal-lg'});
|
||||
|
@ -100,7 +100,7 @@
|
||||
</button>
|
||||
|
||||
<div [hidden]="!settings.client.Converting.enabled">
|
||||
<app-settings-job-button class="float-left"
|
||||
<app-settings-job-button class="mt-2 mt-md-2 float-left"
|
||||
[soloRun]="true"
|
||||
(error)="error=$event"
|
||||
[jobName]="jobName"></app-settings-job-button>
|
||||
|
@ -28,7 +28,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group row" [hidden]="simplifiedMode">
|
||||
<label class="col-md-2 control-label" for="icon">Icon size</label>
|
||||
<label class="col-md-2 control-label" for="icon" i18n>Icon size</label>
|
||||
<div class="col-md-10">
|
||||
<input type="number" class="form-control" placeholder="30"
|
||||
id="icon"
|
||||
@ -45,7 +45,7 @@
|
||||
<div class="form-group row" [hidden]="simplifiedMode">
|
||||
<label class="col-md-2 control-label" for="thumbnailSizes" i18n>Thumbnail sizes</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" class="form-control" placeholder="160; 240"
|
||||
<input type="text" class="form-control" placeholder="240; 480"
|
||||
id="thumbnailSizes"
|
||||
[(ngModel)]="ThumbnailSizes"
|
||||
name="thumbnailSizes" required>
|
||||
@ -56,7 +56,7 @@
|
||||
storage and CPU to render.)
|
||||
</ng-container>
|
||||
<br/>
|
||||
<ng-container i18n>';' separated integers. If size is 160, that shorter side of the thumbnail will have 160
|
||||
<ng-container i18n>';' separated integers. If size is 240, that shorter side of the thumbnail will have 160
|
||||
pixels.
|
||||
</ng-container>
|
||||
</small>
|
||||
@ -74,7 +74,7 @@
|
||||
</button>
|
||||
|
||||
|
||||
<app-settings-job-button class="float-left"
|
||||
<app-settings-job-button class="mt-2 mt-md-2 float-left"
|
||||
[soloRun]="true"
|
||||
(error)="error=$event"
|
||||
[jobName]="jobName"
|
||||
|
@ -127,7 +127,7 @@
|
||||
(click)="reset()" i18n>Reset
|
||||
</button>
|
||||
|
||||
<app-settings-job-button class="float-left"
|
||||
<app-settings-job-button class="mt-2 mt-md-2 float-left"
|
||||
[soloRun]="true"
|
||||
(error)="error=$event"
|
||||
[jobName]="jobName"></app-settings-job-button>
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user