Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions damus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,9 @@
BA3759972ABCCF360018D73B /* CameraPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759962ABCCF360018D73B /* CameraPreview.swift */; };
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
C6391DBA2DA542D100F5B388 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6391DB92DA542D100F5B388 /* ToastView.swift */; };
C6391DBB2DA542D100F5B388 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6391DB92DA542D100F5B388 /* ToastView.swift */; };
C6391DBC2DA542D100F5B388 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6391DB92DA542D100F5B388 /* ToastView.swift */; };
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2277EE92A089BD5006C3807 /* Router.swift */; };
D703D7192C66E47100A400EA /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D703D7182C66E47100A400EA /* UniformTypeIdentifiers.framework */; };
D703D71C2C66E47100A400EA /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D703D71B2C66E47100A400EA /* Media.xcassets */; };
Expand Down Expand Up @@ -2448,6 +2451,7 @@
BA3759962ABCCF360018D73B /* CameraPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPreview.swift; sourceTree = "<group>"; };
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
C6391DB92DA542D100F5B388 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
D2277EE92A089BD5006C3807 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
D703D7172C66E47100A400EA /* HighlighterActionExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = HighlighterActionExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
D703D7182C66E47100A400EA /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -3608,6 +3612,7 @@
4C28595F2A12A2BE004746F7 /* SupporterBadge.swift */,
5C6E1DAC2A193EC2008FC15A /* GradientButtonStyle.swift */,
5CC868DC2AA29B3200FB22BA /* NeutralButtonStyle.swift */,
C6391DB92DA542D100F5B388 /* ToastView.swift */,
);
path = Components;
sourceTree = "<group>";
Expand Down Expand Up @@ -4579,6 +4584,7 @@
BA3759922ABCCEBA0018D73B /* CameraService+Extensions.swift in Sources */,
D74F430C2B23FB9B00425B75 /* StoreObserver.swift in Sources */,
4C363A9A28283854006E126D /* Reply.swift in Sources */,
C6391DBC2DA542D100F5B388 /* ToastView.swift in Sources */,
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
D7ADD3E02B538D4200F104C4 /* DamusPurpleURLSheetView.swift in Sources */,
4CFF8F6729CC9E3A008DB934 /* FullScreenCarouselView.swift in Sources */,
Expand Down Expand Up @@ -5205,6 +5211,7 @@
82D6FB6C2CD99F7900C925F4 /* DamusPurpleURL.swift in Sources */,
82D6FB6D2CD99F7900C925F4 /* DamusPurpleEnvironment.swift in Sources */,
82D6FB6E2CD99F7900C925F4 /* PurpleStoreKitManager.swift in Sources */,
C6391DBB2DA542D100F5B388 /* ToastView.swift in Sources */,
82D6FB6F2CD99F7900C925F4 /* CameraService+Extensions.swift in Sources */,
82D6FB702CD99F7900C925F4 /* ImageResizer.swift in Sources */,
82D6FB712CD99F7900C925F4 /* PhotoCaptureProcessor.swift in Sources */,
Expand Down Expand Up @@ -5973,6 +5980,7 @@
D703D75B2C670A7F00A400EA /* Contacts.swift in Sources */,
D703D7812C670C2B00A400EA /* Bech32.swift in Sources */,
D73E5E1E2C6A9694007EB227 /* RelayFilters.swift in Sources */,
C6391DBA2DA542D100F5B388 /* ToastView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
151 changes: 151 additions & 0 deletions damus/Components/ToastView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
//
// ToastView.swift
// damus
//
// Created by Sanjay Siddharth on 08/04/25.
//

import SwiftUI

// Generic Toast View UI using which we build other custom Toasts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank for adding these documentation strings!

One tip (for next time, there is no need to fix it now), if you add this right on top of the struct without an empty line, and use 3 slashes (///), you can get this documentation to show up on the quick help or automated documentation generators.

A good article that talks about this: https://nshipster.com/swift-documentation/#documentation-is-my-new-bicycle

We are not super strict about code documentation though, so no need to worry — this is just for reference!


struct GenericToastView<Content:View>: View {
// var style: ToastStyle
// var message: String
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can delete unused code.

let content: Content

init(@ViewBuilder content: () -> Content) {
self.content = content()
}

var body: some View {
content
.padding()
.background(
RoundedRectangle(cornerRadius: 24)
.fill(.ultraThinMaterial)
.background(.white.opacity(0.05))
.overlay(
RoundedRectangle(cornerRadius: 24)
.stroke(Color.damusAdaptableBlack.opacity(0.2),lineWidth: 1)
)
.clipShape(RoundedRectangle(cornerRadius: 24))
.shadow(radius: 5.0, x:0 , y: 5)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: There is some inconsistency in the way the code is formatted.

The convention is to add a space after the :, and no space before the , is needed.

Suggested change
.shadow(radius: 5.0, x:0 , y: 5)
.shadow(radius: 5.0, x: 0, y: 5)

)
.padding()
.transition(.opacity.combined(with: .move(edge: .top)))
// .animation(.easeInOut(duration: 0.3))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused commented-out code.


}

}

// Example implementation of a toast which shows a user on how many relays a post has been sucessfully posted to .

struct PostConfirmationToastView: View {
var message : String
let style: ToastStyle
var body: some View {
GenericToastView{
HStack{
if let iconName = style.iconName{
Image(systemName: iconName)
.foregroundStyle(style.color)
}
Text(message)
.font(.caption)
.fontWeight(.semibold)
}
}
}
}
// Current implementation uses separate ViewModifiers for each type of Toast.
// FUTURE WORK: Can be improved to use one ViewModifier for all kinds of toasts .

struct PostConfirmationToastModifier: ViewModifier {
@Binding var message: String?
@State var timer: Timer?
let style: ToastStyle
@State private var offset = CGSize.zero

func body(content: Content) -> some View {
ZStack(alignment: .top){

content

if let message = message {
PostConfirmationToastView(message: message, style: style)
.padding(.top, 50)
.offset(x:offset.width)
.gesture(
DragGesture()
.onChanged{gesture in
offset = gesture.translation
}
.onEnded{_ in
if abs(offset.width)>100 {
withAnimation{
self.message=nil
offset = CGSize.zero
}
}
else{
offset = CGSize.zero
}

}
)
.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
}
}
}

extension View{
func postConfirmationToast(message: Binding<String?>, style: ToastStyle) -> some View {
self.modifier(PostConfirmationToastModifier(message: message, style: style))
}
}

enum ToastStyle{
case success
case error
case initial

}

extension ToastStyle{
var iconName: String? {
switch self {
case .error:
return "xmark.circle.fill"
case .success:
return "checkmark"
case .initial:
return nil
}
}
var color: Color {
switch self {
case .error:
return Color.red
case .success:
return Color.green
case .initial:
return Color.black
}
}
}

27 changes: 26 additions & 1 deletion damus/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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"
// }
Copy link
Collaborator

Choose a reason for hiding this comment

The 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"
Copy link
Collaborator

Choose a reason for hiding this comment

The 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..."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use NSLocalizedString here:

Suggested change
postConfirmationToastMessage = "Your note is being posted..."
postConfirmationToastMessage = NSLocalizedString("Your note is being posted...", comment: "A label on a toast that indicates the user's post is being posted")

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)
Expand Down Expand Up @@ -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")
// }
// }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused code

}
.onReceive(handle_notify(.new_mutes)) { _ in
home.filter_events()
Expand Down Expand Up @@ -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) {
Expand Down
15 changes: 14 additions & 1 deletion damus/Util/PostBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -44,6 +44,7 @@ class PostedEvent {
self.remaining = remaining.map {
Relayer(relay: $0, attempts: 0, retry_after: 10.0)
}
self.totalRelays = remaining.count
}
}

Expand All @@ -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]
Expand Down Expand Up @@ -137,6 +145,11 @@ class PostBox {
if ev.remaining.count == 0 {
self.events.removeValue(forKey: event_id)
}
print("Toatl Relays : \(ev.totalRelays)")
Copy link
Collaborator

@danieldaquino danieldaquino Jun 17, 2025

Choose a reason for hiding this comment

The 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{
Copy link
Collaborator

@danieldaquino danieldaquino Jun 17, 2025

Choose a reason for hiding this comment

The 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 Task and {

Suggested change
Task{
Task {

await relayNotification.add(item: .inProgress(ev, relay_id))
}
return prev_count != after_count
}

Expand Down