Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}