feat: unified space tooltips and ownership transfer UI

- Use custom Tooltip on all space sidebar items instead of browser title
- Add transferOwnership action to spaceStore (federation-aware)
- Add "Transfer Ownership" context menu item for space owners
- Add TransferOwnershipModal with member search and confirmation
- Add transfer ownership option in SpaceSettings > Danger Zone
This commit is contained in:
Jannis Braun
2026-03-11 17:59:27 +01:00
parent b709862741
commit 77ed2a70c4
3 changed files with 362 additions and 15 deletions
+19
View File
@@ -68,6 +68,7 @@ interface SpaceState {
populateFromReady: (origin: string, spaces: SpaceWithChannelsAndMembers[], folders?: SpaceFolder[], dmChannels?: DmChannel[]) => void;
addSpaceFromReady: (origin: string, space: SpaceWithChannelsAndMembers) => void;
removeInstanceSpaces: (origin: string) => void;
transferOwnership: (spaceId: string, newOwnerId: string) => Promise<void>;
findExistingDmForUser: (targetUser: { id: string; homeUserId?: string | null }) => { dm: DmChannel; origin: string } | null;
}
@@ -525,6 +526,24 @@ export const useSpaceStore = create<SpaceState>((set, get) => ({
}));
},
transferOwnership: async (spaceId: string, newOwnerId: string) => {
const space = get().spaces.find(s => s.id === spaceId);
const origin = (space as TaggedSpace)?._instanceOrigin ?? '';
const client = getApiForOrigin(origin);
const updated = await client.spaces.transferOwnership(spaceId, newOwnerId);
if (origin && updated.icon) {
updated.icon = resolveAssetUrl(updated.icon, origin) ?? updated.icon;
}
if (origin && updated.banner) {
updated.banner = resolveAssetUrl(updated.banner, origin) ?? updated.banner;
}
set((state) => ({
spaces: state.spaces.map(s =>
s.id === spaceId ? { ...s, ...updated, _instanceOrigin: origin } as TaggedSpace : s
),
}));
},
findExistingDmForUser: (targetUser) => {
const { dmChannels, channelOriginMap } = get();
const me = useAuthStore.getState().user;