package boards2 import ( "chain" "chain/runtime" "strconv" "strings" "gno.land/p/gnoland/boards" ) // SetFlaggingThreshold sets the number of flags required to hide a thread or comment. // // Threshold is only applicable within the board where it's setted. func SetFlaggingThreshold(_ realm, boardID boards.ID, threshold int) { if threshold < 1 { panic("invalid flagging threshold") } assertRealmIsNotLocked() board := mustGetBoard(boardID) assertBoardIsNotFrozen(board) caller := runtime.PreviousRealm().Address() args := boards.Args{board.ID, threshold} board.Permissions.WithPermission(caller, PermissionBoardFlaggingUpdate, args, crossingFn(func() { assertRealmIsNotLocked() gFlaggingThresholds.Set(boardID.String(), threshold) chain.Emit( "FlaggingThresholdUpdated", "caller", caller.String(), "boardID", board.ID.String(), "threshold", strconv.Itoa(threshold), ) })) } // GetFlaggingThreshold returns the number of flags required to hide a thread or comment within a board. func GetFlaggingThreshold(boardID boards.ID) int { assertBoardExists(boardID) return getFlaggingThreshold(boardID) } // FlagThread adds a new flag to a thread. // // Flagging requires special permissions and hides the thread when // the number of flags reaches a pre-defined flagging threshold. func FlagThread(_ realm, boardID, threadID boards.ID, reason string) { reason = strings.TrimSpace(reason) if reason == "" { panic("flagging reason is required") } caller := runtime.PreviousRealm().Address() board := mustGetBoard(boardID) isRealmOwner := gPerms.HasRole(caller, RoleOwner) if !isRealmOwner { assertRealmIsNotLocked() assertBoardIsNotFrozen(board) } thread, found := getThread(board, threadID) if !found { panic("thread not found") } if thread.Hidden { panic("flagging hidden threads is not allowed") } flagThread := func() { if thread.Hidden { panic("flagged thread is already hidden") } // Hide thread when flagging threshold is reached. // Realm owners can hide with a single flag. hide := flagItem(thread, caller, reason, getFlaggingThreshold(board.ID)) if hide || isRealmOwner { // Remove thread from the list of visible threads thread, removed := board.Threads.Remove(threadID) if !removed { panic("thread not found") } // Mark thread as hidden to avoid rendering content thread.Hidden = true // Keep track of hidden the thread to be able to restore it after moderation disputes meta := board.Meta.(*BoardMeta) meta.HiddenThreads.Add(thread) } chain.Emit( "ThreadFlagged", "caller", caller.String(), "boardID", board.ID.String(), "threadID", thread.ID.String(), "reason", reason, ) } // Realm owners should be able to flag without permissions even when board is frozen if isRealmOwner { flagThread() return } args := boards.Args{caller, board.ID, thread.ID, reason} board.Permissions.WithPermission(caller, PermissionThreadFlag, args, crossingFn(func() { flagThread() })) } // FlagReply adds a new flag to a comment or reply. // // Flagging requires special permissions and hides the comment or reply // when the number of flags reaches a pre-defined flagging threshold. func FlagReply(_ realm, boardID, threadID, replyID boards.ID, reason string) { reason = strings.TrimSpace(reason) if reason == "" { panic("flagging reason is required") } caller := runtime.PreviousRealm().Address() board := mustGetBoard(boardID) isRealmOwner := gPerms.HasRole(caller, RoleOwner) if !isRealmOwner { assertRealmIsNotLocked() assertBoardIsNotFrozen(board) } thread := mustGetThread(board, threadID) reply := mustGetReply(thread, replyID) if reply.Hidden { panic("flagging hidden comments or replies is not allowed") } flagReply := func() { if reply.Hidden { panic("flagged comment or reply is already hidden") } hide := flagItem(reply, caller, reason, getFlaggingThreshold(board.ID)) if hide || isRealmOwner { reply.Hidden = true } chain.Emit( "ReplyFlagged", "caller", caller.String(), "boardID", board.ID.String(), "threadID", thread.ID.String(), "replyID", reply.ID.String(), "reason", reason, ) } // Realm owners should be able to flag without permissions even when board is frozen if isRealmOwner { flagReply() return } args := boards.Args{caller, board.ID, thread.ID, reply.ID, reason} board.Permissions.WithPermission(caller, PermissionReplyFlag, args, crossingFn(func() { flagReply() })) }