diff --git a/social-plus-sdk/core-concepts/user-management/user-operations/flag-unflag-user.mdx b/social-plus-sdk/core-concepts/user-management/user-operations/flag-unflag-user.mdx index 175af17b..ce519ff7 100644 --- a/social-plus-sdk/core-concepts/user-management/user-operations/flag-unflag-user.mdx +++ b/social-plus-sdk/core-concepts/user-management/user-operations/flag-unflag-user.mdx @@ -5,19 +5,29 @@ description: "Flag users, remove your flag, and check user flag state with socia Use user flagging when a member reports another user for moderator review. Flagging does not block, ban, or delete the user; it records reporting state that moderation workflows can review. +You can send a simple flag (just the `userId`) or an optional **structured report** that describes _what_ is being reported and _why_ — see [Add a report type, reason, and comment](#add-a-report-type-reason-and-comment-optional). + -Flutter exposes flag and unflag actions from an `AmityUser` object. TypeScript, iOS, and Android expose repository methods that take a `userId`. + Flutter exposes flag and unflag actions from an `AmityUser` object. + TypeScript, iOS, and Android expose repository methods that take a `userId`. + + The structured report fields (`reportType`, `reason`, `comment`) are + **optional** and **backward compatible**. A flag call with no report fields + behaves exactly as before. These fields are recorded for moderators and are + **not** returned in the API response. + + ## Parameters -| Operation | Required inputs | Platforms | Result shape | -| --- | --- | --- | --- | -| Flag user | `userId` | TypeScript, iOS, Android | TypeScript returns `Promise`; iOS async call throws on failure; Android returns `Completable`. | -| Flag user | `AmityUser` object | Flutter | Returns `Future`. | -| Unflag user | `userId` | TypeScript, iOS, Android | TypeScript returns `Promise`; iOS async call throws on failure; Android returns `Completable`. | -| Unflag user | `AmityUser` object | Flutter | Returns `Future`. | -| Check flag status | `userId` or `AmityUser` object | TypeScript, iOS, Android, Flutter | Boolean flag state where the platform exposes it. | +| Operation | Required inputs | Platforms | Result shape | +| ----------------- | ------------------------------------------------------ | --------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| Flag user | `userId` (optional: `reportType`, `reason`, `comment`) | TypeScript, iOS, Android | TypeScript returns `Promise`; iOS async call throws on failure; Android returns `Completable`. | +| Flag user | `AmityUser` object | Flutter | Returns `Future`. | +| Unflag user | `userId` | TypeScript, iOS, Android | TypeScript returns `Promise`; iOS async call throws on failure; Android returns `Completable`. | +| Unflag user | `AmityUser` object | Flutter | Returns `Future`. | +| Check flag status | `userId` or `AmityUser` object | TypeScript, iOS, Android, Flutter | Boolean flag state where the platform exposes it. | ## Flag a user @@ -25,21 +35,22 @@ Call the flag API when the current user reports another user. ### Inputs -| Platform | Method | Required inputs | Result shape | -| --- | --- | --- | --- | -| TypeScript | `UserRepository.flagUser(userId)` | `userId` | Returns `Promise`. | -| iOS | `userRepository.flagUser(withId:)` | `userId` | Async call that throws on failure. | -| Android | `userRepository.flagUser(userId)` | `userId` | Returns `Completable`. | -| Flutter | `user.report().flag()` | `AmityUser` object | Returns `Future`. | +| Platform | Method | Required inputs | Result shape | +| ---------- | ----------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------- | +| TypeScript | `UserRepository.flagUser(userId, options?)` | `userId` (optional `reportType`, `reason`, `comment`) | Returns `Promise` — the flagged user. | +| iOS | `userRepository.flagUser(withId:reportType:reason:comment:)` | `userId` (optional `reportType`, `reason`, `comment`) | Async call that throws on failure. | +| Android | `userRepository.flagUser(userId, reportType?, reason?, comment?)` | `userId` (optional `reportType`, `reason`, `comment`) | Returns `Completable`. | +| Flutter | `user.report().flag()` | `AmityUser` object | Returns `Future`. | ```typescript TypeScript import { UserRepository } from '@amityco/ts-sdk'; async function flagUser(userId: string) { - const flagged = await UserRepository.flagUser(userId); - console.log('Flagged:', flagged); +const flagged = await UserRepository.flagUser(userId); +console.log('Flagged:', flagged); } + ``` ```swift iOS @@ -76,29 +87,121 @@ Future flagUser(AmityUser user) async { } } ``` + + + +## Add a report type, reason, and comment (optional) + +When you want moderators to know _what_ part of a profile is being reported and _why_, attach one or more optional fields to the flag call. + +| Field | Answers | Description | +| ------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `reportType` | **What** | The aspect of the profile being reported. One of `displayName`, `description`, `avatarFileId`, or `behavior`. Omit it for a generic (typeless) report. | +| `reason` | **Why** | A predefined reason for a `behavior` report. **Required** when `reportType` is `behavior`, and not allowed with any other type. | +| `comment` | **Detail** | Free-text context from the reporter (max 300 characters). Allowed with any report type, including a typeless report. | + +### Report types + +`reportType` uses fixed values; your app decides the label to show the member. + +| Value | Suggested label | Reports the user's… | +| -------------- | --------------- | ----------------------------- | +| `displayName` | Username | display name | +| `description` | Bio | bio / description | +| `avatarFileId` | Avatar | profile image | +| `behavior` | Behavior | conduct (requires a `reason`) | + +### Reasons + +For a `behavior` report, pass a `reason` from the shared content report-reason list (the same values used when flagging posts, comments, and messages): `communityGuidelines`, `harassmentOrBullying`, `selfHarmOrSuicide`, `violenceOrThreateningContent`, `sellingRestrictedItems`, `sexualContentOrNudity`, `spamOrScams`, `falseInformation`, or `others` with a custom string. + +### Rules + + + - `reason` is **required** when `reportType` is `behavior`, and **rejected** for every other type (and for typeless reports). + - `reason` and `comment` are each capped at **300 characters**. + - A member holds **one report per report type** on a target (up to four typed reports plus one typeless). Reporting the **same** type again updates that report; reporting a **different** type adds a new one. + - Unflagging removes **all** of your reports on that user at once — there is no per-type unflag. + - Breaking a rule returns a validation error (HTTP `422`) from the server. + + +### Examples + +Pass the optional fields alongside the `userId`. Send `reason` only with a `behavior` report, and add a `comment` on any report type when the reporter provides extra detail. + + +```typescript TypeScript +import { ContentFlagReasonEnum, UserReportTypeEnum, UserRepository } from '@amityco/ts-sdk'; + +// Report a user's behavior, with a reason and comment +await UserRepository.flagUser(userId, { + reportType: UserReportTypeEnum.BEHAVIOR, + reason: ContentFlagReasonEnum.SpamOrScams, + comment: 'Repeatedly posting promo links', +}); + +// Report a profile field (no reason) +await UserRepository.flagUser(userId, { reportType: UserReportTypeEnum.AVATAR }); +``` + +```swift iOS +// Report a user's behavior, with a reason and comment +try await userRepository.flagUser( + withId: "", + reportType: .behavior, + reason: .spamOrScams, + comment: "Repeatedly posting promo links" +) + +// Report a profile field (no reason) +try await userRepository.flagUser(withId: "", reportType: .avatarFileId) +``` + +```kotlin Android +// Report a user's behavior, with a reason and comment +userRepository.flagUser( + userId = userId, + reportType = AmityUserReportType.BEHAVIOR, + reason = AmityContentFlagReason.SpamOrScams, + comment = "Repeatedly posting promo links" +).subscribe() + +// Report a profile field (no reason) +userRepository.flagUser( + userId = userId, + reportType = AmityUserReportType.AVATAR +).subscribe() +``` + + To report more than one aspect of the same profile (for example the avatar + **and** the behavior), send a separate flag call for each `reportType`. They + are stored as distinct reports. + + ## Unflag a user Call the unflag API when the current user removes their report from another user. ### Inputs -| Platform | Method | Required inputs | Result shape | -| --- | --- | --- | --- | -| TypeScript | `UserRepository.unflagUser(userId)` | `userId` | Returns `Promise`. | -| iOS | `userRepository.unflagUser(withId:)` | `userId` | Async call that throws on failure. | -| Android | `userRepository.unflagUser(userId)` | `userId` | Returns `Completable`. | -| Flutter | `user.report().unflag()` | `AmityUser` object | Returns `Future`. | +| Platform | Method | Required inputs | Result shape | +| ---------- | ------------------------------------ | ------------------ | ---------------------------------- | +| TypeScript | `UserRepository.unflagUser(userId)` | `userId` | Returns `Promise`. | +| iOS | `userRepository.unflagUser(withId:)` | `userId` | Async call that throws on failure. | +| Android | `userRepository.unflagUser(userId)` | `userId` | Returns `Completable`. | +| Flutter | `user.report().unflag()` | `AmityUser` object | Returns `Future`. | ```typescript TypeScript import { UserRepository } from '@amityco/ts-sdk'; async function unflagUser(userId: string) { - const unflagged = await UserRepository.unflagUser(userId); - console.log('Unflagged:', unflagged); +const unflagged = await UserRepository.unflagUser(userId); +console.log('Unflagged:', unflagged); } + ``` ```swift iOS @@ -135,6 +238,7 @@ Future unflagUser(AmityUser user) async { } } ``` + ## Check whether the current user flagged a user @@ -143,21 +247,22 @@ Use the flag-state API or property to decide whether to show a flag or unflag ac ### Inputs -| Platform | Method | Required inputs | Result shape | -| --- | --- | --- | --- | -| TypeScript | `UserRepository.isUserFlaggedByMe(userId)` | `userId` | Returns `Promise`. | -| iOS | `userRepository.isUserFlaggedByMe(withId:)` | `userId` | Returns `Bool` from an async call. | -| Android | `user.isFlaggedByMe()` | `AmityUser` object | Returns `Boolean` from the loaded user model. | -| Flutter | `user.isFlaggedByMe` | `AmityUser` object | Returns `bool` from the loaded user model. | +| Platform | Method | Required inputs | Result shape | +| ---------- | ------------------------------------------- | ------------------ | --------------------------------------------- | +| TypeScript | `UserRepository.isUserFlaggedByMe(userId)` | `userId` | Returns `Promise`. | +| iOS | `userRepository.isUserFlaggedByMe(withId:)` | `userId` | Returns `Bool` from an async call. | +| Android | `user.isFlaggedByMe()` | `AmityUser` object | Returns `Boolean` from the loaded user model. | +| Flutter | `user.isFlaggedByMe` | `AmityUser` object | Returns `bool` from the loaded user model. | ```typescript TypeScript import { UserRepository } from '@amityco/ts-sdk'; async function checkFlagState(userId: string) { - const isFlagged = await UserRepository.isUserFlaggedByMe(userId); - console.log('Flagged by me:', isFlagged); +const isFlagged = await UserRepository.isUserFlaggedByMe(userId); +console.log('Flagged by me:', isFlagged); } + ``` ```swift iOS @@ -185,6 +290,7 @@ void checkFlagState(AmityUser user) { print('Flagged by me: $isFlaggedByMe'); } ``` + ## Platform notes