Use hook to bind atoms with sdk

This commit is contained in:
Ajay Bura 2022-12-31 15:54:57 +05:30
parent 67cd2fc5c4
commit 147a6065d0
9 changed files with 356 additions and 158 deletions

View file

@ -0,0 +1,63 @@
import { useAtomValue, WritableAtom } from 'jotai';
import { selectAtom } from 'jotai/utils';
import { MatrixClient } from 'matrix-js-sdk';
import { useCallback } from 'react';
import { isDirectInvite, isRoom, isSpace, isUnsupportedRoom } from '../../utils/room';
import { compareRoomsEqual, RoomsAction } from '../utils';
import { MDirectAction } from '../mDirectList';
export const useSpaceInvites = (
mx: MatrixClient,
allInvitesAtom: WritableAtom<string[], RoomsAction>
) => {
const selector = useCallback(
(rooms: string[]) => rooms.filter((roomId) => isSpace(mx.getRoom(roomId))),
[mx]
);
return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
};
export const useRoomInvites = (
mx: MatrixClient,
allInvitesAtom: WritableAtom<string[], RoomsAction>,
mDirectAtom: WritableAtom<Set<string>, MDirectAction>
) => {
const mDirects = useAtomValue(mDirectAtom);
const selector = useCallback(
(rooms: string[]) =>
rooms.filter(
(roomId) =>
isRoom(mx.getRoom(roomId)) &&
!(mDirects.has(roomId) || isDirectInvite(mx.getRoom(roomId), mx.getUserId()))
),
[mx, mDirects]
);
return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
};
export const useDirectInvites = (
mx: MatrixClient,
allInvitesAtom: WritableAtom<string[], RoomsAction>,
mDirectAtom: WritableAtom<Set<string>, MDirectAction>
) => {
const mDirects = useAtomValue(mDirectAtom);
const selector = useCallback(
(rooms: string[]) =>
rooms.filter(
(roomId) => mDirects.has(roomId) || isDirectInvite(mx.getRoom(roomId), mx.getUserId())
),
[mx, mDirects]
);
return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
};
export const useUnsupportedInvites = (
mx: MatrixClient,
allInvitesAtom: WritableAtom<string[], RoomsAction>
) => {
const selector = useCallback(
(rooms: string[]) => rooms.filter((roomId) => isUnsupportedRoom(mx.getRoom(roomId))),
[mx]
);
return useAtomValue(selectAtom(allInvitesAtom, selector, compareRoomsEqual));
};

View file

@ -0,0 +1,54 @@
import { useAtomValue, WritableAtom } from 'jotai';
import { selectAtom } from 'jotai/utils';
import { MatrixClient } from 'matrix-js-sdk';
import { useCallback } from 'react';
import { isRoom, isSpace, isUnsupportedRoom } from '../../utils/room';
import { compareRoomsEqual, RoomsAction } from '../utils';
import { MDirectAction } from '../mDirectList';
export const useSpaces = (mx: MatrixClient, allRoomsAtom: WritableAtom<string[], RoomsAction>) => {
const selector = useCallback(
(rooms: string[]) => rooms.filter((roomId) => isSpace(mx.getRoom(roomId))),
[mx]
);
return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
};
export const useRooms = (
mx: MatrixClient,
allRoomsAtom: WritableAtom<string[], RoomsAction>,
mDirectAtom: WritableAtom<Set<string>, MDirectAction>
) => {
const mDirects = useAtomValue(mDirectAtom);
const selector = useCallback(
(rooms: string[]) =>
rooms.filter((roomId) => isRoom(mx.getRoom(roomId)) && !mDirects.has(roomId)),
[mx, mDirects]
);
return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
};
export const useDirects = (
mx: MatrixClient,
allRoomsAtom: WritableAtom<string[], RoomsAction>,
mDirectAtom: WritableAtom<Set<string>, MDirectAction>
) => {
const mDirects = useAtomValue(mDirectAtom);
const selector = useCallback(
(rooms: string[]) =>
rooms.filter((roomId) => isRoom(mx.getRoom(roomId)) && mDirects.has(roomId)),
[mx, mDirects]
);
return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
};
export const useUnsupportedRooms = (
mx: MatrixClient,
allRoomsAtom: WritableAtom<string[], RoomsAction>
) => {
const selector = useCallback(
(rooms: string[]) => rooms.filter((roomId) => isUnsupportedRoom(mx.getRoom(roomId))),
[mx]
);
return useAtomValue(selectAtom(allRoomsAtom, selector, compareRoomsEqual));
};

