Browse Source

[Streaming] Fix hanged chunk load promise

Hide 'Sensitive content' section if it's unavailable
master
Eduard Kuzmenko 3 years ago
parent
commit
eff96de924
  1. 9
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  2. 6
      src/helpers/context.ts
  3. 10
      src/lib/mtproto/mtprotoworker.ts
  4. 10
      src/lib/mtproto/webPushApiManager.ts
  5. 25
      src/lib/serviceWorker/index.service.ts
  6. 44
      src/lib/serviceWorker/stream.ts

9
src/components/sidebarLeft/tabs/privacyAndSecurity.ts

@ -31,6 +31,7 @@ import CheckboxField from "../../checkboxField"; @@ -31,6 +31,7 @@ import CheckboxField from "../../checkboxField";
import PopupPeer from "../../popups/peer";
import appDraftsManager from "../../../lib/appManagers/appDraftsManager";
import Button from "../../button";
import toggleDisability from "../../../helpers/dom/toggleDisability";
export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
private activeSessionsRow: Row;
@ -232,6 +233,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { @@ -232,6 +233,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
const promises: Promise<any>[] = [];
{
const section = new SettingSection({name: 'Privacy.SensitiveContent'});
section.container.classList.add('hide');
promises.push(apiManager.invokeApi('account.getContentSettings').then(settings => {
if(!settings.pFlags.sensitive_can_change) {
@ -247,7 +249,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { @@ -247,7 +249,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
});
section.content.append(sensitiveRow.container);
section.container.classList.remove('hide');
this.eventListener.addEventListener('destroy', () => {
const _enabled = sensitiveRow.checkboxField.checked;
@ -273,7 +275,10 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { @@ -273,7 +275,10 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
buttons: [{
langKey: 'Delete',
callback: () => {
appDraftsManager.clearAllDrafts();
const toggle = toggleDisability([deleteButton], true);
appDraftsManager.clearAllDrafts().then(() => {
toggle();
});
},
isDanger: true,
}],

6
src/helpers/context.ts

@ -10,6 +10,12 @@ export const isWorker = isWebWorker || isServiceWorker; @@ -10,6 +10,12 @@ export const isWorker = isWebWorker || isServiceWorker;
// в SW может быть сразу две переменных TRUE, поэтому проверяю по последней
export const getWindowClients = () => {
return (self as any as ServiceWorkerGlobalScope)
.clients
.matchAll({ includeUncontrolled: false, type: 'window' });
};
const notifyServiceWorker = (all: boolean, ...args: any[]) => {
(self as any as ServiceWorkerGlobalScope)
.clients

10
src/lib/mtproto/mtprotoworker.ts

@ -54,7 +54,7 @@ export interface ToggleStorageTask extends WorkerTaskVoidTemplate { @@ -54,7 +54,7 @@ export interface ToggleStorageTask extends WorkerTaskVoidTemplate {
export class ApiManagerProxy extends CryptoWorkerMethods {
public worker: /* Window */Worker;
public postMessage: (...args: any[]) => void;
public postSWMessage: (...args: any[]) => void = () => {};
//public postSWMessage: (...args: any[]) => void = () => {};
private afterMessageIdTemp = 0;
private taskId = 0;
@ -229,7 +229,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods { @@ -229,7 +229,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
this.log('SW statechange', e);
});
this.postSWMessage = worker.controller.postMessage.bind(worker.controller);
//this.postSWMessage = worker.controller.postMessage.bind(worker.controller);
/// #if MTPROTO_SW
const controller = worker.controller || registration.installing || registration.waiting || registration.active;
@ -292,6 +292,12 @@ export class ApiManagerProxy extends CryptoWorkerMethods { @@ -292,6 +292,12 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
});
}
public postSWMessage(message: any) {
if(navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage(message);
}
}
private onWorkerFirstMessage(worker: any) {
if(!this.worker) {
this.worker = worker;

10
src/lib/mtproto/webPushApiManager.ts

@ -181,9 +181,7 @@ export class WebPushApiManager { @@ -181,9 +181,7 @@ export class WebPushApiManager {
}
};
if(navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage(task);
}
apiManager.postSWMessage(task);
this.isAliveTO = setTimeout(this.isAliveNotify, 10000);
}
@ -199,10 +197,8 @@ export class WebPushApiManager { @@ -199,10 +197,8 @@ export class WebPushApiManager {
return;
}
if(navigator.serviceWorker.controller) {
const task: ServiceWorkerNotificationsClearTask = {type: 'notifications_clear'};
navigator.serviceWorker.controller.postMessage(task);
}
const task: ServiceWorkerNotificationsClearTask = {type: 'notifications_clear'};
apiManager.postSWMessage(task);
}
public setUpServiceWorkerChannel() {

25
src/lib/serviceWorker/index.service.ts

@ -22,7 +22,7 @@ import CacheStorageController from '../cacheStorage'; @@ -22,7 +22,7 @@ import CacheStorageController from '../cacheStorage';
export const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);
const ctx = self as any as ServiceWorkerGlobalScope;
export const deferredPromises: {[taskId: string]: CancellablePromise<any>} = {};
export const deferredPromises: Map<WindowClient['id'], {[taskId: string]: CancellablePromise<any>}> = new Map();
export interface RequestFilePartTask extends Modify<WorkerTaskTemplate, {id: string}> {
type: 'requestFilePart',
@ -69,16 +69,23 @@ const taskListeners: { @@ -69,16 +69,23 @@ const taskListeners: {
ping: (task: ServiceWorkerPingTask, event) => {
onPing(task, event);
},
requestFilePart: (task: RequestFilePartTaskResponse) => {
const promise = deferredPromises[task.id];
if(task.error) {
promise.reject(task.error);
} else {
promise.resolve(task.payload);
requestFilePart: (task: RequestFilePartTaskResponse, e: ExtendableMessageEvent) => {
const windowClient = e.source as WindowClient;
const promises = deferredPromises.get(windowClient.id);
if(!promises) {
return;
}
delete deferredPromises[task.id];
const promise = promises[task.id];
if(promise) {
if(task.error) {
promise.reject(task.error);
} else {
promise.resolve(task.payload);
}
delete promises[task.id];
}
},
toggleStorage: (task: ToggleStorageTask) => {
CacheStorageController.toggleStorage(task.payload);

44
src/lib/serviceWorker/stream.ts

@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
import { readBlobAsUint8Array } from "../../helpers/blob";
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
import { notifySomeone } from "../../helpers/context";
import { getWindowClients, notifySomeone } from "../../helpers/context";
import debounce from "../../helpers/schedulers/debounce";
import { isSafari } from "../../helpers/userAgent";
import { InputFileLocation, UploadFile } from "../../layer";
@ -49,6 +49,20 @@ const clearOldChunks = () => { @@ -49,6 +49,20 @@ const clearOldChunks = () => {
};
setInterval(clearOldChunks, 1800e3);
setInterval(() => {
getWindowClients().then((clients) => {
for(const [clientId, promises] of deferredPromises) {
if(!clients.find(client => client.id === clientId)) {
for(const taskId in promises) {
const promise = promises[taskId];
promise.reject();
}
deferredPromises.delete(clientId);
}
}
});
}, 120e3);
type StreamRange = [number, number];
type StreamId = string;
@ -72,7 +86,7 @@ class Stream { @@ -72,7 +86,7 @@ class Stream {
streams.delete(this.id);
};
private requestFilePartFromWorker(alignedOffset: number, limit: number, fromPreload = false) {
private async requestFilePartFromWorker(alignedOffset: number, limit: number, fromPreload = false) {
const task: Omit<RequestFilePartTask, 'id'> = {
type: 'requestFilePart',
payload: [this.info.dcId, this.info.location, alignedOffset, limit]
@ -81,16 +95,32 @@ class Stream { @@ -81,16 +95,32 @@ class Stream {
const taskId = JSON.stringify(task);
(task as RequestFilePartTask).id = taskId;
let deferred = deferredPromises[taskId] as CancellablePromise<UploadFile.uploadFile>;
const windowClient = await getWindowClients().then((clients) => {
if(!clients.length) {
return;
}
return clients.find(client => deferredPromises.has(client.id)) || clients[0];
});
if(!windowClient) {
throw new Error('no window');
}
let promises = deferredPromises.get(windowClient.id);
if(!promises) {
deferredPromises.set(windowClient.id, promises = {});
}
let deferred = promises[taskId] as CancellablePromise<UploadFile.uploadFile>;
if(deferred) {
return deferred.then(uploadFile => uploadFile.bytes);
}
notifySomeone(task);
windowClient.postMessage(task);
this.loadedOffsets.add(alignedOffset);
deferred = deferredPromises[taskId] = deferredPromise<UploadFile.uploadFile>();
deferred = promises[taskId] = deferredPromise<UploadFile.uploadFile>();
const bytesPromise = deferred.then(uploadFile => uploadFile.bytes);
this.saveChunkToCache(bytesPromise, alignedOffset, limit);

Loading…
Cancel
Save