public_flag.gno
4.34 Kb · 171 lines
1package boards2
2
3import (
4 "chain"
5 "strconv"
6 "strings"
7
8 "gno.land/p/gnoland/boards"
9)
10
11// SetFlaggingThreshold sets the number of flags required to hide a thread or comment.
12//
13// Threshold is only applicable within the board where it's setted.
14func SetFlaggingThreshold(cur realm, boardID boards.ID, threshold int) {
15 if threshold < 1 {
16 panic("invalid flagging threshold")
17 }
18
19 assertRealmIsNotLocked()
20
21 board := mustGetBoard(boardID)
22 assertBoardIsNotFrozen(board)
23
24 caller := cur.Previous().Address()
25 args := boards.Args{board.ID, threshold}
26 board.Permissions.WithPermission(caller, PermissionBoardFlaggingUpdate, args, func() {
27 assertRealmIsNotLocked()
28
29 gFlaggingThresholds.Set(boardID.String(), threshold)
30
31 chain.Emit(
32 "FlaggingThresholdUpdated",
33 "caller", caller.String(),
34 "boardID", board.ID.String(),
35 "threshold", strconv.Itoa(threshold),
36 )
37 })
38}
39
40// GetFlaggingThreshold returns the number of flags required to hide a thread or comment within a board.
41func GetFlaggingThreshold(boardID boards.ID) int {
42 assertBoardExists(boardID)
43 return getFlaggingThreshold(boardID)
44}
45
46// FlagThread adds a new flag to a thread.
47//
48// Flagging requires special permissions and hides the thread when
49// the number of flags reaches a pre-defined flagging threshold.
50func FlagThread(cur realm, boardID, threadID boards.ID, reason string) {
51 reason = strings.TrimSpace(reason)
52 if reason == "" {
53 panic("flagging reason is required")
54 }
55
56 caller := cur.Previous().Address()
57 board := mustGetBoard(boardID)
58 isRealmOwner := gPerms.HasRole(caller, RoleOwner)
59 if !isRealmOwner {
60 assertRealmIsNotLocked()
61 assertBoardIsNotFrozen(board)
62 }
63
64 thread, found := getThread(board, threadID)
65 if !found {
66 panic("thread not found")
67 }
68
69 if thread.Hidden {
70 panic("flagging hidden threads is not allowed")
71 }
72
73 flagThread := func() {
74 if thread.Hidden {
75 panic("flagged thread is already hidden")
76 }
77
78 // Hide thread when flagging threshold is reached.
79 // Realm owners can hide with a single flag.
80 hide := flagItem(thread, caller, reason, getFlaggingThreshold(board.ID))
81 if hide || isRealmOwner {
82 // Remove thread from the list of visible threads
83 thread, removed := board.Threads.Remove(threadID)
84 if !removed {
85 panic("thread not found")
86 }
87
88 // Mark thread as hidden to avoid rendering content
89 thread.Hidden = true
90
91 // Keep track of hidden the thread to be able to restore it after moderation disputes
92 meta := board.Meta.(*BoardMeta)
93 meta.HiddenThreads.Add(thread)
94 }
95
96 chain.Emit(
97 "ThreadFlagged",
98 "caller", caller.String(),
99 "boardID", board.ID.String(),
100 "threadID", thread.ID.String(),
101 "reason", reason,
102 )
103 }
104
105 // Realm owners should be able to flag without permissions even when board is frozen
106 if isRealmOwner {
107 flagThread()
108 return
109 }
110
111 args := boards.Args{caller, board.ID, thread.ID, reason}
112 board.Permissions.WithPermission(caller, PermissionThreadFlag, args, func() {
113 flagThread()
114 })
115}
116
117// FlagReply adds a new flag to a comment or reply.
118//
119// Flagging requires special permissions and hides the comment or reply
120// when the number of flags reaches a pre-defined flagging threshold.
121func FlagReply(cur realm, boardID, threadID, replyID boards.ID, reason string) {
122 reason = strings.TrimSpace(reason)
123 if reason == "" {
124 panic("flagging reason is required")
125 }
126
127 caller := cur.Previous().Address()
128 board := mustGetBoard(boardID)
129 isRealmOwner := gPerms.HasRole(caller, RoleOwner)
130 if !isRealmOwner {
131 assertRealmIsNotLocked()
132 assertBoardIsNotFrozen(board)
133 }
134
135 thread := mustGetThread(board, threadID)
136 reply := mustGetReply(thread, replyID)
137 if reply.Hidden {
138 panic("flagging hidden comments or replies is not allowed")
139 }
140
141 flagReply := func() {
142 if reply.Hidden {
143 panic("flagged comment or reply is already hidden")
144 }
145
146 hide := flagItem(reply, caller, reason, getFlaggingThreshold(board.ID))
147 if hide || isRealmOwner {
148 reply.Hidden = true
149 }
150
151 chain.Emit(
152 "ReplyFlagged",
153 "caller", caller.String(),
154 "boardID", board.ID.String(),
155 "threadID", thread.ID.String(),
156 "replyID", reply.ID.String(),
157 "reason", reason,
158 )
159 }
160
161 // Realm owners should be able to flag without permissions even when board is frozen
162 if isRealmOwner {
163 flagReply()
164 return
165 }
166
167 args := boards.Args{caller, board.ID, thread.ID, reply.ID, reason}
168 board.Permissions.WithPermission(caller, PermissionReplyFlag, args, func() {
169 flagReply()
170 })
171}