Skip to content

Commit

Permalink
feat: add UploadxService.state() method (#350)
Browse files Browse the repository at this point in the history
* refactor(ts): `stateChange` parameter type

* test: run tests with headless chrome

* test: stricter ts config

* feat: add `UploadxService.state()` method

Returns the current state of uploads.
  • Loading branch information
kukhariev authored Nov 15, 2021
1 parent 4d9ac00 commit cf7c753
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 33 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,17 @@ selector: `uploadxDrop`
Make HTTP request with `axios` like interface. [(example)](src/app/service-way/service-way.component.ts)
- `state(): UploadState[]`
Returns the current state of uploads.
```ts
constructor(private uploadService: UploadxService) {
// restore background uploads
this.uploads = this.uploadService.state();
}
```
- `queue: Uploader[]`
Uploaders array.
Expand Down
2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module.exports = function (config) {
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
browsers: ['ChromeHeadless'],
singleRun: false,
restartOnFileChange: true
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
},
"scripts": {
"start": "run-p serve:dev server",
"test": "ng test --watch=false --progress=false --browsers=ChromeHeadless",
"test": "ng test --watch=false --progress=false",
"e2e": "ng e2e",
"lint": "ng lint",
"serve:dev": "ng serve",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('ServiceCodeWayComponent', () => {
init: jasmine.createSpy('init'),
control: jasmine.createSpy('control'),
handleFiles: jasmine.createSpy('handleFiles'),
state: () => [],
queue: []
};
TestBed.configureTestingModule({
Expand Down
2 changes: 1 addition & 1 deletion src/app/service-code-way/service-code-way.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class ServiceCodeWayComponent implements OnDestroy, OnInit {

constructor(private uploadService: UploadxService) {
// restore background uploads
this.uploadService.queue.forEach(uploader => this.uploads.push(uploader));
this.uploads = this.uploadService.state();
}

ngOnInit(): void {
Expand Down
11 changes: 0 additions & 11 deletions src/uploadx/lib/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Ajax } from './ajax';
import { Canceler } from './canceler';
import { RetryConfig } from './retry-handler';
import { Uploader } from './uploader';

export type Primitive = null | boolean | number | string;

Expand Down Expand Up @@ -130,12 +128,3 @@ export interface UploaderOptions extends UploadItem {
*/
authorize?: AuthorizeRequest;
}

export type UploaderClass = new (
file: File,
options: UploaderOptions,
stateChange: (evt: UploadState) => void,
ajax: Ajax
) => Uploader;

export type Writable<T> = { -readonly [K in keyof T]: T[K] };
11 changes: 10 additions & 1 deletion src/uploadx/lib/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { InjectionToken } from '@angular/core';
import { AuthorizeRequest, UploaderClass, UploaderOptions } from './interfaces';
import { Ajax } from './ajax';
import { AuthorizeRequest, UploaderOptions } from './interfaces';
import { Uploader } from './uploader';
import { UploaderX } from './uploaderx';

/**
Expand Down Expand Up @@ -46,6 +48,13 @@ export interface UploadxFactoryOptions extends UploadxOptions {
storeIncompleteHours: number;
}

export type UploaderClass = new (
file: File,
options: UploaderOptions,
stateChange: (uploader: Uploader) => void,
ajax: Ajax
) => Uploader;

const defaultOptions: UploadxFactoryOptions = {
endpoint: '/upload',
autoUpload: true,
Expand Down
2 changes: 1 addition & 1 deletion src/uploadx/lib/uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export abstract class Uploader implements UploadState {
constructor(
readonly file: File,
readonly options: Readonly<UploaderOptions>,
readonly stateChange: (evt: UploadState) => void,
readonly stateChange: (uploader: Uploader) => void,
readonly ajax: Ajax
) {
this.retry = new RetryHandler(options.retryConfig);
Expand Down
35 changes: 20 additions & 15 deletions src/uploadx/lib/uploadx.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Inject, Injectable, NgZone, Optional } from '@angular/core';
import { OnDestroy } from '@angular/core';
import { Inject, Injectable, NgZone, OnDestroy, Optional } from '@angular/core';
import { fromEvent, Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, map, startWith } from 'rxjs/operators';
import { debounceTime, map } from 'rxjs/operators';
import { Ajax, AjaxRequestConfig, AjaxResponse, UPLOADX_AJAX } from './ajax';
import { IdService } from './id.service';
import { UploadState, UploadxControlEvent, Writable } from './interfaces';
import { UploadState, UploadxControlEvent } from './interfaces';
import {
iOSPatch,
UploadxFactoryOptions,
Expand All @@ -14,9 +13,9 @@ import {
} from './options';
import { store } from './store';
import { Uploader } from './uploader';
import { isIOS, pick } from './utils';
import { isIOS, onLine, pick } from './utils';

const stateKeys: (keyof UploadState)[] = [
export const UPLOAD_STATE_KEYS: (keyof UploadState)[] = [
'file',
'name',
'progress',
Expand Down Expand Up @@ -79,7 +78,6 @@ export class UploadxService implements OnDestroy {
*/
connect(options?: UploadxOptions): Observable<Uploader[]> {
return this.init(options).pipe(
startWith(null),
map(() => this.queue),
debounceTime(DUE_TIME)
);
Expand All @@ -93,6 +91,16 @@ export class UploadxService implements OnDestroy {
this.queue = [];
}

/**
* Returns current uploads state
* @example
* // restore background uploads
* this.uploads = this.uploadService.state();
*/
state(): UploadState[] {
return this.queue.map(uploader => pick(uploader, UPLOAD_STATE_KEYS));
}

ngOnDestroy(): void {
this.disconnect();
this.subs.forEach(sub => sub.unsubscribe());
Expand Down Expand Up @@ -143,16 +151,17 @@ export class UploadxService implements OnDestroy {
return this.ajax.request(config);
}

private stateChange = (evt: UploadState) => {
this.ngZone.run(() => this.eventsStream.next(pick(evt, stateKeys)));
if (evt.status !== 'uploading' && evt.status !== 'added') {
private stateChange = (uploader: Uploader) => {
this.ngZone.run(() => this.eventsStream.next(pick(uploader, UPLOAD_STATE_KEYS)));
if (uploader.status !== 'uploading' && uploader.status !== 'added') {
this.ngZone.runOutsideAngular(() => setTimeout(() => this.processQueue()));
}
};

private async addUploaderInstance(file: File, options: UploadxFactoryOptions): Promise<void> {
const uploader = new options.uploaderClass(file, options, this.stateChange, this.ajax);
(uploader.uploadId as Writable<string>) = await this.idService.generateId(uploader);
(uploader as { uploadId: string }).uploadId = await this.idService.generateId(uploader);
// Object.defineProperty(uploader, 'uploadId', { configurable: false, writable: false });
this.queue.push(uploader);
uploader.status = options.autoUpload && onLine() ? 'queue' : 'added';
}
Expand All @@ -165,7 +174,3 @@ export class UploadxService implements OnDestroy {
.forEach(uploader => uploader.upload());
}
}

function onLine(): boolean {
return typeof window !== 'undefined' ? window.navigator.onLine : true;
}
8 changes: 6 additions & 2 deletions src/uploadx/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export function unfunc<T, V>(value: T | ((ref: V) => T), ref: V): T {
return value instanceof Function ? value(ref) : value;
}

export const pick = <T, K extends keyof T>(obj: T, whitelist: K[]): Pick<T, K> => {
export const pick = <T, K extends keyof T>(obj: T, props: K[]): Pick<T, K> => {
const result = {} as Pick<T, K>;
whitelist.forEach(key => (result[key] = obj[key]));
props.forEach(key => (result[key] = obj[key]));
return result;
};

Expand Down Expand Up @@ -84,3 +84,7 @@ export function isIOS(): boolean {
/MacIntel/.test(navigator.platform)
);
}

export function onLine(): boolean {
return typeof window !== 'undefined' ? window.navigator.onLine : true;
}
3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"importHelpers": true,
"module": "es2020",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"noImplicitOverride": false,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
Expand Down

0 comments on commit cf7c753

Please sign in to comment.