Fixed Firefox private mode
This commit is contained in:
parent
28ecc39853
commit
4823ed4f76
6 changed files with 95 additions and 64 deletions
|
@ -1,4 +1,7 @@
|
||||||
# Changelog:
|
# Changelog:
|
||||||
|
* **05.12.20**
|
||||||
|
- Fixed the webclient for Firefox in incognito mode
|
||||||
|
|
||||||
* **04.12.20**
|
* **04.12.20**
|
||||||
- Properly logging channel creations, deletions, shows and hides
|
- Properly logging channel creations, deletions, shows and hides
|
||||||
- Fixed missing collapsed arrow update after channel move
|
- Fixed missing collapsed arrow update after channel move
|
||||||
|
|
|
@ -70,7 +70,6 @@ class RetryTimeCalculator {
|
||||||
this.retryCount++;
|
this.retryCount++;
|
||||||
const time = this.currentTime;
|
const time = this.currentTime;
|
||||||
this.currentTime = Math.min(this.currentTime + this.increment, this.maxTime);
|
this.currentTime = Math.min(this.currentTime + this.increment, this.maxTime);
|
||||||
console.error(time + " - " + this.retryCount);
|
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {tr} from "../i18n/localize";
|
import {tr} from "../i18n/localize";
|
||||||
|
import {LogCategory, logDebug} from "tc-shared/log";
|
||||||
|
|
||||||
export enum ImageType {
|
export enum ImageType {
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
|
@ -56,51 +57,84 @@ export function responseImageType(encoded_data: string | ArrayBuffer, base64_enc
|
||||||
return ImageType.UNKNOWN;
|
return ImageType.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ImageCacheState = {
|
||||||
|
state: "loaded",
|
||||||
|
instance: Cache
|
||||||
|
} | {
|
||||||
|
state: "errored",
|
||||||
|
reason: string
|
||||||
|
} | {
|
||||||
|
state: "unloaded"
|
||||||
|
}
|
||||||
|
|
||||||
export class ImageCache {
|
export class ImageCache {
|
||||||
readonly cache_name: string;
|
readonly cacheName: string;
|
||||||
|
|
||||||
private _cache_category: Cache;
|
private state: ImageCacheState;
|
||||||
|
|
||||||
constructor(name: string) {
|
private constructor(name: string) {
|
||||||
this.cache_name = name;
|
this.cacheName = name;
|
||||||
|
this.state = { state: "unloaded" };
|
||||||
}
|
}
|
||||||
|
|
||||||
setupped() : boolean { return !!this._cache_category; }
|
public static async load(cacheName: string) : Promise<ImageCache> {
|
||||||
|
const cache = new ImageCache(cacheName);
|
||||||
|
|
||||||
async reset() {
|
return cache;
|
||||||
if(!window.caches)
|
}
|
||||||
|
|
||||||
|
private async initialize() {
|
||||||
|
if(!window.caches) {
|
||||||
|
this.state = { "state": "errored", reason: tr("Caches are not enabled by the user") };
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await caches.delete(this.cache_name);
|
const instance = await window.caches.open(this.cacheName);
|
||||||
|
this.state = { state: "loaded", instance: instance };
|
||||||
|
} catch (error) {
|
||||||
|
logDebug(LogCategory.GENERAL, tr("Failed to open image cache %s: %o"), this.cacheName, error);
|
||||||
|
this.state = { "state": "errored", reason: tr("Failed to open the cache") };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCacheInstance() : Cache | undefined {
|
||||||
|
return this.state.state === "loaded" ? this.state.instance : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPersistent() {
|
||||||
|
return this.state.state === "loaded";
|
||||||
|
}
|
||||||
|
|
||||||
|
async reset() {
|
||||||
|
if(!window.caches) {
|
||||||
|
/* Caches are disabled by the user */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await caches.delete(this.cacheName);
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
throw "Failed to delete cache: " + error;
|
throw "Failed to delete cache: " + error;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.setup();
|
await this.initialize();
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
throw "Failed to reinitialize cache!";
|
throw "Failed to reinitialize cache!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setup() {
|
|
||||||
if(!window.caches)
|
|
||||||
throw "Missing caches!";
|
|
||||||
|
|
||||||
if(this._cache_category)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._cache_category = await caches.open(this.cache_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async cleanup(maxAge: number) {
|
async cleanup(maxAge: number) {
|
||||||
/* FIXME: TODO */
|
/* FIXME: TODO */
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveCached(key: string, maxAge?: number) : Promise<Response | undefined> {
|
async resolveCached(key: string, maxAge?: number) : Promise<Response | undefined> {
|
||||||
maxAge = typeof(maxAge) === "number" ? maxAge : -1;
|
const cacheInstance = this.getCacheInstance();
|
||||||
|
if(!cacheInstance) { return undefined; }
|
||||||
|
|
||||||
const cachedResponse = await this._cache_category.match("https://_local_cache/cache_request_" + key);
|
maxAge = typeof(maxAge) === "number" ? maxAge : -1;
|
||||||
|
const cachedResponse = await cacheInstance.match("https://_local_cache/cache_request_" + key);
|
||||||
if(!cachedResponse)
|
if(!cachedResponse)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
|
@ -109,6 +143,9 @@ export class ImageCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
async putCache(key: string, value: Response, type?: string, headers?: {[key: string]:string}) {
|
async putCache(key: string, value: Response, type?: string, headers?: {[key: string]:string}) {
|
||||||
|
const cacheInstance = this.getCacheInstance();
|
||||||
|
if(!cacheInstance) { return; }
|
||||||
|
|
||||||
const new_headers = new Headers();
|
const new_headers = new Headers();
|
||||||
for(const key of value.headers.keys())
|
for(const key of value.headers.keys())
|
||||||
new_headers.set(key, value.headers.get(key));
|
new_headers.set(key, value.headers.get(key));
|
||||||
|
@ -117,13 +154,16 @@ export class ImageCache {
|
||||||
for(const key of Object.keys(headers || {}))
|
for(const key of Object.keys(headers || {}))
|
||||||
new_headers.set(key, headers[key]);
|
new_headers.set(key, headers[key]);
|
||||||
|
|
||||||
await this._cache_category.put("https://_local_cache/cache_request_" + key, new Response(value.body, {
|
await cacheInstance.put("https://_local_cache/cache_request_" + key, new Response(value.body, {
|
||||||
headers: new_headers
|
headers: new_headers
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(key: string) {
|
async delete(key: string) {
|
||||||
const flag = await this._cache_category.delete("https://_local_cache/cache_request_" + key, {
|
const cacheInstance = this.getCacheInstance();
|
||||||
|
if(!cacheInstance) { return; }
|
||||||
|
|
||||||
|
const flag = await cacheInstance.delete("https://_local_cache/cache_request_" + key, {
|
||||||
ignoreVary: true,
|
ignoreVary: true,
|
||||||
ignoreMethod: true,
|
ignoreMethod: true,
|
||||||
ignoreSearch: true
|
ignoreSearch: true
|
||||||
|
|
|
@ -42,19 +42,14 @@ class LocalClientAvatar extends ClientAvatar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let localAvatarCache: ImageCache;
|
||||||
export class AvatarManager extends AbstractAvatarManager {
|
export class AvatarManager extends AbstractAvatarManager {
|
||||||
private static cache: ImageCache;
|
|
||||||
|
|
||||||
readonly handle: FileManager;
|
readonly handle: FileManager;
|
||||||
private cachedAvatars: {[avatarId: string]: LocalClientAvatar} = {};
|
private cachedAvatars: {[avatarId: string]: LocalClientAvatar} = {};
|
||||||
|
|
||||||
constructor(handle: FileManager) {
|
constructor(handle: FileManager) {
|
||||||
super();
|
super();
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
|
||||||
if(!AvatarManager.cache) {
|
|
||||||
AvatarManager.cache = new ImageCache("avatars");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
@ -83,11 +78,7 @@ export class AvatarManager extends AbstractAvatarManager {
|
||||||
|
|
||||||
/* try to lookup our cache for the avatar */
|
/* try to lookup our cache for the avatar */
|
||||||
cache_lookup: {
|
cache_lookup: {
|
||||||
if(!AvatarManager.cache.setupped()) {
|
const response = await localAvatarCache.resolveCached('avatar_' + avatar.clientAvatarId); //TODO age!
|
||||||
await AvatarManager.cache.setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await AvatarManager.cache.resolveCached('avatar_' + avatar.clientAvatarId); //TODO age!
|
|
||||||
if(!response) {
|
if(!response) {
|
||||||
break cache_lookup;
|
break cache_lookup;
|
||||||
}
|
}
|
||||||
|
@ -96,11 +87,11 @@ export class AvatarManager extends AbstractAvatarManager {
|
||||||
if(avatar.getAvatarHash() !== "unknown") {
|
if(avatar.getAvatarHash() !== "unknown") {
|
||||||
if(cachedAvatarHash === undefined) {
|
if(cachedAvatarHash === undefined) {
|
||||||
log.debug(LogCategory.FILE_TRANSFER, tr("Invalidating cached avatar for %s (Version miss match. Cached: unset, Current: %s)"), avatar.clientAvatarId, avatar.getAvatarHash());
|
log.debug(LogCategory.FILE_TRANSFER, tr("Invalidating cached avatar for %s (Version miss match. Cached: unset, Current: %s)"), avatar.clientAvatarId, avatar.getAvatarHash());
|
||||||
await AvatarManager.cache.delete('avatar_' + avatar.clientAvatarId);
|
await localAvatarCache.delete('avatar_' + avatar.clientAvatarId);
|
||||||
break cache_lookup;
|
break cache_lookup;
|
||||||
} else if(cachedAvatarHash !== avatar.getAvatarHash()) {
|
} else if(cachedAvatarHash !== avatar.getAvatarHash()) {
|
||||||
log.debug(LogCategory.FILE_TRANSFER, tr("Invalidating cached avatar for %s (Version miss match. Cached: %s, Current: %s)"), avatar.clientAvatarId, cachedAvatarHash, avatar.getAvatarHash());
|
log.debug(LogCategory.FILE_TRANSFER, tr("Invalidating cached avatar for %s (Version miss match. Cached: %s, Current: %s)"), avatar.clientAvatarId, cachedAvatarHash, avatar.getAvatarHash());
|
||||||
await AvatarManager.cache.delete('avatar_' + avatar.clientAvatarId);
|
await localAvatarCache.delete('avatar_' + avatar.clientAvatarId);
|
||||||
break cache_lookup;
|
break cache_lookup;
|
||||||
}
|
}
|
||||||
} else if(cachedAvatarHash) {
|
} else if(cachedAvatarHash) {
|
||||||
|
@ -174,7 +165,7 @@ export class AvatarManager extends AbstractAvatarManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await AvatarManager.cache.putCache('avatar_' + avatar.clientAvatarId, transferResponse.getResponse().clone(), "image/" + media, {
|
await localAvatarCache.putCache('avatar_' + avatar.clientAvatarId, transferResponse.getResponse().clone(), "image/" + media, {
|
||||||
"X-avatar-version": avatar.getAvatarHash()
|
"X-avatar-version": avatar.getAvatarHash()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -231,31 +222,29 @@ export class AvatarManager extends AbstractAvatarManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCache(clientAvatarId: string, clientAvatarHash: string) {
|
async updateCache(clientAvatarId: string, clientAvatarHash: string) {
|
||||||
AvatarManager.cache.setup().then(async () => {
|
const cached = this.cachedAvatars[clientAvatarId];
|
||||||
const cached = this.cachedAvatars[clientAvatarId];
|
if(cached) {
|
||||||
if(cached) {
|
if(cached.getAvatarHash() === clientAvatarHash)
|
||||||
if(cached.getAvatarHash() === clientAvatarHash)
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
log.info(LogCategory.GENERAL, tr("Deleting cached avatar for client %s. Cached version: %s; New version: %s"), cached.getAvatarHash(), clientAvatarHash);
|
log.info(LogCategory.GENERAL, tr("Deleting cached avatar for client %s. Cached version: %s; New version: %s"), cached.getAvatarHash(), clientAvatarHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await AvatarManager.cache.resolveCached('avatar_' + clientAvatarId);
|
const response = await localAvatarCache.resolveCached('avatar_' + clientAvatarId);
|
||||||
if(response) {
|
if(response) {
|
||||||
let cachedAvatarHash = response.headers.has("X-avatar-version") ? response.headers.get("X-avatar-version") : undefined;
|
let cachedAvatarHash = response.headers.has("X-avatar-version") ? response.headers.get("X-avatar-version") : undefined;
|
||||||
if(cachedAvatarHash !== clientAvatarHash) {
|
if(cachedAvatarHash !== clientAvatarHash) {
|
||||||
await AvatarManager.cache.delete("avatar_" + clientAvatarId).catch(error => {
|
await localAvatarCache.delete("avatar_" + clientAvatarId).catch(error => {
|
||||||
log.warn(LogCategory.FILE_TRANSFER, tr("Failed to delete avatar %s: %o"), clientAvatarId, error);
|
log.warn(LogCategory.FILE_TRANSFER, tr("Failed to delete avatar %s: %o"), clientAvatarId, error);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(cached) {
|
if(cached) {
|
||||||
cached.events.fire("avatar_changed", { newAvatarHash: clientAvatarHash });
|
cached.events.fire("avatar_changed", { newAvatarHash: clientAvatarHash });
|
||||||
this.executeAvatarLoad(cached);
|
this.executeAvatarLoad(cached);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveAvatar(clientAvatarId: string, avatarHash?: string, cacheOnly?: boolean) : ClientAvatar {
|
resolveAvatar(clientAvatarId: string, avatarHash?: string, cacheOnly?: boolean) : ClientAvatar {
|
||||||
|
@ -449,6 +438,7 @@ class LocalAvatarManagerFactory extends AbstractAvatarManagerFactory {
|
||||||
loader.register_task(Stage.LOADED, {
|
loader.register_task(Stage.LOADED, {
|
||||||
name: "Avatar init",
|
name: "Avatar init",
|
||||||
function: async () => {
|
function: async () => {
|
||||||
|
localAvatarCache = await ImageCache.load("avatars");
|
||||||
setGlobalAvatarManagerFactory(new LocalAvatarManagerFactory());
|
setGlobalAvatarManagerFactory(new LocalAvatarManagerFactory());
|
||||||
},
|
},
|
||||||
priority: 5
|
priority: 5
|
||||||
|
|
|
@ -346,9 +346,7 @@ loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||||
name: "icon init",
|
name: "icon init",
|
||||||
priority: 60,
|
priority: 60,
|
||||||
function: async () => {
|
function: async () => {
|
||||||
localIconCache = new ImageCache("icons");
|
localIconCache = await ImageCache.load("icons");
|
||||||
await localIconCache.setup();
|
|
||||||
|
|
||||||
setIconManager(new IconManager());
|
setIconManager(new IconManager());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -65,8 +65,9 @@ export const ServerLogRenderer = (props: { events: Registry<ServerLogUIEvents>,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fixScroll = () => {
|
const fixScroll = () => {
|
||||||
if(!refContainer.current)
|
if(!refContainer.current) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refContainer.current.scrollTop = scrollOffset.current === "bottom" ? refContainer.current.scrollHeight : scrollOffset.current;
|
refContainer.current.scrollTop = scrollOffset.current === "bottom" ? refContainer.current.scrollHeight : scrollOffset.current;
|
||||||
};
|
};
|
||||||
|
@ -86,7 +87,7 @@ export const ServerLogRenderer = (props: { events: Registry<ServerLogUIEvents>,
|
||||||
|
|
||||||
const top = target.scrollTop;
|
const top = target.scrollTop;
|
||||||
const total = target.scrollHeight - target.clientHeight;
|
const total = target.scrollHeight - target.clientHeight;
|
||||||
const shouldFollow = top + 50 > total;
|
const shouldFollow = top + 100 > total;
|
||||||
|
|
||||||
scrollOffset.current = shouldFollow ? "bottom" : top;
|
scrollOffset.current = shouldFollow ? "bottom" : top;
|
||||||
}}>
|
}}>
|
||||||
|
|
Loading…
Add table
Reference in a new issue