View file

@ -1,32 +1,32 @@
import { atom } from 'jotai'; import { atom, WritableAtom } from 'jotai';
import { mx } from '../../client/mx'; import { MatrixClient } from 'matrix-js-sdk';
import { useMemo } from 'react';
import { Membership } from '../../types/matrix/room'; import { Membership } from '../../types/matrix/room';
import { isDirectInvite, isRoom, isSpace, isUnsupportedRoom } from '../utils/room'; import { RoomsAction, useBindRoomsWithMembershipsAtom } from './utils';
import { mDirectAtom } from './mDirectList';
import { atomRoomsWithMemberships } from './utils';
export const allInvitesAtom = atom<string[]>([]); const baseRoomsAtom = atom<string[]>([]);
allInvitesAtom.onMount = (setAtom) => atomRoomsWithMemberships(setAtom, mx(), [Membership.Invite]); export const allInvitesAtom = atom<string[], RoomsAction>(
(get) => get(baseRoomsAtom),
export const spaceInvitesAtom = atom((get) => (get, set, action) => {
get(allInvitesAtom).filter((roomId) => isSpace(mx().getRoom(roomId))) if (action.type === 'INITIALIZE') {
set(baseRoomsAtom, action.rooms);
return;
}
set(baseRoomsAtom, (ids) => {
const newIds = ids.filter((id) => id !== action.roomId);
if (action.type === 'PUT') newIds.push(action.roomId);
return newIds;
});
}
); );
export const roomInvitesAtom = atom((get) => export const useBindAllInvitesAtom = (
get(allInvitesAtom).filter( mx: MatrixClient,
(roomId) => allRooms: WritableAtom<string[], RoomsAction>
isRoom(mx().getRoom(roomId)) && ) => {
!(get(mDirectAtom).has(roomId) || isDirectInvite(mx().getRoom(roomId), mx().getUserId())) useBindRoomsWithMembershipsAtom(
) mx,
); allRooms,
useMemo(() => [Membership.Invite], [])
export const directInvitesAtom = atom((get) => );
get(allInvitesAtom).filter( };
(roomId) =>
get(mDirectAtom).has(roomId) || isDirectInvite(mx().getRoom(roomId), mx().getUserId())
)
);
export const unsupportedInvitesAtom = atom((get) =>
get(allInvitesAtom).filter((roomId) => isUnsupportedRoom(mx().getRoom(roomId)))
);

View file

@ -1,20 +1,47 @@
import { atom } from 'jotai'; import { atom, useSetAtom, WritableAtom } from 'jotai';
import { ClientEvent, MatrixEvent } from 'matrix-js-sdk'; import { ClientEvent, MatrixClient, MatrixEvent } from 'matrix-js-sdk';
import { mx } from '../../client/mx'; import { useEffect } from 'react';
import { AccountDataEvent } from '../../types/matrix/accountData'; import { AccountDataEvent } from '../../types/matrix/accountData';
import { getAccountData, getMDirects } from '../utils/room'; import { getAccountData, getMDirects } from '../utils/room';
export const mDirectAtom = atom(new Set<string>()); export type MDirectAction = {
mDirectAtom.onMount = (setAtom) => { type: 'INITIALIZE' | 'UPDATE';
const mDirectEvent = getAccountData(mx(), AccountDataEvent.Direct); rooms: Set<string>;
if (mDirectEvent) setAtom(getMDirects(mDirectEvent)); };
const handleAccountData = (event: MatrixEvent) => { const baseMDirectAtom = atom(new Set<string>());
setAtom(getMDirects(event)); export const mDirectAtom = atom<Set<string>, MDirectAction>(
}; (get) => get(baseMDirectAtom),
(get, set, action) => {
mx().on(ClientEvent.AccountData, handleAccountData); set(baseMDirectAtom, action.rooms);
return () => { }
mx().removeListener(ClientEvent.AccountData, handleAccountData); );
};
export const useBindMDirectAtom = (
mx: MatrixClient,
mDirect: WritableAtom<Set<string>, MDirectAction>
) => {
const setMDirect = useSetAtom(mDirect);
useEffect(() => {
const mDirectEvent = getAccountData(mx, AccountDataEvent.Direct);
if (mDirectEvent) {
setMDirect({
type: 'INITIALIZE',
rooms: getMDirects(mDirectEvent),
});
}
const handleAccountData = (event: MatrixEvent) => {
setMDirect({
type: 'UPDATE',
rooms: getMDirects(event),
});
};
mx.on(ClientEvent.AccountData, handleAccountData);
return () => {
mx.removeListener(ClientEvent.AccountData, handleAccountData);
};
}, [mx, setMDirect]);
}; };

