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