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