vojo/src/app/hooks/useSidebarItems.ts

137 lines
3.8 KiB
TypeScript

import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react';
import { MatrixClient } from 'matrix-js-sdk';
import { AccountDataEvent } from '../../types/matrix/accountData';
import { useMatrixClient } from './useMatrixClient';
import { getAccountData, isSpace } from '../utils/room';
import { Membership } from '../../types/matrix/room';
import { useAccountDataCallback } from './useAccountDataCallback';
export type ISidebarFolder = {
name?: string;
id: string;
content: string[];
};
export type TSidebarItem = string | ISidebarFolder;
export type SidebarItems = Array<TSidebarItem>;
export type InVojoSpacesContent = {
shortcut?: string[];
sidebar?: SidebarItems;
};
export const parseSidebar = (
mx: MatrixClient,
orphanSpaces: string[],
content?: InVojoSpacesContent
) => {
const sidebar = content?.sidebar ?? content?.shortcut ?? [];
const orphans = new Set(orphanSpaces);
const items: SidebarItems = [];
const safeToAdd = (spaceId: string): boolean => {
if (typeof spaceId !== 'string') return false;
const space = mx.getRoom(spaceId);
if (space?.getMyMembership() !== Membership.Join) return false;
return isSpace(space);
};
sidebar.forEach((item) => {
if (typeof item === 'string') {
if (safeToAdd(item) && !items.includes(item)) {
orphans.delete(item);
items.push(item);
}
return;
}
if (
typeof item === 'object' &&
typeof item.id === 'string' &&
Array.isArray(item.content) &&
!items.find((i) => (typeof i === 'string' ? false : i.id === item.id))
) {
const safeContent = item.content.filter(safeToAdd);
safeContent.forEach((i) => orphans.delete(i));
items.push({
...item,
content: Array.from(new Set(safeContent)),
});
}
});
orphans.forEach((spaceId) => items.push(spaceId));
return items;
};
const LEGACY_CINNY_SPACES = 'in.cinny.spaces';
const getSpacesContent = (mx: MatrixClient): InVojoSpacesContent | undefined =>
getAccountData(mx, AccountDataEvent.VojoSpaces)?.getContent<InVojoSpacesContent>() ??
getAccountData(mx, LEGACY_CINNY_SPACES as AccountDataEvent)?.getContent<InVojoSpacesContent>();
export const useSidebarItems = (
orphanSpaces: string[]
): [SidebarItems, Dispatch<SetStateAction<SidebarItems>>] => {
const mx = useMatrixClient();
const [sidebarItems, setSidebarItems] = useState(() =>
parseSidebar(mx, orphanSpaces, getSpacesContent(mx))
);
useEffect(() => {
setSidebarItems(parseSidebar(mx, orphanSpaces, getSpacesContent(mx)));
}, [mx, orphanSpaces]);
useAccountDataCallback(
mx,
useCallback(
(mEvent) => {
if (
mEvent.getType() === AccountDataEvent.VojoSpaces ||
mEvent.getType() === LEGACY_CINNY_SPACES
) {
setSidebarItems(parseSidebar(mx, orphanSpaces, mEvent.getContent<InVojoSpacesContent>()));
}
},
[mx, orphanSpaces]
)
);
return [sidebarItems, setSidebarItems];
};
export const sidebarItemWithout = (items: SidebarItems, roomId: string) => {
const newItems: SidebarItems = items
.map((item) => {
if (typeof item === 'string') {
if (item === roomId) return null;
return item;
}
if (item.content.includes(roomId)) {
const newContent = item.content.filter((id) => id !== roomId);
if (newContent.length === 0) return null;
return {
...item,
content: newContent,
};
}
return item;
})
.filter((item) => item !== null) as SidebarItems;
return newItems;
};
export const makeVojoSpacesContent = (
mx: MatrixClient,
items: SidebarItems
): InVojoSpacesContent => {
const currentInSpaces = getSpacesContent(mx) ?? {};
const newSpacesContent: InVojoSpacesContent = {
...currentInSpaces,
sidebar: items,
};
return newSpacesContent;
};