import type { FollowResponseDto } from '@/api/generated/models' import { createServiceError, errorResult, handleServiceError, type ServiceCallOptions, type ServiceResult, shouldBubbleErrorToParent, successResult, } from '@/services/errorHandler' import { unwrapApiData } from '@/services/apiResponse' import { getOrganizerFollows } from '@/api/generated/organizer-follows/organizer-follows' import { parseRemittanceList } from '@/helpers/listResponse' import { texts } from '@/texts' export interface FollowUserSummary { /** Omitted by public follower-list responses. */ mobile?: string firstName: string | null lastName: string | null avatarUrl?: string | null gender?: 'male' | 'female' | 'other' | null } export type FollowedOrganizer = Omit & { user?: FollowUserSummary } const organizerFollowsApi = getOrganizerFollows() /** * `GET /organizers/:id/is-following` — a lightweight boolean check, no * `notifyNewEvents` in the response. Per docs/workflows/fa/organizer-follow.md, * a freshly-created (or reactivated) follow always starts with * notifyNewEvents=true on the backend, so callers show the bell "on" by * default; the real value only matters after the user has actually * toggled it in this same mounted session (handled client-side, not * re-fetched here — see docs/workflows/fa/organizer-follow.md). */ export const IS_FOLLOWING_ORGANIZER = async ( organizerId: string, options?: ServiceCallOptions ): Promise> => { try { const res = await organizerFollowsApi.organizerFollowsControllerIsFollowing(organizerId) const raw = res.data if ('success' in raw && !raw.success) { throw createServiceError(raw.message || texts.profile.fetchFollowStatusFailed) } // This endpoint isn't wrapped in the usual { success, data } envelope // (see organizer-follows.controller.ts), but tolerate either shape. const isFollowing = 'isFollowing' in raw ? Boolean(raw.isFollowing) : Boolean(unwrapApiData(raw)?.isFollowing) return successResult({ isFollowing }) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } export const FOLLOW_ORGANIZER = async (organizerId: string, options?: ServiceCallOptions): Promise> => { try { const res = await organizerFollowsApi.organizerFollowsControllerFollow(organizerId) const payload = res.data if (!payload.success) { throw createServiceError(payload.message || texts.profile.followFailed) } const follow = unwrapApiData(payload) if (!follow) { throw createServiceError(texts.common.invalidServerResponse) } return successResult(follow) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } export const UNFOLLOW_ORGANIZER = async ( organizerId: string, options?: ServiceCallOptions ): Promise> => { try { const res = await organizerFollowsApi.organizerFollowsControllerUnfollow(organizerId) const payload = res.data if (!payload.success) { throw createServiceError(payload.message || texts.profile.unfollowFailed) } return successResult({ success: true }) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } export const UPDATE_FOLLOW_SETTINGS = async ( organizerId: string, notifyNewEvents: boolean, options?: ServiceCallOptions ): Promise> => { try { const res = await organizerFollowsApi.organizerFollowsControllerUpdateFollowSettings(organizerId, { notifyNewEvents, }) const payload = res.data if (!payload.success) { throw createServiceError(payload.message || texts.profile.updateNotifyFailed) } const follow = unwrapApiData(payload) if (!follow) { throw createServiceError(texts.common.invalidServerResponse) } return successResult(follow) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } /** * `GET /organizers/me/following` — paginated. Used both for the * `/profile/following` list (page-by-page via `page`) and for the * MenuRow badge count (pageSize=1, only `pagination.totalItemsCount` is * read — see GET_FOLLOWING_COUNT below). */ export const GET_FOLLOWING = async ( page: number, pageSize: number, options?: ServiceCallOptions ): Promise> => { try { const res = await organizerFollowsApi.organizerFollowMeControllerGetFollowing({ page, pageSize, sort: '-createdAt' }) const payload = res.data if (!payload.success) { throw createServiceError(payload.message || texts.profile.fetchFollowingFailed) } const { items, pagination } = parseRemittanceList(unwrapApiData(payload) ?? {}, 'items', page, pageSize) return successResult({ items, totalItemsCount: pagination.totalItemsCount }) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } } /** Cheap count-only call for the profile MenuRow badge — pageSize=1, discards items. */ export const GET_FOLLOWING_COUNT = async (options?: ServiceCallOptions): Promise> => { const result = await GET_FOLLOWING(1, 1, options) if (!result.ok) { return result } return successResult(result.data.totalItemsCount) } /** * `GET /organizers/me/followers` — users following the current user as an * organizer/host. Only meaningful for verified hosts (see * `HostCapabilityService.isVerifiedHost` — non-hosts simply have no * followers, so this returns an empty list rather than erroring). */ export const GET_FOLLOWERS = async ( page: number, pageSize: number, options?: ServiceCallOptions ): Promise> => { try { const res = await organizerFollowsApi.organizerFollowMeControllerGetFollowers({ page, pageSize, sort: '-createdAt' }) const payload = res.data if (!payload.success) { throw createServiceError(payload.message || texts.profile.fetchFollowersFailed) } const { items, pagination } = parseRemittanceList(unwrapApiData(payload) ?? {}, 'items', page, pageSize) return successResult({ items, totalItemsCount: pagination.totalItemsCount }) } catch (error) { const normalizedError = handleServiceError(error, options) if (shouldBubbleErrorToParent(options)) { throw normalizedError } return errorResult(normalizedError) } }