Add sliding sync support and change font to SF Pro Display

- Enable sliding sync in config.json with matrix.org proxy
- Update font from InterVariable to SF Pro Display
- Add sliding sync state management with Jotai atoms
- Create bridge between sliding sync and existing room list atoms
- Add sliding sync settings UI in General settings
- Implement purple theme with gradient enhancements
- Add synchronization status display for sliding sync
- Update client initialization to support sliding sync

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Azi Mandias 2025-07-28 12:38:13 -04:00
parent 67b05eeb09
commit d25cc7250b
25 changed files with 1510 additions and 14 deletions

View file

@ -5,13 +5,28 @@ import { mDirectAtom, useBindMDirectAtom } from '../mDirectList';
import { roomToUnreadAtom, useBindRoomToUnreadAtom } from '../room/roomToUnread';
import { roomToParentsAtom, useBindRoomToParentsAtom } from '../room/roomToParents';
import { roomIdToTypingMembersAtom, useBindRoomIdToTypingMembersAtom } from '../typingMembers';
import { useBindSlidingSyncAtom, useSlidingSyncRoomListBridge, useShouldUseSlidingSync } from '../sliding-sync';
import { getSlidingSync } from '../../../client/initMatrix';
export const useBindAtoms = (mx: MatrixClient) => {
const slidingSync = getSlidingSync();
const shouldUseSlidingSync = useShouldUseSlidingSync();
// Bind sliding sync atoms if enabled
useBindSlidingSyncAtom(mx, slidingSync);
// Bridge sliding sync data to existing room list atoms
useSlidingSyncRoomListBridge();
// Only bind traditional room list atoms if not using sliding sync
if (!shouldUseSlidingSync) {
useBindAllInvitesAtom(mx, allInvitesAtom);
useBindAllRoomsAtom(mx, allRoomsAtom);
}
// These atoms are always bound regardless of sync method
useBindMDirectAtom(mx, mDirectAtom);
useBindAllInvitesAtom(mx, allInvitesAtom);
useBindAllRoomsAtom(mx, allRoomsAtom);
useBindRoomToParentsAtom(mx, roomToParentsAtom);
useBindRoomToUnreadAtom(mx, roomToUnreadAtom);
useBindRoomIdToTypingMembersAtom(mx, roomIdToTypingMembersAtom);
};

View file

@ -0,0 +1,3 @@
export * from './slidingSync';
export * from './useSlidingSync';
export * from './roomListBridge';

View file

