Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ dist-js
src-tauri/target/
# Tauri Android build outputs
src-tauri/gen/android/app/build/
# Tauri Android release keystore
*.jks

# Tauri-typegen cache
.typecache

# Worktrees
.worktrees
.worktrees

58 changes: 58 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
fn main() {
#[cfg(target_os = "linux")]
unsafe {
use std::path::{Path, PathBuf};

// Tao/Tauri Wayland decorations are don't respect server side decorations, forcing GTK onto X11/XWayland for now.
// https://github.com/tauri-apps/tao/issues/1046
// https://github.com/tauri-apps/tauri/issues/11856
Expand All @@ -16,6 +18,62 @@ fn main() {
if std::env::var_os("__NV_DISABLE_EXPLICIT_SYNC").is_none() {
std::env::set_var("__NV_DISABLE_EXPLICIT_SYNC", "1");
}

// WebKit2GTK can hit compositor/DMABUF bugs
// https://github.com/tauri-apps/tauri/issues/14424
// https://github.com/tauri-apps/tauri/issues/9394
if std::env::var_os("WEBKIT_DISABLE_COMPOSITING_MODE").is_none() {
std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1");
}
if std::env::var_os("WEBKIT_DISABLE_DMABUF_RENDERER").is_none() {
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
}
Comment on lines +22 to +30
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already handled by __NV_DISABLE_EXPLICIT_SYNC no? what's the exact error/bug here? tauri-apps/tauri#9394 (comment)


// AppImage can fail to discover host GStreamer plugins/scanner. Probe
// common distro layouts, but don't override explicit user config.
// Not finding these plugings prevents Sable from launching correctly.
// Maybe there's a better way to do this?
let plugin_dirs = [
"/usr/lib/gstreamer-1.0",
"/usr/lib64/gstreamer-1.0",
"/usr/local/lib/gstreamer-1.0",
"/usr/local/lib64/gstreamer-1.0",
"/usr/lib/x86_64-linux-gnu/gstreamer-1.0",
"/usr/lib/aarch64-linux-gnu/gstreamer-1.0",
"/run/host/usr/lib/gstreamer-1.0",
"/run/host/usr/lib64/gstreamer-1.0",
];
let resolved_plugin_dir = plugin_dirs.iter().find(|dir| Path::new(dir).exists());

if std::env::var_os("GST_PLUGIN_SYSTEM_PATH_1_0").is_none() {
if let Some(dir) = resolved_plugin_dir {
std::env::set_var("GST_PLUGIN_SYSTEM_PATH_1_0", dir);
}
}
if std::env::var_os("GST_PLUGIN_PATH_1_0").is_none() {
if let Some(dir) = resolved_plugin_dir {
std::env::set_var("GST_PLUGIN_PATH_1_0", dir);
}
}
if std::env::var_os("GST_PLUGIN_SCANNER").is_none() {
let mut scanner_candidates: Vec<PathBuf> = vec![
PathBuf::from("/usr/lib/gstreamer-1.0/gst-plugin-scanner"),
PathBuf::from("/usr/lib64/gstreamer-1.0/gst-plugin-scanner"),
PathBuf::from("/usr/libexec/gstreamer-1.0/gst-plugin-scanner"),
PathBuf::from("/usr/lib/x86_64-linux-gnu/gstreamer-1.0/gst-plugin-scanner"),
PathBuf::from("/usr/lib/aarch64-linux-gnu/gstreamer-1.0/gst-plugin-scanner"),
PathBuf::from("/run/host/usr/lib/gstreamer-1.0/gst-plugin-scanner"),
PathBuf::from("/run/host/usr/lib64/gstreamer-1.0/gst-plugin-scanner"),
];

if let Some(path_env) = std::env::var_os("PATH") {
scanner_candidates.extend(std::env::split_paths(&path_env).map(|p| p.join("gst-plugin-scanner")));
}

if let Some(scanner) = scanner_candidates.iter().find(|path| path.exists()) {
std::env::set_var("GST_PLUGIN_SCANNER", scanner.as_os_str());
}
}
Comment on lines +32 to +76
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of this, we should just enable bundleMediaFramework under appimage option. https://v2.tauri.app/distribute/appimage/#multimedia-support-via-gstreamer

}

app_lib::run();
Expand Down
63 changes: 47 additions & 16 deletions src/app/pages/client/ClientRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ export function ClientRoot({ children }: ClientRootProps) {
const loadedUserIdRef = useRef<string | undefined>(undefined);
const syncStartTimeRef = useRef(performance.now());
const firstSyncReadyRef = useRef(false);
const loadingStateLoggedRef = useRef<'unknown' | 'true' | 'false'>('unknown');

const [loadState, loadMatrix, setLoadState] = useAsyncCallback<MatrixClient, Error, []>(
useCallback(async () => {
Expand Down Expand Up @@ -227,7 +228,7 @@ export function ClientRoot({ children }: ClientRootProps) {

const mx = loadState.status === AsyncStatus.Success ? loadState.data : undefined;

const [startState, startMatrix] = useAsyncCallback<void, Error, [MatrixClient]>(
const [startState, startMatrix, setStartState] = useAsyncCallback<void, Error, [MatrixClient]>(
useCallback(
(m) =>
startClient(m, {
Expand All @@ -239,6 +240,19 @@ export function ClientRoot({ children }: ClientRootProps) {
)
);

useEffect(() => {
if (loadState.status === AsyncStatus.Loading) {
firstSyncReadyRef.current = false;
syncStartTimeRef.current = performance.now();
}
if (loadState.status !== AsyncStatus.Success) {
setStartState((prev) => {
if (prev.status === AsyncStatus.Idle) return prev;
return { status: AsyncStatus.Idle };
});
}
}, [loadState.status, setStartState]);

useEffect(() => {
if (!activeSession) return;
if (loadedUserIdRef.current && loadedUserIdRef.current !== activeSession.userId) {
Expand Down Expand Up @@ -289,6 +303,32 @@ export function ClientRoot({ children }: ClientRootProps) {
}
}, [loadState, loadMatrix]);

useSyncState(
mx,
useCallback((state: string) => {
if (isClientReady(state)) {
if (!firstSyncReadyRef.current) {
firstSyncReadyRef.current = true;
try {
Sentry.metrics.distribution(
'sable.sync.time_to_ready_ms',
performance.now() - syncStartTimeRef.current
);
} catch {
// no-op: never block loading gate on telemetry
}
}
setLoading(false);
}
}, [])
);

useEffect(() => {
const next = loading ? 'true' : 'false';
if (loadingStateLoggedRef.current === next) return;
loadingStateLoggedRef.current = next;
}, [loading]);

useEffect(() => {
if (mx && !mx.clientRunning) {
startMatrix(mx);
Expand All @@ -302,21 +342,12 @@ export function ClientRoot({ children }: ClientRootProps) {
}
}, [mx]);

useSyncState(
mx,
useCallback((state: string) => {
if (isClientReady(state)) {
if (!firstSyncReadyRef.current) {
firstSyncReadyRef.current = true;
Sentry.metrics.distribution(
'sable.sync.time_to_ready_ms',
performance.now() - syncStartTimeRef.current
);
}
setLoading(false);
}
}, [])
);
useEffect(() => {
if (startState.status !== AsyncStatus.Success || !mx) return;
const syncState = mx.getSyncState();
if (!isClientReady(syncState)) return;
setLoading(false);
}, [startState.status, mx]);

// Set matrix client context: homeserver and sync type (not PII)
useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/app/pages/client/SyncStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const isSyncStatusDemoEnabled = (): boolean => {

export function SyncStatus({ mx }: SyncStatusProps) {
const [stateData, setStateData] = useState<StateData>({
current: null,
current: mx.getSyncState(),
previous: undefined,
});
const [demoIndex, setDemoIndex] = useState(0);
Expand Down
17 changes: 15 additions & 2 deletions src/app/utils/mediaCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@ async function openCache(): Promise<Cache | undefined> {
}
}

function getCacheRequest(url: string): Request {
const isAbsoluteHttpUrl = /^https?:\/\//i.test(url);
if (!isAbsoluteHttpUrl) {
return new Request(`https://sable-media-cache.invalid/${encodeURIComponent(url)}`);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this do?

}

try {
return new Request(url);
} catch {
return new Request(`https://sable-media-cache.invalid/${encodeURIComponent(url)}`);
}
}

export async function getFromMediaCache(url: string): Promise<Blob | undefined> {
const cache = await openCache();
if (!cache) return undefined;
try {
const response = await cache.match(url);
const response = await cache.match(getCacheRequest(url));
if (!response) return undefined;
return await response.blob();
} catch {
Expand Down Expand Up @@ -43,7 +56,7 @@ export async function putInMediaCache(url: string, blob: Blob): Promise<void> {
if (!cache) return;
try {
await cache.put(
url,
getCacheRequest(url),
new Response(blob, {
headers: { 'Content-Type': blob.type || 'application/octet-stream' },
})
Expand Down
3 changes: 2 additions & 1 deletion src/app/utils/mediaTransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ async function fetchMediaResponse(

async function fetchMediaBlobInternal(url: string, options?: MediaTransportOptions): Promise<Blob> {
const cacheMode = options?.cache ?? 'default';
const scopedCacheKey = getScopedMediaCacheKey(url, resolveSessionScope(options));
const resolvedScope = resolveSessionScope(options);
const scopedCacheKey = getScopedMediaCacheKey(url, resolvedScope);

if (cacheMode === 'default') {
const cachedBlob = await getFromMediaCache(scopedCacheKey);
Expand Down
18 changes: 11 additions & 7 deletions src/client/initMatrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,13 +476,17 @@ export const startClient = async (mx: MatrixClient, config?: StartClientConfig):
data?: ISyncStateData
) => {
classicSyncCount += 1;
Sentry.metrics.count('sable.sync.cycle', 1, { attributes: { transport: 'classic', state } });
debugLog.info('sync', `Classic sync state: ${state}`, {
state,
prevState: prevState ?? 'null',
syncNumber: classicSyncCount,
error: data?.error?.message,
});
if (prevState !== state) {
Sentry.metrics.count('sable.sync.cycle', 1, {
attributes: { transport: 'classic', state },
});
debugLog.info('sync', `Classic sync state: ${state}`, {
state,
prevState: prevState ?? 'null',
syncNumber: classicSyncCount,
error: data?.error?.message,
});
}
if (state === SyncState.Error || state === SyncState.Reconnecting) {
debugLog.warn('sync', `Classic sync problem: ${state}`, {
state,
Expand Down
Loading