admin/services/userBlocks.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

118 lines
3.8 KiB
TypeScript

import type { UserBlockResponseDto } from '@/api/generated/models'
import {
createServiceError,
errorResult,
handleServiceError,
type ServiceCallOptions,
type ServiceResult,
shouldBubbleErrorToParent,
successResult,
} from '@/services/errorHandler'
import { unwrapApiData } from '@/services/apiResponse'
import { getUserBlocks } from '@/api/generated/user-blocks/user-blocks'
import { texts } from '@/texts'
import { parseRemittanceList } from '@/helpers/listResponse'
// docs/workflows/fa/user-blocking-reporting.md, Flow A — a directed block
// record (`blocker_id` = current user, `blocked_id` = the other user).
// Effect is mutual: once either side has a row against the other, new
// *direct* messages between them are rejected in both directions. Event
// group chats are never affected.
export type UserBlock = UserBlockResponseDto
const userBlocksApi = getUserBlocks()
export const BLOCK_USER = async (blockedId: string, options?: ServiceCallOptions): Promise<ServiceResult<UserBlock>> => {
try {
const res = await userBlocksApi.userBlocksControllerCreate({ blockedId })
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.chats.blockUserFailed, payload.code)
}
const block = unwrapApiData(payload)
if (!block?.id) {
throw createServiceError(texts.common.invalidServerResponse)
}
return successResult(block)
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) {
throw normalizedError
}
return errorResult(normalizedError)
}
}
export const UNBLOCK_USER = async (blockedId: string, options?: ServiceCallOptions): Promise<ServiceResult<UserBlock>> => {
try {
const res = await userBlocksApi.userBlocksControllerUnblock(blockedId)
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.chats.unblockUserFailed, payload.code)
}
const block = unwrapApiData(payload)
if (!block?.id) {
throw createServiceError(texts.common.invalidServerResponse)
}
return successResult(block)
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) {
throw normalizedError
}
return errorResult(normalizedError)
}
}
/**
* Checks whether the current user has an active block row against
* `otherUserId`. Used to toggle the header menu between «مسدود کردن» and
* «رفع مسدودیت» — there's no dedicated "is blocked" endpoint, so this
* reuses `GET /user-blocks/me?filters[blockedId]=...` (paginated, so a
* single-row page is enough to answer a yes/no question).
*
* Note: this only reflects blocks *I* created (`blocker_id = me`), not
* ones the other person may have created against me — matching the known
* gap documented in user-blocking-reporting.md ("هیچ endpoint «چه کسی مرا
* مسدود کرده» وجود ندارد").
*/
export const GET_MY_BLOCK_STATUS = async (
otherUserId: string,
options?: ServiceCallOptions
): Promise<ServiceResult<{ isBlocked: boolean }>> => {
try {
const res = await userBlocksApi.userBlocksControllerListMine({ page: 1, pageSize: 1, 'filters[blockedId]': otherUserId })
const payload = res.data
if (!payload.success) {
throw createServiceError(payload.message || texts.chats.blockStatusFailed)
}
const parsed = parseRemittanceList<UserBlock>(unwrapApiData(payload) ?? {}, 'items')
return successResult({ isBlocked: parsed.items.length > 0 })
} catch (error) {
const normalizedError = handleServiceError(error, options)
if (shouldBubbleErrorToParent(options)) {
throw normalizedError
}
return errorResult(normalizedError)
}
}