Skip to content

Commit

Permalink
Merge remote-tracking branches 'pr/2362', 'pr/2361', 'pr/2319' and 'p…
Browse files Browse the repository at this point in the history
…r/2355'
  • Loading branch information
jb55 committed Aug 5, 2024
5 parents dba1799 + 90c8064 + 740c10c + 653f9fb + 1767a67 commit 774da23
Show file tree
Hide file tree
Showing 23 changed files with 635 additions and 149 deletions.
12 changes: 12 additions & 0 deletions damus/Assets.xcassets/tor.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "tor.svg.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file added damus/Assets.xcassets/tor.imageset/tor.svg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
218 changes: 196 additions & 22 deletions damus/Models/PushNotificationClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ struct PushNotificationClient {
let keypair: Keypair
let settings: UserSettingsStore
private(set) var device_token: Data? = nil
var device_token_hex: String? {
guard let device_token else { return nil }
return device_token.map { String(format: "%02.2hhx", $0) }.joined()
}

mutating func set_device_token(new_device_token: Data) async throws {
self.device_token = new_device_token
Expand All @@ -20,26 +24,21 @@ struct PushNotificationClient {
}

func send_token() async throws {
guard let device_token else { return }
// Send the device token and pubkey to the server
let token = device_token.map { String(format: "%02.2hhx", $0) }.joined()
guard let token = device_token_hex else { return }

Log.info("Sending device token to server: %s", for: .push_notifications, token)

let pubkey = self.keypair.pubkey

// Send those as JSON to the server
let json: [String: Any] = ["deviceToken": token, "pubkey": pubkey.hex()]

// create post request
let url = self.settings.send_device_token_to_localhost ? Constants.DEVICE_TOKEN_RECEIVER_TEST_URL : Constants.DEVICE_TOKEN_RECEIVER_PRODUCTION_URL
let json_data = try JSONSerialization.data(withJSONObject: json)

let url = self.current_push_notification_environment().api_base_url()
.appendingPathComponent("user-info")
.appendingPathComponent(self.keypair.pubkey.hex())
.appendingPathComponent(token)

let (data, response) = try await make_nip98_authenticated_request(
method: .post,
method: .put,
url: url,
payload: json_data,
payload: nil,
payload_type: .json,
auth_keypair: self.keypair
)
Expand All @@ -58,26 +57,23 @@ struct PushNotificationClient {
}

func revoke_token() async throws {
guard let device_token else { return }
// Send the device token and pubkey to the server
let token = device_token.map { String(format: "%02.2hhx", $0) }.joined()
guard let token = device_token_hex else { return }

Log.info("Revoking device token from server: %s", for: .push_notifications, token)

let pubkey = self.keypair.pubkey

// Send those as JSON to the server
let json: [String: Any] = ["deviceToken": token, "pubkey": pubkey.hex()]

// create post request
let url = self.settings.send_device_token_to_localhost ? Constants.DEVICE_TOKEN_REVOKER_TEST_URL : Constants.DEVICE_TOKEN_REVOKER_PRODUCTION_URL
let json_data = try JSONSerialization.data(withJSONObject: json)
let url = self.current_push_notification_environment().api_base_url()
.appendingPathComponent("user-info")
.appendingPathComponent(pubkey.hex())
.appendingPathComponent(token)


let (data, response) = try await make_nip98_authenticated_request(
method: .post,
method: .delete,
url: url,
payload: json_data,
payload: nil,
payload_type: .json,
auth_keypair: self.keypair
)
Expand All @@ -94,12 +90,190 @@ struct PushNotificationClient {

return
}

func set_settings(_ new_settings: NotificationSettings? = nil) async throws {
// Send the device token and pubkey to the server
guard let token = device_token_hex else { return }

Log.info("Sending notification preferences to the server", for: .push_notifications)

let url = self.current_push_notification_environment().api_base_url()
.appendingPathComponent("user-info")
.appendingPathComponent(self.keypair.pubkey.hex())
.appendingPathComponent(token)
.appendingPathComponent("preferences")

let json_payload = try JSONEncoder().encode(new_settings ?? NotificationSettings.from(settings: settings))

let (data, response) = try await make_nip98_authenticated_request(
method: .put,
url: url,
payload: json_payload,
payload_type: .json,
auth_keypair: self.keypair
)

if let httpResponse = response as? HTTPURLResponse {
switch httpResponse.statusCode {
case 200:
Log.info("Sent notification settings to Damus push notification server successfully", for: .push_notifications)
default:
Log.error("Error in sending notification settings to Damus push notification server. HTTP status code: %d; Response: %s", for: .push_notifications, httpResponse.statusCode, String(data: data, encoding: .utf8) ?? "Unknown")
throw ClientError.http_response_error(status_code: httpResponse.statusCode, response: data)
}
}

return
}

func get_settings() async throws -> NotificationSettings {
// Send the device token and pubkey to the server
guard let token = device_token_hex else {
throw ClientError.no_device_token
}

let url = self.current_push_notification_environment().api_base_url()
.appendingPathComponent("user-info")
.appendingPathComponent(self.keypair.pubkey.hex())
.appendingPathComponent(token)
.appendingPathComponent("preferences")

let (data, response) = try await make_nip98_authenticated_request(
method: .get,
url: url,
payload: nil,
payload_type: .json,
auth_keypair: self.keypair
)

if let httpResponse = response as? HTTPURLResponse {
switch httpResponse.statusCode {
case 200:
guard let notification_settings = NotificationSettings.from(json_data: data) else { throw ClientError.json_decoding_error }
return notification_settings
default:
Log.error("Error in getting notification settings to Damus push notification server. HTTP status code: %d; Response: %s", for: .push_notifications, httpResponse.statusCode, String(data: data, encoding: .utf8) ?? "Unknown")
throw ClientError.http_response_error(status_code: httpResponse.statusCode, response: data)
}
}
throw ClientError.could_not_process_response
}

func current_push_notification_environment() -> Environment {
return self.settings.send_device_token_to_localhost ? .local_test(host: nil) : .production
}
}

// MARK: Helper structures

extension PushNotificationClient {
enum ClientError: Error {
case http_response_error(status_code: Int, response: Data)
case could_not_process_response
case no_device_token
case json_decoding_error
}

struct NotificationSettings: Codable, Equatable {
let zap_notifications_enabled: Bool
let mention_notifications_enabled: Bool
let repost_notifications_enabled: Bool
let reaction_notifications_enabled: Bool
let dm_notifications_enabled: Bool
let only_notifications_from_following_enabled: Bool

static func from(json_data: Data) -> Self? {
guard let decoded = try? JSONDecoder().decode(Self.self, from: json_data) else { return nil }
return decoded
}

static func from(settings: UserSettingsStore) -> Self {
return NotificationSettings(
zap_notifications_enabled: settings.zap_notification,
mention_notifications_enabled: settings.mention_notification,
repost_notifications_enabled: settings.repost_notification,
reaction_notifications_enabled: settings.like_notification,
dm_notifications_enabled: settings.dm_notification,
only_notifications_from_following_enabled: settings.notification_only_from_following
)
}

}

enum Environment: CaseIterable, Codable, Identifiable, StringCodable, Equatable, Hashable {
static var allCases: [Environment] = [.local_test(host: nil), .production]

case local_test(host: String?)
case production

func text_description() -> String {
switch self {
case .local_test:
return NSLocalizedString("Test (local)", comment: "Label indicating a local test environment for Push notification functionality (Developer feature)")
case .production:
return NSLocalizedString("Production", comment: "Label indicating the production environment for Push notification functionality")
}
}

func api_base_url() -> URL {
switch self {
case .local_test(let host):
URL(string: "http://\(host ?? "localhost:8000")") ?? Constants.PUSH_NOTIFICATION_SERVER_TEST_BASE_URL
case .production:
Constants.PURPLE_API_PRODUCTION_BASE_URL

}
}

func custom_host() -> String? {
switch self {
case .local_test(let host):
return host
default:
return nil
}
}

init?(from string: String) {
switch string {
case "local_test":
self = .local_test(host: nil)
case "production":
self = .production
default:
let components = string.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false)
if components.count == 2 && components[0] == "local_test" {
self = .local_test(host: String(components[1]))
} else {
return nil
}
}
}

func to_string() -> String {
switch self {
case .local_test(let host):
if let host {
return "local_test:\(host)"
}
return "local_test"
case .production:
return "production"
}
}

var id: String {
switch self {
case .local_test(let host):
if let host {
return "local_test:\(host)"
}
else {
return "local_test"
}
case .production:
return "production"
}
}
}
}
8 changes: 4 additions & 4 deletions damus/Util/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import Foundation
class Constants {
//static let EXAMPLE_DEMOS: DamusState = .empty
static let DAMUS_APP_GROUP_IDENTIFIER: String = "group.com.damus"
static let DEVICE_TOKEN_RECEIVER_PRODUCTION_URL: URL = URL(string: "http://45.33.32.5:8000/user-info")!
static let DEVICE_TOKEN_RECEIVER_TEST_URL: URL = URL(string: "http://localhost:8000/user-info")!
static let DEVICE_TOKEN_REVOKER_PRODUCTION_URL: URL = URL(string: "http://45.33.32.5:8000/user-info/remove")!
static let DEVICE_TOKEN_REVOKER_TEST_URL: URL = URL(string: "http://localhost:8000/user-info/remove")!
static let MAIN_APP_BUNDLE_IDENTIFIER: String = "com.jb55.damus2"
static let NOTIFICATION_EXTENSION_BUNDLE_IDENTIFIER: String = "com.jb55.damus2.DamusNotificationService"

// MARK: Push notification server
static let PUSH_NOTIFICATION_SERVER_PRODUCTION_BASE_URL: URL = URL(string: "http://45.33.32.5:8000")!
static let PUSH_NOTIFICATION_SERVER_TEST_BASE_URL: URL = URL(string: "http://localhost:8000")!

// MARK: Purple
// API
static let PURPLE_API_LOCAL_TEST_BASE_URL: URL = URL(string: "http://localhost:8989")!
Expand Down
4 changes: 2 additions & 2 deletions damus/Views/Chat/ChatBubbleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ struct ChatBubble<T: View, U: ShapeStyle, V: View>: View {
stroke_style: .init(lineWidth: 4),
background_style: Color.accentColor
) {
Text("Hello there")
Text(verbatim: "Hello there")
.padding()
}
.foregroundColor(.white)
Expand All @@ -176,7 +176,7 @@ struct ChatBubble<T: View, U: ShapeStyle, V: View>: View {
stroke_style: .init(lineWidth: 4),
background_style: Color.accentColor
) {
Text("Hello there")
Text(verbatim: "Hello there")
.padding()
}
.foregroundColor(.white)
Expand Down
21 changes: 9 additions & 12 deletions damus/Views/Profile/ProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,6 @@ struct ProfileView: View {
}
}

var customNavbar: some View {
HStack {
navBackButton
Spacer()
navActionSheetButton
}
.padding(.top, 5)
.accentColor(DamusColors.white)
}

func lnButton(unownedProfile: Profile?, record: ProfileRecord?) -> some View {
return ProfileZapLinkView(unownedProfileRecord: record, profileModel: self.profile) { reactions_enabled, lud16, lnurl in
Image(reactions_enabled ? "zap.fill" : "zap")
Expand Down Expand Up @@ -458,8 +448,15 @@ struct ProfileView: View {
.navigationTitle("")
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: .principal) {
customNavbar
ToolbarItem(placement: .topBarLeading) {
navBackButton
.padding(.top, 5)
.accentColor(DamusColors.white)
}
ToolbarItem(placement: .topBarTrailing) {
navActionSheetButton
.padding(.top, 5)
.accentColor(DamusColors.white)
}
}
.toolbarBackground(.hidden)
Expand Down
7 changes: 7 additions & 0 deletions damus/Views/Relays/RelayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ struct RelayView: View {
.padding(.bottom, 2)
.lineLimit(1)
RelayType(is_paid: state.relay_model_cache.model(with_relay_id: relay)?.metadata.is_paid ?? false)

if relay.absoluteString.hasSuffix(".onion") {
Image("tor")
.resizable()
.interpolation(.none)
.frame(width: 20, height: 20)
}
}
Text(relay.absoluteString)
.font(.subheadline)
Expand Down
Loading

0 comments on commit 774da23

Please sign in to comment.