View file

@ -4,7 +4,7 @@ import { useEffect } from 'react';
import { MuteChanges } from '../../types/matrix/room'; import { MuteChanges } from '../../types/matrix/room';
import { findMutedRule, isMutedRule } from '../utils/room'; import { findMutedRule, isMutedRule } from '../utils/room';
type MutedRoomsUpdate = export type MutedRoomsUpdate =
| { | {
type: 'INITIALIZE'; type: 'INITIALIZE';
addRooms: string[]; addRooms: string[];

View file

@ -1,27 +1,31 @@
import { atom } from 'jotai'; import { atom, WritableAtom } from 'jotai';
import { mx } from '../../client/mx'; import { MatrixClient } from 'matrix-js-sdk';
import { useMemo } from 'react';
import { Membership } from '../../types/matrix/room'; import { Membership } from '../../types/matrix/room';
import { isRoom, isSpace, isUnsupportedRoom } from '../utils/room'; import { RoomsAction, useBindRoomsWithMembershipsAtom } from './utils';
import { mDirectAtom } from './mDirectList';
import { atomRoomsWithMemberships } from './utils';
export const allRoomsAtom = atom<string[]>([]); const baseRoomsAtom = atom<string[]>([]);
allRoomsAtom.onMount = (setAtom) => atomRoomsWithMemberships(setAtom, mx(), [Membership.Join]); export const allRoomsAtom = atom<string[], RoomsAction>(
(get) => get(baseRoomsAtom),
export const spacesAtom = atom((get) => (get, set, action) => {
get(allRoomsAtom).filter((roomId) => isSpace(mx().getRoom(roomId))) if (action.type === 'INITIALIZE') {
); set(baseRoomsAtom, action.rooms);
return;
export const roomsAtom = atom((get) => }
get(allRoomsAtom).filter( set(baseRoomsAtom, (ids) => {
(roomId) => isRoom(mx().getRoom(roomId)) && !get(mDirectAtom).has(roomId) const newIds = ids.filter((id) => id !== action.roomId);
) if (action.type === 'PUT') newIds.push(action.roomId);
); return newIds;
});
export const directsAtom = atom((get) => }
get(allRoomsAtom).filter((roomId) => isRoom(mx().getRoom(roomId)) && get(mDirectAtom).has(roomId))
);
export const unsupportedRoomsAtom = atom((get) =>
get(allRoomsAtom).filter((roomId) => isUnsupportedRoom(mx().getRoom(roomId)))
); );
export const useBindAllRoomsAtom = (
mx: MatrixClient,
allRooms: WritableAtom<string[], RoomsAction>
) => {
useBindRoomsWithMembershipsAtom(
mx,
allRooms,
useMemo(() => [Membership.Join], [])
);
};

View file

@ -1,7 +1,14 @@
import produce from 'immer'; import produce from 'immer';
import { atom } from 'jotai'; import { atom, useSetAtom, WritableAtom } from 'jotai';
import { ClientEvent, MatrixEvent, Room, RoomEvent, RoomStateEvent } from 'matrix-js-sdk'; import {
import { mx } from '../../client/mx'; ClientEvent,
MatrixClient,
MatrixEvent,
Room,
RoomEvent,
RoomStateEvent,
} from 'matrix-js-sdk';
import { useEffect } from 'react';
import { Membership, RoomToParents, StateEvent } from '../../types/matrix/room'; import { Membership, RoomToParents, StateEvent } from '../../types/matrix/room';
import { import {
getRoomToParents, getRoomToParents,
@ -11,69 +18,103 @@ import {
mapParentWithChildren, mapParentWithChildren,
} from '../utils/room'; } from '../utils/room';
export const roomToParentsAtom = atom<RoomToParents>(new Map()); export type RoomToParentsAction =
roomToParentsAtom.onMount = (setAtom) => { | {
setAtom(getRoomToParents(mx())); type: 'INITIALIZE';
roomToParents: RoomToParents;
const deleteFromAtom = (roomId: string) => {
setAtom(
produce((roomToParents) => {
const noParentRooms: string[] = [];
roomToParents.delete(roomId);
roomToParents.forEach((parents, child) => {
parents.delete(roomId);
if (parents.size === 0) noParentRooms.push(child);
});
noParentRooms.forEach((room) => roomToParents.delete(room));
return roomToParents;
})
);
};
const addToAtom = (parent: string, children: string[]) => {
setAtom(
produce((roomToParents) => {
mapParentWithChildren(roomToParents, parent, children);
return roomToParents;
})
);
};
const handleAddRoom = (room: Room) => {
if (isSpace(room) && room.getMyMembership() !== Membership.Invite) {
addToAtom(room.roomId, getSpaceChildren(room));
} }
}; | {
type: 'PUT';
const handleMembershipChange = (room: Room, membership: string) => { parent: string;
if (isSpace(room) && membership === Membership.Join) { children: string[];
addToAtom(room.roomId, getSpaceChildren(room));
} }
}; | {
type: 'DELETE';
roomId: string;
};
const handleStateChange = (mEvent: MatrixEvent) => { const baseRoomToParents = atom<RoomToParents>(new Map());
if (mEvent.getType() === StateEvent.SpaceChild) { export const roomToParentsAtom = atom<RoomToParents, RoomToParentsAction>(
const childId = mEvent.getStateKey(); (get) => get(baseRoomToParents),
const roomId = mEvent.getRoomId(); (get, set, action) => {
if (childId && roomId) { if (action.type === 'INITIALIZE') {
if (isValidChild(mEvent)) addToAtom(roomId, [childId]); set(baseRoomToParents, action.roomToParents);
else deleteFromAtom(childId); return;
}
if (action.type === 'PUT') {
set(
baseRoomToParents,
produce(get(baseRoomToParents), (draftRoomToParents) => {
mapParentWithChildren(draftRoomToParents, action.parent, action.children);
})
);
return;
}
if (action.type === 'DELETE') {
set(
baseRoomToParents,
produce(get(baseRoomToParents), (draftRoomToParents) => {
const noParentRooms: string[] = [];
draftRoomToParents.delete(action.roomId);
draftRoomToParents.forEach((parents, child) => {
parents.delete(action.roomId);
if (parents.size === 0) noParentRooms.push(child);
});
noParentRooms.forEach((room) => draftRoomToParents.delete(room));
})
);
}
}
);
export const useBindRoomToParentsAtom = (
mx: MatrixClient,
roomToParents: WritableAtom<RoomToParents, RoomToParentsAction>
) => {
const setRoomToParents = useSetAtom(roomToParents);
useEffect(() => {
setRoomToParents({ type: 'INITIALIZE', roomToParents: getRoomToParents(mx) });
const handleAddRoom = (room: Room) => {
if (isSpace(room) && room.getMyMembership() !== Membership.Invite) {
setRoomToParents({ type: 'PUT', parent: room.roomId, children: getSpaceChildren(room) });
} }
} };
};
const handleDeleteRoom = (roomId: string) => { const handleMembershipChange = (room: Room, membership: string) => {
deleteFromAtom(roomId); if (isSpace(room) && membership === Membership.Join) {
}; setRoomToParents({ type: 'PUT', parent: room.roomId, children: getSpaceChildren(room) });
}
};
mx().on(ClientEvent.Room, handleAddRoom); const handleStateChange = (mEvent: MatrixEvent) => {
mx().on(RoomEvent.MyMembership, handleMembershipChange); if (mEvent.getType() === StateEvent.SpaceChild) {
mx().on(RoomStateEvent.Events, handleStateChange); const childId = mEvent.getStateKey();
mx().on(ClientEvent.DeleteRoom, handleDeleteRoom); const roomId = mEvent.getRoomId();
return () => { if (childId && roomId) {
mx().removeListener(ClientEvent.Room, handleAddRoom); if (isValidChild(mEvent)) {
mx().removeListener(RoomEvent.MyMembership, handleMembershipChange); setRoomToParents({ type: 'PUT', parent: roomId, children: [childId] });
mx().removeListener(RoomStateEvent.Events, handleStateChange); } else {
mx().removeListener(ClientEvent.DeleteRoom, handleDeleteRoom); setRoomToParents({ type: 'DELETE', roomId: childId });
}; }
}
}
};
const handleDeleteRoom = (roomId: string) => {
setRoomToParents({ type: 'DELETE', roomId });
};
mx.on(ClientEvent.Room, handleAddRoom);
mx.on(RoomEvent.MyMembership, handleMembershipChange);
mx.on(RoomStateEvent.Events, handleStateChange);
mx.on(ClientEvent.DeleteRoom, handleDeleteRoom);
return () => {
mx.removeListener(ClientEvent.Room, handleAddRoom);
mx.removeListener(RoomEvent.MyMembership, handleMembershipChange);
mx.removeListener(RoomStateEvent.Events, handleStateChange);
mx.removeListener(ClientEvent.DeleteRoom, handleDeleteRoom);
};
}, [mx, setRoomToParents]);
}; };

View file

@ -20,7 +20,7 @@ import {
} from '../utils/room'; } from '../utils/room';
import { roomToParentsAtom } from './roomToParents'; import { roomToParentsAtom } from './roomToParents';
type RoomToUnreadAction = export type RoomToUnreadAction =
| { | {
type: 'RESET'; type: 'RESET';
unreadInfos: UnreadInfo[]; unreadInfos: UnreadInfo[];

View file

@ -1,46 +1,50 @@
import { SetStateAction } from 'jotai'; import { useSetAtom, WritableAtom } from 'jotai';
import { ClientEvent, MatrixClient, Room, RoomEvent } from 'matrix-js-sdk'; import { ClientEvent, MatrixClient, Room, RoomEvent } from 'matrix-js-sdk';
import { useEffect } from 'react';
import { Membership } from '../../types/matrix/room'; import { Membership } from '../../types/matrix/room';
import { disposable } from '../utils/disposable';
export const atomRoomsWithMemberships = disposable( export type RoomsAction =
( | {
setAtom: (update: SetStateAction<string[]>) => void, type: 'INITIALIZE';
mx: MatrixClient, rooms: string[];
memberships: Membership[] }
) => { | {
type: 'PUT' | 'DELETE';
roomId: string;
};
export const useBindRoomsWithMembershipsAtom = (
mx: MatrixClient,
roomsAtom: WritableAtom<string[], RoomsAction>,
memberships: Membership[]
) => {
const setRoomsAtom = useSetAtom(roomsAtom);
useEffect(() => {
const satisfyMembership = (room: Room): boolean => const satisfyMembership = (room: Room): boolean =>
!!memberships.find((membership) => membership === room.getMyMembership()); !!memberships.find((membership) => membership === room.getMyMembership());
setRoomsAtom({
setAtom( type: 'INITIALIZE',
mx rooms: mx
.getRooms() .getRooms()
.filter(satisfyMembership) .filter(satisfyMembership)
.map((room) => room.roomId) .map((room) => room.roomId),
); });
const updateAtom = (type: 'PUT' | 'DELETE', roomId: string) => {
setAtom((ids) => {
const newIds = ids.filter((id) => id !== roomId);
if (type === 'PUT') newIds.push(roomId);
return newIds;
});
};
const handleAddRoom = (room: Room) => { const handleAddRoom = (room: Room) => {
if (satisfyMembership(room)) { if (satisfyMembership(room)) {
updateAtom('PUT', room.roomId); setRoomsAtom({ type: 'PUT', roomId: room.roomId });
} }
}; };
const handleMembershipChange = (room: Room) => { const handleMembershipChange = (room: Room) => {
if (!satisfyMembership(room)) { if (!satisfyMembership(room)) {
updateAtom('DELETE', room.roomId); setRoomsAtom({ type: 'DELETE', roomId: room.roomId });
} }
}; };
const handleDeleteRoom = (roomId: string) => { const handleDeleteRoom = (roomId: string) => {
updateAtom('DELETE', roomId); setRoomsAtom({ type: 'DELETE', roomId });
}; };
mx.on(ClientEvent.Room, handleAddRoom); mx.on(ClientEvent.Room, handleAddRoom);
@ -51,5 +55,10 @@ export const atomRoomsWithMemberships = disposable(
mx.removeListener(RoomEvent.MyMembership, handleMembershipChange); mx.removeListener(RoomEvent.MyMembership, handleMembershipChange);
mx.removeListener(ClientEvent.DeleteRoom, handleDeleteRoom); mx.removeListener(ClientEvent.DeleteRoom, handleDeleteRoom);
}; };
} }, [mx, memberships, setRoomsAtom]);
); };
export const compareRoomsEqual = (a: string[], b: string[]) => {
if (a.length !== b.length) return false;
return a.every((roomId, roomIdIndex) => roomId === b[roomIdIndex]);
};