Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}