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