Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
307db21
feat: auto-clear cache and recreate webview on server version change
jeanfbrito Apr 13, 2026
30667c0
fix: address review findings on cache-clear and webview-recreate paths
jeanfbrito Apr 13, 2026
e69e71f
revert: drop webview recreate path from cache-clear flow
jeanfbrito Apr 13, 2026
e842511
fix: guard auto cache-clear listener against uninitialized store
jeanfbrito Apr 28, 2026
309436e
fix: prevent lost build signal and double cache-clear on commit-hash …
jeanfbrito Apr 28, 2026
3685414
fix: propagate buildIdSource to prevent migration miss and version-st…
jeanfbrito Apr 28, 2026
268f926
fix(cache): handle version-source legacy baseline and wrap clear fail…
jeanfbrito Apr 28, 2026
f55cac9
feat: add Meteor Autoupdate as third bundle-version signal source
jeanfbrito Apr 28, 2026
1a4abf5
fix(servers): split lastServerBuildId into per-source baselines
jeanfbrito Apr 28, 2026
14488e4
fix: close legacy-listener race and document cache_version cookie
jeanfbrito Apr 28, 2026
8363000
fix(cache): hoist cacheVersion mismatch check above source-specific p…
jeanfbrito Apr 28, 2026
b26701a
fix(smart-cache-clear): address four auditor-surfaced bugs (C2/H1+M3/…
jeanfbrito Apr 28, 2026
1a95af4
fix(cache-clear): queue in-flight signals and fix RC version comparison
jeanfbrito Apr 28, 2026
2da0a6a
fix(cache): always dispatch on autoupdate signal; latest-wins pending…
jeanfbrito Apr 28, 2026
07c4f9f
fix(autoupdate): edge-trigger guard prevents infinite reload loop
jeanfbrito Apr 28, 2026
4b5f0cb
fix(smart-cache-clear): sentinel-aware autoupdate comparison in main …
jeanfbrito Apr 28, 2026
610ff9c
test: tighten sentinel regex and add rejection cases
jeanfbrito Apr 28, 2026
a3f50c2
refactor(cache-clear): per-source pendingClears Map in main.ts (F1)
jeanfbrito Apr 28, 2026
e3f66d0
refactor(cache-clear): retire legacy WEBVIEW_GIT_COMMIT_HASH_CHECK li…
jeanfbrito Apr 28, 2026
b9cbcb2
fix: clear on first autoupdate observation, not adopt
jeanfbrito Apr 28, 2026
31d3d3a
fix: prevent repeated smart cache clears
jeanfbrito Apr 29, 2026
3be1cee
fix(cache-clear): align preload slotKey with main pendingKey
jeanfbrito Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/servers/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const SERVER_URL_RESOLUTION_REQUESTED =
export const SERVER_URL_RESOLVED = 'server/url-resolved';
export const SERVER_DOCUMENT_VIEWER_OPEN_URL =
'server/document-viewer/open-url';
export const SERVER_WEBVIEW_RECREATE_REQUESTED =
'server/webview-recreate-requested';

export type ServersActionTypeToPayloadMap = {
[SERVERS_LOADED]: {
Expand All @@ -19,4 +21,5 @@ export type ServersActionTypeToPayloadMap = {
documentUrl: string;
documentFormat?: string;
};
[SERVER_WEBVIEW_RECREATE_REQUESTED]: { url: Server['url'] };
};
42 changes: 30 additions & 12 deletions src/servers/cache.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
import { webContents, type WebContents } from 'electron';

import { listen } from '../store';
import { dispatch, listen, select } from '../store';
import {
CLEAR_CACHE_DIALOG_DELETE_LOGIN_DATA_CLICKED,
CLEAR_CACHE_DIALOG_KEEP_LOGIN_DATA_CLICKED,
} from '../ui/actions';
import { SERVER_WEBVIEW_RECREATE_REQUESTED } from './actions';

const getServerUrlFromWebContents = (
guestWebContents: WebContents
): string | undefined => {
const { id } = guestWebContents;
const servers = select(({ servers }) => servers);
return servers.find((s) => s.webContentsId === id)?.url;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
};

const requestWebviewRecreate = (url: string): void => {
dispatch({
type: SERVER_WEBVIEW_RECREATE_REQUESTED,
payload: { url },
});
};

export const clearWebviewStorageKeepingLoginData = async (
guestWebContents: WebContents
) => {
if (!guestWebContents) return;
await guestWebContents.session.clearCache();
await guestWebContents.session.clearStorageData({
storages: [
'cookies',
'indexdb',
'filesystem',
'shadercache',
'websql',
'serviceworkers',
'cachestorage',
],
storages: ['indexdb', 'serviceworkers', 'cachestorage'],
});
guestWebContents?.reloadIgnoringCache();
const url = getServerUrlFromWebContents(guestWebContents);
if (url) {
requestWebviewRecreate(url);
return;
}
guestWebContents.reloadIgnoringCache();
};

export const clearWebviewStorageDeletingLoginData = async (
Expand All @@ -31,7 +44,12 @@ export const clearWebviewStorageDeletingLoginData = async (
if (!guestWebContents) return;
await guestWebContents.session.clearCache();
await guestWebContents.session.clearStorageData();
guestWebContents?.reloadIgnoringCache();
const url = getServerUrlFromWebContents(guestWebContents);
if (url) {
requestWebviewRecreate(url);
return;
}
guestWebContents.reloadIgnoringCache();
};

export const handleClearCacheDialog = () => {
Expand Down
3 changes: 3 additions & 0 deletions src/servers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export type Server = {
webContentsId?: number;
userLoggedIn?: boolean;
gitCommitHash?: string;
lastServerBuildId?: string;
lastCacheVersion?: string;
webviewNonce?: number;
allowedRedirects?: string[];
outlookCredentials?: OutlookCredentials;
version?: string;
Expand Down
59 changes: 59 additions & 0 deletions src/servers/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { hasMeta } from '../store/fsa';
import {
WEBVIEW_GIT_COMMIT_HASH_CHANGED,
WEBVIEW_GIT_COMMIT_HASH_CHECK,
WEBVIEW_SERVER_BUILD_CHECK,
WEBVIEW_SERVER_BUILD_UPDATED,
} from '../ui/actions';
import { getRootWindow } from '../ui/main/rootWindow';
import { getWebContentsByServerUrl } from '../ui/main/serverView';
Expand All @@ -20,6 +22,7 @@ import {
SERVER_URL_RESOLVED,
SERVERS_LOADED,
} from './actions';
import { clearWebviewStorageKeepingLoginData } from './cache';
import type { Server, ServerUrlResolutionResult } from './common';
import {
ServerUrlResolutionStatus,
Expand Down Expand Up @@ -203,6 +206,62 @@ export const setupServers = async (
}
});

listen(WEBVIEW_SERVER_BUILD_CHECK, async (action) => {
const { url, buildId, cacheVersion } = action.payload;
if (!buildId && !cacheVersion) return;

const servers = select(({ servers }) => servers);
const server = servers.find((s) => s.url === url);
if (!server) return;

const firstObservation =
!server.lastServerBuildId && !server.lastCacheVersion;

const buildChanged =
!!buildId &&
!!server.lastServerBuildId &&
server.lastServerBuildId !== buildId;
const cacheVersionChanged =
!!cacheVersion &&
!!server.lastCacheVersion &&
server.lastCacheVersion !== cacheVersion;

if (firstObservation) {
dispatch({
type: WEBVIEW_SERVER_BUILD_UPDATED,
payload: { url, buildId, cacheVersion },
});
return;
}

if (buildChanged || cacheVersionChanged) {
const guestWebContents = getWebContentsByServerUrl(url);
if (!guestWebContents) return;
const reason = buildChanged
? `buildId ${server.lastServerBuildId} -> ${buildId}`
: `cacheVersion ${server.lastCacheVersion} -> ${cacheVersion}`;
console.log(
`[Rocket.Chat Desktop] auto cache-clear + webview recreate for ${url} (${reason})`
);
await clearWebviewStorageKeepingLoginData(guestWebContents);
dispatch({
type: WEBVIEW_SERVER_BUILD_UPDATED,
payload: { url, buildId, cacheVersion },
});
return;
}

if (
(buildId && !server.lastServerBuildId) ||
(cacheVersion && !server.lastCacheVersion)
) {
dispatch({
type: WEBVIEW_SERVER_BUILD_UPDATED,
payload: { url, buildId, cacheVersion },
});
}
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

let servers = select(({ servers }) => servers);

let currentServerUrl = select(({ currentView }) =>
Expand Down
12 changes: 12 additions & 0 deletions src/servers/preload/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
openInternalVideoChatWindow,
} from './internalVideoChatWindow';
import { reloadServer } from './reloadServer';
import { setServerBuildSignals } from './serverBuild';
import {
setBackground,
setServerVersionToSidebar,
Expand All @@ -37,6 +38,9 @@ import { setUserLoggedIn } from './userLoggedIn';

type ServerInfo = {
version: string;
commit?: {
hash?: string;
};
};

export let serverInfo: ServerInfo;
Expand Down Expand Up @@ -67,6 +71,14 @@ export const RocketChatDesktop: Window['RocketChatDesktop'] = {
serverInfo = _serverInfo;
cb(_serverInfo);
setServerVersionToSidebar(_serverInfo.version);
const extended = _serverInfo as ServerInfo;
const buildId = extended?.commit?.hash || extended?.version;
const cacheVersionMatch =
typeof document !== 'undefined'
? document.cookie?.match(/(?:^|;\s*)cache_version=([^;]+)/)
: null;
const cacheVersion = cacheVersionMatch?.[1];
setServerBuildSignals({ buildId, cacheVersion });
},
setUrlResolver,
setBadge,
Expand Down
19 changes: 19 additions & 0 deletions src/servers/preload/serverBuild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { dispatch } from '../../store';
import { WEBVIEW_SERVER_BUILD_CHECK } from '../../ui/actions';
import { getServerUrl } from './urls';

export const setServerBuildSignals = (signals: {
buildId?: string;
cacheVersion?: string;
}): void => {
const { buildId, cacheVersion } = signals;
if (!buildId && !cacheVersion) return;
dispatch({
type: WEBVIEW_SERVER_BUILD_CHECK,
payload: {
url: getServerUrl(),
buildId,
cacheVersion,
},
});
};
24 changes: 23 additions & 1 deletion src/servers/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
WEBVIEW_READY,
WEBVIEW_ATTACHED,
WEBVIEW_GIT_COMMIT_HASH_CHANGED,
WEBVIEW_SERVER_BUILD_UPDATED,
WEBVIEW_ALLOWED_REDIRECTS_CHANGED,
WEBVIEW_SERVER_SUPPORTED_VERSIONS_UPDATED,
WEBVIEW_SERVER_SUPPORTED_VERSIONS_LOADING,
Expand All @@ -32,7 +33,11 @@ import {
SUPPORTED_VERSION_DIALOG_DISMISS,
WEBVIEW_SIDEBAR_CUSTOM_THEME_CHANGED,
} from '../ui/actions';
import { SERVERS_LOADED, SERVER_DOCUMENT_VIEWER_OPEN_URL } from './actions';
import {
SERVERS_LOADED,
SERVER_DOCUMENT_VIEWER_OPEN_URL,
SERVER_WEBVIEW_RECREATE_REQUESTED,
} from './actions';
import type { Server } from './common';

const ensureUrlFormat = (serverUrl: string | null): string => {
Expand All @@ -53,6 +58,7 @@ type ServersActionTypes =
| ActionOf<typeof WEBVIEW_SIDEBAR_STYLE_CHANGED>
| ActionOf<typeof WEBVIEW_SIDEBAR_CUSTOM_THEME_CHANGED>
| ActionOf<typeof WEBVIEW_GIT_COMMIT_HASH_CHANGED>
| ActionOf<typeof WEBVIEW_SERVER_BUILD_UPDATED>
| ActionOf<typeof WEBVIEW_TITLE_CHANGED>
| ActionOf<typeof WEBVIEW_UNREAD_CHANGED>
| ActionOf<typeof WEBVIEW_USER_LOGGED_IN>
Expand All @@ -72,6 +78,7 @@ type ServersActionTypes =
| ActionOf<typeof WEBVIEW_SERVER_VERSION_UPDATED>
| ActionOf<typeof SUPPORTED_VERSION_DIALOG_DISMISS>
| ActionOf<typeof SERVER_DOCUMENT_VIEWER_OPEN_URL>
| ActionOf<typeof SERVER_WEBVIEW_RECREATE_REQUESTED>
| ActionOf<typeof WEBVIEW_PAGE_TITLE_CHANGED>
| ActionOf<typeof SIDE_BAR_SERVER_REMOVE>;

Expand Down Expand Up @@ -206,6 +213,21 @@ export const servers: Reducer<Server[], ServersActionTypes> = (
return upsert(state, { url, gitCommitHash });
}

case WEBVIEW_SERVER_BUILD_UPDATED: {
const { url, buildId, cacheVersion } = action.payload;
const patch: Partial<Server> & { url: string } = { url };
if (buildId !== undefined) patch.lastServerBuildId = buildId;
if (cacheVersion !== undefined) patch.lastCacheVersion = cacheVersion;
return upsert(state, patch as Server);
}

case SERVER_WEBVIEW_RECREATE_REQUESTED: {
const { url } = action.payload;
const existing = state.find((s) => s.url === url);
const nextNonce = (existing?.webviewNonce ?? 0) + 1;
return upsert(state, { url, webviewNonce: nextNonce } as Server);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

case WEBVIEW_FAVICON_CHANGED: {
const { url, favicon } = action.payload;
return upsert(state, { url, favicon });
Expand Down
12 changes: 12 additions & 0 deletions src/ui/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ export const WEBVIEW_SIDEBAR_CUSTOM_THEME_CHANGED =
export const WEBVIEW_GIT_COMMIT_HASH_CHANGED =
'webview/git-commit-hash-changed';
export const WEBVIEW_GIT_COMMIT_HASH_CHECK = 'webview/git-commit-hash-check';
export const WEBVIEW_SERVER_BUILD_CHECK = 'webview/server-build-check';
export const WEBVIEW_SERVER_BUILD_UPDATED = 'webview/server-build-updated';
export const WEBVIEW_TITLE_CHANGED = 'webview/title-changed';
export const WEBVIEW_PAGE_TITLE_CHANGED = 'webview/page-title-changed';
export const WEBVIEW_UNREAD_CHANGED = 'webview/unread-changed';
Expand Down Expand Up @@ -240,6 +242,16 @@ export type UiActionTypeToPayloadMap = {
url: Server['url'];
gitCommitHash: Server['gitCommitHash'];
};
[WEBVIEW_SERVER_BUILD_CHECK]: {
url: Server['url'];
buildId?: string;
cacheVersion?: string;
};
[WEBVIEW_SERVER_BUILD_UPDATED]: {
url: Server['url'];
buildId?: string;
cacheVersion?: string;
};
[WEBVIEW_ALLOWED_REDIRECTS_CHANGED]: {
url: Server['url'];
allowedRedirects: Server['allowedRedirects'];
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/ServersView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const ServersView = () => {
<ReparentingContainer>
{servers.map((server) => (
<ServerPane
key={server.url}
key={`${server.url}:${server.webviewNonce ?? 0}`}
lastPath={server.lastPath}
serverUrl={server.url}
isSelected={server.selected}
Expand Down
Loading