-
Notifications
You must be signed in to change notification settings - Fork 296
Posting confirmation Toast #3015
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
634f86e
8f7a5f6
32145c8
8fe1352
9d5e354
decddfb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// | ||
// ToastView.swift | ||
// damus | ||
// | ||
// Created by Sanjay Siddharth on 08/04/25. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct ToastView: View { | ||
var style: ToastStyle | ||
var message: String | ||
|
||
var body: some View { | ||
HStack{ | ||
Image(systemName: style.iconName) | ||
.foregroundStyle(style.color) | ||
Text(message) | ||
.font(.caption) | ||
.fontWeight(.semibold) | ||
} | ||
.padding() | ||
.background( | ||
RoundedRectangle(cornerRadius: 24) | ||
.stroke(.purple, lineWidth: 0.5) | ||
.background( | ||
RoundedRectangle(cornerRadius: 24) | ||
.fill(.white) | ||
) | ||
.shadow(color: .purple.opacity(0.35), radius: 6) | ||
|
||
) | ||
.padding() | ||
.transition(.opacity.combined(with: .move(edge: .top))) | ||
.animation(.easeInOut(duration: 0.3), value: message) | ||
} | ||
} | ||
|
||
#Preview { | ||
ToastView(style: .success, message: "Your note has been posted to 10/14 relays") | ||
ToastView(style: .error, message: "Could not post your note") | ||
|
||
} | ||
|
||
struct ToastModifier: ViewModifier { | ||
@Binding var message: String? | ||
@State var timer: Timer? | ||
let style: ToastStyle | ||
|
||
func body(content: Content) -> some View { | ||
ZStack(alignment: .top){ | ||
content | ||
|
||
if let message = message { | ||
ToastView(style: style, message: message) | ||
.padding(.top, 50) | ||
.animation(.easeInOut, value: message) | ||
.onChange(of: message){ _ in | ||
restartTimer() | ||
} | ||
.onAppear{ | ||
restartTimer() | ||
} | ||
} | ||
} | ||
} | ||
private func restartTimer(){ | ||
timer?.invalidate() | ||
timer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { _ in | ||
message = nil | ||
} | ||
} | ||
} | ||
|
||
struct Toast: Equatable { | ||
var style: ToastStyle | ||
var message: String | ||
var Duration: Double = 3 | ||
var width: Double = .infinity | ||
} | ||
|
||
enum ToastStyle{ | ||
case success | ||
case error | ||
|
||
} | ||
|
||
extension ToastStyle{ | ||
var iconName: String { | ||
switch self { | ||
case .error: | ||
return "xmark.circle.fill" | ||
case .success: | ||
return "checkmark" | ||
} | ||
} | ||
var color: Color { | ||
switch self { | ||
case .error: | ||
return Color.red | ||
case .success: | ||
return Color.green | ||
} | ||
|
||
} | ||
} | ||
|
||
extension View{ | ||
func postConfirmationToast(message: Binding<String?>, style: ToastStyle) -> some View { | ||
self.modifier(ToastModifier(message: message, style: style)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -134,6 +134,7 @@ struct ContentView: View { | |||||
@StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator() | ||||||
@AppStorage("has_seen_suggested_users") private var hasSeenOnboardingSuggestions = false | ||||||
let sub_id = UUID().description | ||||||
@State var postConfirmationToastMessage: String? | ||||||
|
||||||
// connect retry timer | ||||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() | ||||||
|
@@ -297,6 +298,22 @@ struct ContentView: View { | |||||
} | ||||||
.ignoresSafeArea(.keyboard) | ||||||
.edgesIgnoringSafeArea(hide_bar ? [.bottom] : []) | ||||||
.postConfirmationToast(message: $postConfirmationToastMessage, style: .success) | ||||||
.task { | ||||||
// for await (event, relayID) in relayNotification.stream { | ||||||
// postConfirmationToastMessage = "Your note has been posted to \(event.totalRelays-event.remaining.count) out of \(event.totalRelays) relays" | ||||||
// } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused code |
||||||
for await notification in relayNotification.stream { | ||||||
switch notification { | ||||||
case .inProgress(let event, let relayID): | ||||||
postConfirmationToastMessage = "Your note has been posted to \(event.totalRelays-event.remaining.count) out of \(event.totalRelays) relays" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should format this for localization to make sure it can be translated into different languages. This one is a bit trickier than usual, but here is a useful comment from @tyiu from another recent PR that had a similar situation: #3031 (comment) |
||||||
|
||||||
case .initial: | ||||||
postConfirmationToastMessage = "Your note is being posted..." | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use
Suggested change
This will ensure that our translators can translate this label to other languages. |
||||||
|
||||||
} | ||||||
} | ||||||
} | ||||||
.onAppear() { | ||||||
self.connect() | ||||||
try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: .default, options: .mixWithOthers) | ||||||
|
@@ -421,6 +438,11 @@ struct ContentView: View { | |||||
if !handle_post_notification(keypair: keypair, postbox: state.postbox, events: state.events, post: post) { | ||||||
self.active_sheet = nil | ||||||
} | ||||||
// Task { | ||||||
// for await (event, relayID) in relayNotification.stream { | ||||||
// print("Rithi Event: Posted to \(event.totalRelays-event.remaining.count) out of \(event.totalRelays) relays") | ||||||
// } | ||||||
// } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused code |
||||||
} | ||||||
.onReceive(handle_notify(.new_mutes)) { _ in | ||||||
home.filter_events() | ||||||
|
@@ -1181,7 +1203,10 @@ func handle_post_notification(keypair: FullKeypair, postbox: PostBox, events: Ev | |||||
guard let new_ev = post.to_event(keypair: keypair) else { | ||||||
return false | ||||||
} | ||||||
postbox.send(new_ev) | ||||||
Task{ | ||||||
await relayNotification.add(item: .initial) | ||||||
postbox.send(new_ev) | ||||||
} | ||||||
for eref in new_ev.referenced_ids.prefix(3) { | ||||||
// also broadcast at most 3 referenced events | ||||||
if let ev = events.lookup(eref) { | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -34,7 +34,7 @@ class PostedEvent { | |||||
let flush_after: Date? | ||||||
var flushed_once: Bool | ||||||
let on_flush: OnFlush? | ||||||
|
||||||
let totalRelays: Int | ||||||
init(event: NostrEvent, remaining: [RelayURL], skip_ephemeral: Bool, flush_after: Date?, on_flush: OnFlush?) { | ||||||
self.event = event | ||||||
self.skip_ephemeral = skip_ephemeral | ||||||
|
@@ -44,6 +44,7 @@ class PostedEvent { | |||||
self.remaining = remaining.map { | ||||||
Relayer(relay: $0, attempts: 0, retry_after: 10.0) | ||||||
} | ||||||
self.totalRelays = remaining.count | ||||||
} | ||||||
} | ||||||
|
||||||
|
@@ -53,6 +54,13 @@ enum CancelSendErr { | |||||
case too_late | ||||||
} | ||||||
|
||||||
enum RelayNotification { | ||||||
case initial | ||||||
case inProgress(PostedEvent,RelayURL) | ||||||
} | ||||||
|
||||||
let relayNotification = QueueableNotify<(RelayNotification)>(maxQueueItems: 100) | ||||||
|
||||||
class PostBox { | ||||||
let pool: RelayPool | ||||||
var events: [NoteId: PostedEvent] | ||||||
|
@@ -137,6 +145,11 @@ class PostBox { | |||||
if ev.remaining.count == 0 { | ||||||
self.events.removeValue(forKey: event_id) | ||||||
} | ||||||
print("Toatl Relays : \(ev.totalRelays)") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this debug log? It looks like we might not need it. |
||||||
|
||||||
Task{ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick/Minor: Please use code formatting conventions for styling consistency. In this case, the convention is generally to use a space between
Suggested change
|
||||||
await relayNotification.add(item: .inProgress(ev, relay_id)) | ||||||
} | ||||||
return prev_count != after_count | ||||||
} | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can make the base
ToastView
more generic and allow other usages to make toasts with custom layouts, by using ViewBuilders:EventMutingContainerView.swift