mirror of https://github.com/lukechilds/damus.git
Browse Source
This view provides a way to report content (nudity, illegal, spam) to relays. Clients can use this information to filter or warn if they choose to. This is needed for the appstore release Changelog-Added: Added a way to report contentzaps
William Casarin
2 years ago
9 changed files with 309 additions and 101 deletions
@ -0,0 +1,59 @@ |
|||||
|
// |
||||
|
// Report.swift |
||||
|
// damus |
||||
|
// |
||||
|
// Created by William Casarin on 2023-01-24. |
||||
|
// |
||||
|
|
||||
|
import Foundation |
||||
|
|
||||
|
enum ReportType: String { |
||||
|
case explicit |
||||
|
case illegal |
||||
|
case spam |
||||
|
case impersonation |
||||
|
} |
||||
|
|
||||
|
struct ReportNoteTarget { |
||||
|
let pubkey: String |
||||
|
let note_id: String |
||||
|
} |
||||
|
|
||||
|
enum ReportTarget { |
||||
|
case user(String) |
||||
|
case note(ReportNoteTarget) |
||||
|
} |
||||
|
|
||||
|
struct Report { |
||||
|
let type: ReportType |
||||
|
let target: ReportTarget |
||||
|
let message: String |
||||
|
} |
||||
|
|
||||
|
func create_report_tags(target: ReportTarget, type: ReportType) -> [[String]] { |
||||
|
var tags: [[String]] |
||||
|
switch target { |
||||
|
case .user(let pubkey): |
||||
|
tags = [["p", pubkey]] |
||||
|
case .note(let notet): |
||||
|
tags = [["e", notet.note_id], ["p", notet.pubkey]] |
||||
|
} |
||||
|
|
||||
|
tags.append(["report", type.rawValue]) |
||||
|
return tags |
||||
|
} |
||||
|
|
||||
|
func create_report_event(privkey: String, report: Report) -> NostrEvent? { |
||||
|
guard let pubkey = privkey_to_pubkey(privkey: privkey) else { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
let kind = 1984 |
||||
|
let tags = create_report_tags(target: report.target, type: report.type) |
||||
|
let ev = NostrEvent(content: report.message, pubkey: pubkey, kind: kind, tags: tags) |
||||
|
|
||||
|
ev.id = calculate_event_id(ev: ev) |
||||
|
ev.sig = sign_event(privkey: privkey, ev: ev) |
||||
|
|
||||
|
return ev |
||||
|
} |
@ -0,0 +1,93 @@ |
|||||
|
// |
||||
|
// EventMenu.swift |
||||
|
// damus |
||||
|
// |
||||
|
// Created by William Casarin on 2023-01-23. |
||||
|
// |
||||
|
|
||||
|
import SwiftUI |
||||
|
|
||||
|
struct EventMenuContext: View { |
||||
|
let event: NostrEvent |
||||
|
let privkey: String? |
||||
|
let pubkey: String |
||||
|
|
||||
|
var body: some View { |
||||
|
|
||||
|
Button { |
||||
|
UIPasteboard.general.string = event.get_content(privkey) |
||||
|
} label: { |
||||
|
Label(NSLocalizedString("Copy Text", comment: "Context menu option for copying the text from an note."), systemImage: "doc.on.doc") |
||||
|
} |
||||
|
|
||||
|
Button { |
||||
|
UIPasteboard.general.string = bech32_pubkey(pubkey) ?? pubkey |
||||
|
} label: { |
||||
|
Label(NSLocalizedString("Copy User Pubkey", comment: "Context menu option for copying the ID of the user who created the note."), systemImage: "person") |
||||
|
} |
||||
|
|
||||
|
Button { |
||||
|
UIPasteboard.general.string = bech32_note_id(event.id) ?? event.id |
||||
|
} label: { |
||||
|
Label(NSLocalizedString("Copy Note ID", comment: "Context menu option for copying the ID of the note."), systemImage: "note.text") |
||||
|
} |
||||
|
|
||||
|
Button { |
||||
|
UIPasteboard.general.string = event_to_json(ev: event) |
||||
|
} label: { |
||||
|
Label(NSLocalizedString("Copy Note JSON", comment: "Context menu option for copying the JSON text from the note."), systemImage: "square.on.square") |
||||
|
} |
||||
|
|
||||
|
Button { |
||||
|
let target: ReportTarget = .note(ReportNoteTarget(pubkey: event.pubkey, note_id: event.id)) |
||||
|
notify(.report, target) |
||||
|
} label: { |
||||
|
Label(NSLocalizedString("Report", comment: "Context menu option for reporting content."), systemImage: "exclamationmark.bubble") |
||||
|
} |
||||
|
|
||||
|
Button { |
||||
|
NotificationCenter.default.post(name: .broadcast_event, object: event) |
||||
|
} label: { |
||||
|
Label(NSLocalizedString("Broadcast", comment: "Context menu option for broadcasting the user's note to all of the user's connected relay servers."), systemImage: "globe") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
struct EventMenu: UIViewRepresentable { |
||||
|
|
||||
|
typealias UIViewType = UIButton |
||||
|
|
||||
|
let saveAction = UIAction(title: "") { action in } |
||||
|
let saveMenu = UIMenu(title: "", children: [ |
||||
|
UIAction(title: "First Menu Item", image: UIImage(systemName: "nameOfSFSymbol")) { action in |
||||
|
//code action for menu item |
||||
|
}, |
||||
|
UIAction(title: "First Menu Item", image: UIImage(systemName: "nameOfSFSymbol")) { action in |
||||
|
//code action for menu item |
||||
|
}, |
||||
|
UIAction(title: "First Menu Item", image: UIImage(systemName: "nameOfSFSymbol")) { action in |
||||
|
//code action for menu item |
||||
|
}, |
||||
|
]) |
||||
|
|
||||
|
func makeUIView(context: Context) -> UIButton { |
||||
|
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20)) |
||||
|
button.showsMenuAsPrimaryAction = true |
||||
|
button.menu = saveMenu |
||||
|
|
||||
|
return button |
||||
|
} |
||||
|
|
||||
|
func updateUIView(_ uiView: UIButton, context: Context) { |
||||
|
uiView.setImage(UIImage(systemName: "plus"), for: .normal) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct EventMenu_Previews: PreviewProvider { |
||||
|
static var previews: some View { |
||||
|
EventMenu(event: test_event, privkey: nil, pubkey: test_event.pubkey) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
*/ |
@ -0,0 +1,115 @@ |
|||||
|
// |
||||
|
// ReportView.swift |
||||
|
// damus |
||||
|
// |
||||
|
// Created by William Casarin on 2023-01-25. |
||||
|
// |
||||
|
|
||||
|
import SwiftUI |
||||
|
|
||||
|
struct ReportView: View { |
||||
|
let pool: RelayPool |
||||
|
let target: ReportTarget |
||||
|
let privkey: String |
||||
|
|
||||
|
@State var report_sent: Bool = false |
||||
|
@State var report_id: String = "" |
||||
|
|
||||
|
var body: some View { |
||||
|
if report_sent { |
||||
|
Success |
||||
|
} else { |
||||
|
MainForm |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var Success: some View { |
||||
|
VStack(alignment: .center, spacing: 20) { |
||||
|
Text("Report sent!") |
||||
|
.font(.headline) |
||||
|
|
||||
|
Text("Relays have been notified and clients will be able to use this information to filter content. Thank you!") |
||||
|
|
||||
|
Text("Report ID:") |
||||
|
|
||||
|
Text(report_id) |
||||
|
|
||||
|
Button("Copy Report ID") { |
||||
|
UIPasteboard.general.string = report_id |
||||
|
let g = UIImpactFeedbackGenerator(style: .medium) |
||||
|
g.impactOccurred() |
||||
|
} |
||||
|
} |
||||
|
.padding() |
||||
|
} |
||||
|
|
||||
|
func do_send_report(type: ReportType) { |
||||
|
guard let ev = send_report(privkey: privkey, pool: pool, target: target, type: .spam) else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
guard let note_id = bech32_note_id(ev.id) else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
report_sent = true |
||||
|
report_id = note_id |
||||
|
} |
||||
|
|
||||
|
var MainForm: some View { |
||||
|
VStack { |
||||
|
|
||||
|
Text("Report") |
||||
|
.font(.headline) |
||||
|
.padding() |
||||
|
|
||||
|
Form { |
||||
|
Section(content: { |
||||
|
Button("It's spam") { |
||||
|
do_send_report(type: .spam) |
||||
|
} |
||||
|
|
||||
|
Button("Nudity or explicit content") { |
||||
|
do_send_report(type: .explicit) |
||||
|
} |
||||
|
|
||||
|
Button("Illegal content") { |
||||
|
do_send_report(type: .illegal) |
||||
|
} |
||||
|
|
||||
|
if case .user = target { |
||||
|
Button("They are impersonating someone") { |
||||
|
do_send_report(type: .impersonation) |
||||
|
} |
||||
|
} |
||||
|
}, header: { |
||||
|
Text("What do you want to report?") |
||||
|
}, footer: { |
||||
|
Text("Your report will be sent to the relays you are connected to") |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func send_report(privkey: String, pool: RelayPool, target: ReportTarget, type: ReportType) -> NostrEvent? { |
||||
|
let report = Report(type: type, target: target, message: "") |
||||
|
guard let ev = create_report_event(privkey: privkey, report: report) else { |
||||
|
return nil |
||||
|
} |
||||
|
pool.send(.event(ev)) |
||||
|
return ev |
||||
|
} |
||||
|
|
||||
|
struct ReportView_Previews: PreviewProvider { |
||||
|
static var previews: some View { |
||||
|
let ds = test_damus_state() |
||||
|
VStack { |
||||
|
|
||||
|
ReportView(pool: ds.pool, target: ReportTarget.user(""), privkey: "") |
||||
|
|
||||
|
ReportView(pool: ds.pool, target: ReportTarget.user(""), privkey: "", report_sent: true, report_id: "report_id") |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue