69f12aa
/**
* Space-management API: space mutations behind the header menu (create
* invite / repo / board / room / dir, update + leave) plus the personal
* privacy dialog's preference endpoints.
*
* `fetchPreferences` / `updatePreferences` intentionally duplicate the
* wrappers in features/user-settings: features may not import each other,
* and the privacy dialog needs the same two endpoints. Keep both copies
* four lines long.
*/
import Apis from '@/shared/api'
import { settingsKey, unwrapApi } from '@/shared/lib'
import { invalidateCache, writeCache } from '@/shared/storage'
import type {
AppResponseData_InviteDto,
AppResponseData_SpaceDto,
AppResponseData_UserPreferencesDto,
AppResponseData_Vec_DirNodeDto,
AppResponseData_Vec_MySpaceDto,
CreateBoardCmd,
CreateDirCmd,
CreateInviteCmd,
CreateRepoCmd,
CreateRoomCmd,
UpdatePreferencesCmd,
UpdateRoomCmd,
UpdateSpaceCmd,
CreateSpaceCmd as CreateSpaceCmdSchema,
} from '@/shared/api'
export type SpaceDto = NonNullable<AppResponseData_SpaceDto['data']>
export type DirNode = NonNullable<AppResponseData_Vec_DirNodeDto['data']>[number]
export type MySpace = NonNullable<AppResponseData_Vec_MySpaceDto['data']>[number]
export type UserPreferences = NonNullable<AppResponseData_UserPreferencesDto['data']>
export type CreateSpaceCmd = CreateSpaceCmdSchema
export const createSpace = (cmd: CreateSpaceCmd) =>
unwrapApi<SpaceDto>(Apis.space.create_space({ data: cmd }))
/** Authoritative full SpaceDto — the /me/spaces payload is a slim subset. */
export const fetchSpace = (spaceName: string) =>
unwrapApi<SpaceDto>(Apis.space.get_space({ pathParams: { space_name: spaceName } }))
export const fetchSpaceDir = (spaceName: string) =>
unwrapApi<DirNode[]>(
Apis.space.get_space_dir_tree({ pathParams: { space_name: spaceName }, params: {} }),
)
export const createSpaceDir = (spaceName: string, cmd: CreateDirCmd) =>
unwrapApi<DirNode>(
Apis.space.create_dir_node({ pathParams: { space_name: spaceName }, data: cmd }),
)
/**
* Move a directory node — folders and the object nodes that stand for a repo or
* a board — to another parent (`null` = space root) and position. This is the
* only ordering write the tree has, and the sidebar drag-and-drop is the only
* caller: `PATCH /spaces/{space}/dir/{dir_id}/position`.
*/
export const moveSpaceDir = (
spaceName: string,
dirId: string,
cmd: { parentDirId: string | null; position: number },
) =>
unwrapApi<DirNode>(
Apis.space.move_dir_node({
pathParams: { space_name: spaceName, dir_id: dirId },
data: { parent_dir_id: cmd.parentDirId, position: cmd.position },
}),
)
/**
* Reorder a channel and/or file it into a folder. `parent_id` is a three-state
* field on the backend (`null` = move to the space root); it is added through an
* intersection until `bun run gen:api` sees it in a refreshed schema. The
* response is the room's own row, so the caller can verify the server actually
* applied the parent — an apihost built without it silently ignores the key.
*/
export const moveRoom = (roomId: string, cmd: { position: number; parentId: string | null }) =>
unwrapApi<{ parent_id?: string | null }>(
Apis.chat.update_room({
pathParams: { room_id: roomId },
data: {
position: cmd.position,
parent_id: cmd.parentId,
} as UpdateRoomCmd & { parent_id?: string | null },
}),
)
export const createInvite = (spaceName: string, cmd: CreateInviteCmd) =>
unwrapApi<NonNullable<AppResponseData_InviteDto['data']>>(
Apis.space.create_invite({ pathParams: { space_name: spaceName }, data: cmd }),
)
/**
* Accept an invitation by the code its email carried. Mirrors the notification
* inbox's wrapper: features may not import each other, so the two copies stay
* deliberately small.
*/
export const acceptSpaceInvite = (code: string) =>
unwrapApi<unknown>(Apis.space.accept_invite({ data: { code } }))
export const createRepo = (spaceName: string, cmd: CreateRepoCmd) =>
unwrapApi<unknown>(Apis.repos.create_repo({ pathParams: { space_name: spaceName }, data: cmd }))
export const createBoard = (spaceName: string, cmd: CreateBoardCmd) =>
unwrapApi<unknown>(Apis.issues.create_board({ pathParams: { space_name: spaceName }, data: cmd }))
export const createRoom = (spaceName: string, cmd: CreateRoomCmd) =>
unwrapApi<unknown>(
Apis.chat.create_room({ pathParams: { space_name: spaceName }, data: cmd }),
)
export const updateSpace = (spaceName: string, cmd: UpdateSpaceCmd) =>
unwrapApi<SpaceDto>(Apis.space.update_space({ pathParams: { space_name: spaceName }, data: cmd }))
export const leaveSpace = (spaceName: string) =>
unwrapApi<null>(Apis.space.leave_space({ pathParams: { space_name: spaceName } }))
export const fetchPreferences = () =>
unwrapApi<UserPreferences>(Apis.account.get_preferences())
export const updatePreferences = (cmd: UpdatePreferencesCmd) =>
unwrapApi<UserPreferences>(Apis.account.update_preferences({ data: cmd })).then(prefs => {
// Mirror of the user-settings write-through (features may not import each
// other): keep the shared preferences cache truthful after the dialog saves.
writeCache(settingsKey('preferences'), prefs)
invalidateCache(settingsKey('diff-style'))
return prefs
})