@ -0,0 +1,40 @@
import { useSetAtom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { allRoomsAtom } from '../room-list/roomList';
import { allInvitesAtom } from '../room-list/inviteList';
import { slidingSyncRoomListAtom, slidingSyncEnabledAtom } from './slidingSync';
/**
* Bridge sliding sync room list data to existing Cinny room list atoms
* This allows existing UI components to work seamlessly with sliding sync
*/
export const useSlidingSyncRoomListBridge = () => {
const slidingSyncEnabled = useAtomValue(slidingSyncEnabledAtom);
const slidingSyncRoomList = useAtomValue(slidingSyncRoomListAtom);
const setAllRooms = useSetAtom(allRoomsAtom);
const setAllInvites = useSetAtom(allInvitesAtom);
useEffect(() => {
if (!slidingSyncEnabled) return;
// Bridge sliding sync room lists to existing atoms
const allRoomsList = slidingSyncRoomList.allRooms || [];
const directMessagesList = slidingSyncRoomList.directMessages || [];
const invitesList = slidingSyncRoomList.invites || [];
// Combine all rooms and DMs for the all rooms atom
const combinedRooms = [...allRoomsList, ...directMessagesList];
// Update existing atoms
setAllRooms({ type: 'INITIALIZE', rooms: combinedRooms });
setAllInvites({ type: 'INITIALIZE', invites: invitesList });
}, [slidingSyncEnabled, slidingSyncRoomList, setAllRooms, setAllInvites]);
};
/**
* Hook to determine if sliding sync should override traditional sync behavior
*/
export const useShouldUseSlidingSync = () => {
return useAtomValue(slidingSyncEnabledAtom);
};

View file

@ -0,0 +1,20 @@
import { atom } from 'jotai';
import { SlidingSync } from 'matrix-js-sdk/lib/sliding-sync';
// Sliding sync instance atom
export const slidingSyncAtom = atom<SlidingSync | null>(null);
// Sliding sync state atom
export const slidingSyncStateAtom = atom<'PREPARED' | 'SYNCING' | 'STOPPED' | 'ERROR'>('STOPPED');
// Sliding sync enabled atom
export const slidingSyncEnabledAtom = atom<boolean>(false);
// Room list data from sliding sync
export const slidingSyncRoomListAtom = atom<Record<string, string[]>>({});
// Room data from sliding sync
export const slidingSyncRoomDataAtom = atom<Record<string, any>>({});
// Error state for sliding sync
export const slidingSyncErrorAtom = atom<Error | null>(null);

View file

@ -0,0 +1,79 @@
import { useSetAtom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { MatrixClient } from 'matrix-js-sdk';
import { SlidingSync, SlidingSyncEvent } from 'matrix-js-sdk/lib/sliding-sync';
import {
slidingSyncAtom,
slidingSyncStateAtom,
slidingSyncEnabledAtom,
slidingSyncRoomListAtom,
slidingSyncRoomDataAtom,
slidingSyncErrorAtom
} from './slidingSync';
export const useBindSlidingSyncAtom = (mx: MatrixClient, slidingSync: SlidingSync | null) => {
const setSlidingSync = useSetAtom(slidingSyncAtom);
const setSlidingSyncState = useSetAtom(slidingSyncStateAtom);
const setSlidingSyncEnabled = useSetAtom(slidingSyncEnabledAtom);
const setSlidingSyncRoomList = useSetAtom(slidingSyncRoomListAtom);
const setSlidingSyncRoomData = useSetAtom(slidingSyncRoomDataAtom);
const setSlidingSyncError = useSetAtom(slidingSyncErrorAtom);
useEffect(() => {
if (!slidingSync) {
setSlidingSyncEnabled(false);
return;
}
setSlidingSync(slidingSync);
setSlidingSyncEnabled(true);
const handleSyncUpdate = () => {
setSlidingSyncState('SYNCING');
// Extract room lists
const lists: Record<string, string[]> = {};
slidingSync.getListData().forEach((listData, listKey) => {
lists[listKey] = listData.joinedRooms || [];
});
setSlidingSyncRoomList(lists);
// Extract room data
const roomData: Record<string, any> = {};
slidingSync.getRoomData().forEach((data, roomId) => {
roomData[roomId] = data;
});
setSlidingSyncRoomData(roomData);
};
const handleSyncComplete = () => {
setSlidingSyncState('PREPARED');
setSlidingSyncError(null);
};
const handleSyncError = (error: Error) => {
setSlidingSyncState('ERROR');
setSlidingSyncError(error);
};
// Bind sliding sync events
slidingSync.on(SlidingSyncEvent.List, handleSyncUpdate);
slidingSync.on(SlidingSyncEvent.RoomData, handleSyncUpdate);
// Note: These event names might need adjustment based on actual SDK events
slidingSync.on('sync' as any, handleSyncComplete);
slidingSync.on('error' as any, handleSyncError);
return () => {
slidingSync.removeListener(SlidingSyncEvent.List, handleSyncUpdate);
slidingSync.removeListener(SlidingSyncEvent.RoomData, handleSyncUpdate);
slidingSync.removeListener('sync' as any, handleSyncComplete);
slidingSync.removeListener('error' as any, handleSyncError);
};
}, [mx, slidingSync, setSlidingSync, setSlidingSyncState, setSlidingSyncEnabled, setSlidingSyncRoomList, setSlidingSyncRoomData, setSlidingSyncError]);
};
export const useSlidingSyncEnabled = () => useAtomValue(slidingSyncEnabledAtom);
export const useSlidingSyncState = () => useAtomValue(slidingSyncStateAtom);
export const useSlidingSyncRoomList = () => useAtomValue(slidingSyncRoomListAtom);
export const useSlidingSyncRoomData = () => useAtomValue(slidingSyncRoomDataAtom);
export const useSlidingSyncError = () => useAtomValue(slidingSyncErrorAtom);