Compare commits
8 Commits
tyiu/local
...
tyiu/remov
| Author | SHA1 | Date | |
|---|---|---|---|
|
6858d7e72f
|
|||
|
|
476f52562a | ||
|
|
f591ad2dff | ||
|
|
dacade299d | ||
|
|
cdacbcfdca | ||
|
|
41e036cff2 | ||
| eb901a4d84 | |||
|
|
9f15688699 |
@@ -415,6 +415,7 @@
|
||||
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9609F057296E220800069BF3 /* BannerImageView.swift */; };
|
||||
9C83F89329A937B900136C08 /* TextViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C83F89229A937B900136C08 /* TextViewWrapper.swift */; };
|
||||
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
|
||||
BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759892ABCCDE30018D73B /* ImageResizer.swift */; };
|
||||
BA4AB0AE2A63B9270070A32A /* AddEmojiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */; };
|
||||
BA4AB0B02A63B94D0070A32A /* EmojiListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */; };
|
||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
||||
@@ -1094,6 +1095,7 @@
|
||||
9609F057296E220800069BF3 /* BannerImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImageView.swift; sourceTree = "<group>"; };
|
||||
9C83F89229A937B900136C08 /* TextViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewWrapper.swift; sourceTree = "<group>"; };
|
||||
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachMediaUtility.swift; sourceTree = "<group>"; };
|
||||
BA3759892ABCCDE30018D73B /* ImageResizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageResizer.swift; sourceTree = "<group>"; };
|
||||
BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEmojiView.swift; sourceTree = "<group>"; };
|
||||
BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiListItemView.swift; sourceTree = "<group>"; };
|
||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
||||
@@ -1239,6 +1241,7 @@
|
||||
4C0A3F8D280F63FF000448DE /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BA3759882ABCCDE30018D73B /* Camera */,
|
||||
4C190F1E2A535FC200027FD5 /* Zaps */,
|
||||
4C54AA0829A55416003E4487 /* Notifications */,
|
||||
3AA247FC297E3CFF0090C62D /* RepostsModel.swift */,
|
||||
@@ -2251,6 +2254,14 @@
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BA3759882ABCCDE30018D73B /* Camera */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BA3759892ABCCDE30018D73B /* ImageResizer.swift */,
|
||||
);
|
||||
path = Camera;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F71694E82A66221E001F4053 /* Onboarding */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2794,6 +2805,7 @@
|
||||
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
||||
4C32B9502A9AD44700DC3548 /* FlatBufferBuilder.swift in Sources */,
|
||||
50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */,
|
||||
BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */,
|
||||
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
||||
4C32B9512A9AD44700DC3548 /* FlatbuffersErrors.swift in Sources */,
|
||||
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
||||
@@ -3175,7 +3187,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 18;
|
||||
CURRENT_PROJECT_VERSION = 19;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -3224,7 +3236,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 18;
|
||||
CURRENT_PROJECT_VERSION = 19;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
||||
@@ -7,30 +7,49 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum StatusDuration: String, CaseIterable {
|
||||
case never = "Never"
|
||||
case thirty_mins = "30 Minutes"
|
||||
case hour = "1 Hour"
|
||||
case four_hours = "4 Hours"
|
||||
case day = "1 Day"
|
||||
case week = "1 Week"
|
||||
enum StatusDuration: CustomStringConvertible, CaseIterable {
|
||||
case never
|
||||
case thirty_mins
|
||||
case hour
|
||||
case four_hours
|
||||
case day
|
||||
case week
|
||||
|
||||
var expiration: Date? {
|
||||
var timeInterval: TimeInterval? {
|
||||
switch self {
|
||||
case .never:
|
||||
return nil
|
||||
case .thirty_mins:
|
||||
return Date.now.addingTimeInterval(60 * 30)
|
||||
return 60 * 30
|
||||
case .hour:
|
||||
return Date.now.addingTimeInterval(60 * 60)
|
||||
return 60 * 60
|
||||
case .four_hours:
|
||||
return Date.now.addingTimeInterval(60 * 60 * 4)
|
||||
return 60 * 60 * 4
|
||||
case .day:
|
||||
return Date.now.addingTimeInterval(60 * 60 * 24)
|
||||
return 60 * 60 * 24
|
||||
case .week:
|
||||
return Date.now.addingTimeInterval(60 * 60 * 24 * 7)
|
||||
return 60 * 60 * 24 * 7
|
||||
}
|
||||
}
|
||||
|
||||
var expiration: Date? {
|
||||
guard let timeInterval else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Date.now.addingTimeInterval(timeInterval)
|
||||
}
|
||||
|
||||
var description: String {
|
||||
guard let timeInterval else {
|
||||
return NSLocalizedString("Never", comment: "Profile status duration setting of never expiring.")
|
||||
}
|
||||
|
||||
let formatter = DateComponentsFormatter()
|
||||
formatter.unitsStyle = .full
|
||||
formatter.allowedUnits = [.minute, .hour, .day, .weekOfMonth]
|
||||
return formatter.string(from: timeInterval) ?? "\(timeInterval) seconds"
|
||||
}
|
||||
}
|
||||
|
||||
struct UserStatusSheet: View {
|
||||
@@ -68,43 +87,43 @@ struct UserStatusSheet: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
Text("Set Status")
|
||||
Text("Set Status", comment: "Title of view that allows the user to set their profile status (e.g. working, studying, coding)")
|
||||
.font(.largeTitle)
|
||||
|
||||
TextField(text: status_binding, label: {
|
||||
Text("📋 Working")
|
||||
Text("📋 Working", comment: "Placeholder as an example of what the user could set as their profile status.")
|
||||
})
|
||||
|
||||
HStack {
|
||||
Image("link")
|
||||
|
||||
TextField(text: url_binding, label: {
|
||||
Text("https://example.com")
|
||||
Text("https://example.com", comment: "Placeholder as an example of what the user could set so that the link is opened when the status is tapped.")
|
||||
})
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Clear status")
|
||||
Text("Clear status", comment: "Label to prompt user to select an expiration time for the profile status to clear.")
|
||||
|
||||
Spacer()
|
||||
|
||||
Picker("Duration", selection: $duration) {
|
||||
Picker(NSLocalizedString("Duration", comment: "Label for profile status expiration duration picker."), selection: $duration) {
|
||||
ForEach(StatusDuration.allCases, id: \.self) { d in
|
||||
Text("\(d.rawValue)")
|
||||
Text(verbatim: d.description)
|
||||
.tag(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Toggle(isOn: $status.playing_enabled, label: {
|
||||
Text("Broadcast music playing on Apple Music")
|
||||
Text("Broadcast music playing on Apple Music", comment: "Toggle to enable or disable broadcasting what music is being played on Apple Music in their profile status.")
|
||||
})
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Button(action: {
|
||||
dismiss()
|
||||
}, label: {
|
||||
Text("Cancel")
|
||||
Text("Cancel", comment: "Cancel button text for dismissing profile status settings view.")
|
||||
})
|
||||
|
||||
Spacer()
|
||||
@@ -121,7 +140,7 @@ struct UserStatusSheet: View {
|
||||
|
||||
dismiss()
|
||||
}, label: {
|
||||
Text("Save")
|
||||
Text("Save", comment: "Save button text for saving profile status settings.")
|
||||
})
|
||||
.buttonStyle(GradientButtonStyle())
|
||||
}
|
||||
|
||||
40
damus/Models/Camera/ImageResizer.swift
Normal file
40
damus/Models/Camera/ImageResizer.swift
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// ImageResizer.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Suhail Saqan on 8/5/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public enum ImageResizingError: Error {
|
||||
case cannotRetrieveFromURL
|
||||
case cannotRetrieveFromData
|
||||
}
|
||||
|
||||
public struct ImageResizer {
|
||||
public var targetWidth: CGFloat
|
||||
|
||||
public init(targetWidth: CGFloat) {
|
||||
self.targetWidth = targetWidth
|
||||
}
|
||||
|
||||
public func resize(at url: URL) -> UIImage? {
|
||||
guard let image = UIImage(contentsOfFile: url.path) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.resize(image: image)
|
||||
}
|
||||
|
||||
public func resize(image: UIImage) -> UIImage {
|
||||
let originalSize = image.size
|
||||
let targetSize = CGSize(width: targetWidth, height: targetWidth*originalSize.height/originalSize.width)
|
||||
let renderer = UIGraphicsImageRenderer(size: targetSize)
|
||||
return renderer.image { (context) in
|
||||
image.draw(in: CGRect(origin: .zero, size: targetSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,16 +28,10 @@ enum LibreTranslateServer: String, CaseIterable, Identifiable, StringCodable {
|
||||
self = libreTranslateServer
|
||||
}
|
||||
|
||||
case argosopentech
|
||||
case terraprint
|
||||
case custom
|
||||
|
||||
var model: Model {
|
||||
switch self {
|
||||
case .argosopentech:
|
||||
return .init(tag: self.rawValue, displayName: "translate.argosopentech.com", url: "https://translate.argosopentech.com")
|
||||
case .terraprint:
|
||||
return .init(tag: self.rawValue, displayName: "translate.terraprint.co", url: "https://translate.terraprint.co")
|
||||
case .custom:
|
||||
return .init(tag: self.rawValue, displayName: NSLocalizedString("Custom", comment: "Dropdown option for selecting a custom translation server."), url: nil)
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ class UserSettingsStore: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
@StringSetting(key: "libretranslate_server", default_value: .terraprint)
|
||||
@StringSetting(key: "libretranslate_server", default_value: .custom)
|
||||
var libretranslate_server: LibreTranslateServer
|
||||
|
||||
@Setting(key: "libretranslate_url", default_value: "")
|
||||
|
||||
@@ -212,9 +212,19 @@ class RelayPool {
|
||||
print("queueing request for \(relay)")
|
||||
request_queue.append(QueuedRequest(req: r, relay: relay, skip_ephemeral: skip_ephemeral))
|
||||
}
|
||||
|
||||
|
||||
func send_raw(_ req: NostrRequestType, to: [String]? = nil, skip_ephemeral: Bool = true) {
|
||||
let relays = to.map{ get_relays($0) } ?? self.relays
|
||||
|
||||
// send to local relay (nostrdb)
|
||||
switch req {
|
||||
case .typical(let r):
|
||||
if let rstr = make_nostr_req(r) {
|
||||
ndb.process_client_event(rstr)
|
||||
}
|
||||
case .custom(let string):
|
||||
ndb.process_client_event(string)
|
||||
}
|
||||
|
||||
for relay in relays {
|
||||
if req.is_read && !(relay.descriptor.info.read ?? true) {
|
||||
|
||||
@@ -22,7 +22,7 @@ struct GradientFollowButton: View {
|
||||
Button(action: {
|
||||
follow_state = perform_follow_btn_action(follow_state, target: target)
|
||||
}) {
|
||||
Text("\(follow_btn_txt(follow_state, follows_you: follows_you))")
|
||||
Text(follow_btn_txt(follow_state, follows_you: follows_you))
|
||||
.foregroundColor(follow_state == .unfollows ? .white : grayTextColor)
|
||||
.font(.callout)
|
||||
.fontWeight(.medium)
|
||||
|
||||
@@ -40,7 +40,7 @@ struct FollowHashtagView: View {
|
||||
HStack {
|
||||
SingleCharacterAvatar(character: "#")
|
||||
|
||||
Text("#\(hashtag.hashtag)")
|
||||
Text(verbatim: "#\(hashtag.hashtag)")
|
||||
.bold()
|
||||
}
|
||||
.onTapGesture {
|
||||
|
||||
@@ -476,6 +476,7 @@ struct ProfileView: View {
|
||||
customNavbar
|
||||
}
|
||||
}
|
||||
.toolbarBackground(.hidden)
|
||||
.onReceive(handle_notify(.switched_timeline)) { _ in
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ struct RelayConfigView: View {
|
||||
|
||||
if recommended.count > 0 {
|
||||
VStack {
|
||||
Text("Recommended relays")
|
||||
Text("Recommended relays", comment: "Title for view of recommended relays.")
|
||||
.foregroundStyle(DamusLightGradient.gradient)
|
||||
.padding(10)
|
||||
.background {
|
||||
|
||||
@@ -13,7 +13,7 @@ struct FailedRelayImage: View {
|
||||
|
||||
var body: some View {
|
||||
let abbrv = String(url?.host()?.first?.uppercased() ?? "R")
|
||||
Text("\(abbrv)")
|
||||
Text(abbrv)
|
||||
.font(.system(size: 40, weight: .bold))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ struct RelayStatusView: View {
|
||||
var body: some View {
|
||||
Group {
|
||||
if connection.isConnecting {
|
||||
Text("Connecting")
|
||||
Text("Connecting", comment: "Relay status label that indicates a relay is connecting.")
|
||||
.font(.caption)
|
||||
.frame(height: 20)
|
||||
.padding(.horizontal, 10)
|
||||
@@ -25,7 +25,7 @@ struct RelayStatusView: View {
|
||||
.stroke(DamusColors.warningBorder, lineWidth: 1)
|
||||
)
|
||||
} else if connection.isConnected {
|
||||
Text("Online")
|
||||
Text("Online", comment: "Relay status label that indicates a relay is connected.")
|
||||
.font(.caption)
|
||||
.frame(height: 20)
|
||||
.padding(.horizontal, 10)
|
||||
@@ -37,7 +37,7 @@ struct RelayStatusView: View {
|
||||
.stroke(DamusColors.successBorder, lineWidth: 1)
|
||||
)
|
||||
} else {
|
||||
Text("Error")
|
||||
Text("Error", comment: "Relay status label that indicates a relay had an error when connecting")
|
||||
.font(.caption)
|
||||
.frame(height: 20)
|
||||
.padding(.horizontal, 10)
|
||||
|
||||
@@ -24,10 +24,8 @@ struct AppearanceSettingsView: View {
|
||||
|
||||
var FontSize: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Slider(value: $settings.font_size, in: 0.5...2.0, step: 0.1) {
|
||||
Text("Font Size")
|
||||
}
|
||||
.padding()
|
||||
Slider(value: $settings.font_size, in: 0.5...2.0, step: 0.1)
|
||||
.padding()
|
||||
|
||||
// Sample text to show how the font size would look
|
||||
ResizedEventPreview(damus_state: damus_state, settings: settings)
|
||||
@@ -37,7 +35,7 @@ struct AppearanceSettingsView: View {
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Font Size") {
|
||||
Section(NSLocalizedString("Font Size", comment: "Section label for font size settings.")) {
|
||||
FontSize
|
||||
}
|
||||
|
||||
@@ -49,7 +47,7 @@ struct AppearanceSettingsView: View {
|
||||
.toggleStyle(.switch)
|
||||
}
|
||||
|
||||
Section(header: Text("User Statuses")) {
|
||||
Section(header: Text("User Statuses", comment: "Section header for user profile status settings.")) {
|
||||
Toggle(NSLocalizedString("Show general statuses", comment: "Settings toggle for enabling general user statuses"), isOn: $settings.show_general_statuses)
|
||||
.toggleStyle(.switch)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||
<file original="damus/en-US.lproj/InfoPlist.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.3.1" build-num="14E300c"/>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.0" build-num="15A240d"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
||||
@@ -15,6 +15,11 @@
|
||||
<target>damus</target>
|
||||
<note>Bundle name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSAppleMusicUsageDescription" xml:space="preserve">
|
||||
<source>Damus needs access to your media library for playback statuses</source>
|
||||
<target>Damus needs access to your media library for playback statuses</target>
|
||||
<note>Privacy - Media Library Usage Description</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSCameraUsageDescription" xml:space="preserve">
|
||||
<source>Damus needs access to your camera if you want to upload photos from it</source>
|
||||
<target>Damus needs access to your camera if you want to upload photos from it</target>
|
||||
@@ -39,15 +44,15 @@
|
||||
</file>
|
||||
<file original="damus/en-US.lproj/Localizable.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.3.1" build-num="14E300c"/>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.0" build-num="15A240d"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="%@ %@" xml:space="preserve">
|
||||
<source>%@ %@</source>
|
||||
<target>%@ %@</target>
|
||||
<note>Sentence composed of 2 variables to describe how many imports were performed from loading a NostrScript. In source English, the first variable is the number of imports, and the second variable is 'Import' or 'Imports'.
|
||||
Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.
|
||||
Sentence composed of 2 variables to describe how many people are following a user. In source English, the first variable is the number of followers, and the second variable is 'Follower' or 'Followers'.</note>
|
||||
<note>Sentence composed of 2 variables to describe how many people are following a user. In source English, the first variable is the number of followers, and the second variable is 'Follower' or 'Followers'.
|
||||
Sentence composed of 2 variables to describe how many imports were performed from loading a NostrScript. In source English, the first variable is the number of imports, and the second variable is 'Import' or 'Imports'.
|
||||
Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ has been muted" xml:space="preserve">
|
||||
<source>%@ has been muted</source>
|
||||
@@ -137,13 +142,19 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<trans-unit id="Add" xml:space="preserve">
|
||||
<source>Add</source>
|
||||
<target>Add</target>
|
||||
<note>Button to confirm adding user inputted relay.</note>
|
||||
<note>Button to add relay server to list.
|
||||
Button to confirm adding user inputted emoji.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add Bookmark" xml:space="preserve">
|
||||
<source>Add Bookmark</source>
|
||||
<target>Add Bookmark</target>
|
||||
<note>Button text to add bookmark to a note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add Emoji" xml:space="preserve">
|
||||
<source>Add Emoji</source>
|
||||
<target>Add Emoji</target>
|
||||
<note>Label for section for adding an emoji to the reactions list.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add all" xml:space="preserve">
|
||||
<source>Add all</source>
|
||||
<target>Add all</target>
|
||||
@@ -154,6 +165,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Add bookmark</target>
|
||||
<note>Context menu option for adding a note bookmark.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add relay" xml:space="preserve">
|
||||
<source>Add relay</source>
|
||||
<target>Add relay</target>
|
||||
<note>Title text to indicate user to an add a relay.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Additional information" xml:space="preserve">
|
||||
<source>Additional information</source>
|
||||
<target>Additional information</target>
|
||||
@@ -206,6 +222,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<note>Navigation title for text and appearance settings.
|
||||
Section header for text and appearance settings</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Appearance and filters" xml:space="preserve">
|
||||
<source>Appearance and filters</source>
|
||||
<target>Appearance and filters</target>
|
||||
<note>Section header for text, appearance, and content filter settings</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Are you lost?" xml:space="preserve">
|
||||
<source>Are you lost?</source>
|
||||
<target>Are you lost?</target>
|
||||
@@ -283,6 +304,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<note>Button to broadcast note to all your relays
|
||||
Context menu option for broadcasting the user's note to all of the user's connected relay servers.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Broadcast music playing on Apple Music" xml:space="preserve">
|
||||
<source>Broadcast music playing on Apple Music</source>
|
||||
<target>Broadcast music playing on Apple Music</target>
|
||||
<note>Toggle to enable or disable broadcasting what music is being played on Apple Music in their profile status.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<target>Cancel</target>
|
||||
@@ -290,7 +316,7 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
Button to cancel a repost.
|
||||
Button to cancel out of alert that creates a new mutelist.
|
||||
Button to cancel out of posting a note.
|
||||
Button to cancel out of view adding user inputted relay.
|
||||
Button to cancel out of view adding user inputted emoji.
|
||||
Button to cancel the upload.
|
||||
Cancel deleting bookmarks.
|
||||
Cancel deleting the user.
|
||||
@@ -312,30 +338,36 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Clear Cache</target>
|
||||
<note>Button to clear image cache.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect" xml:space="preserve">
|
||||
<source>Connect</source>
|
||||
<target>Connect</target>
|
||||
<note>Button to connect to recommended relay server.</note>
|
||||
<trans-unit id="Clear status" xml:space="preserve">
|
||||
<source>Clear status</source>
|
||||
<target>Clear status</target>
|
||||
<note>Label to prompt user to select an expiration time for the profile status to clear.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect To Relay" xml:space="preserve">
|
||||
<source>Connect To Relay</source>
|
||||
<target>Connect To Relay</target>
|
||||
<note>Label for section for adding a relay server.</note>
|
||||
<note>Button to connect to the relay.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected Relays" xml:space="preserve">
|
||||
<source>Connected Relays</source>
|
||||
<target>Connected Relays</target>
|
||||
<note>Section title for relay servers that are connected.</note>
|
||||
<trans-unit id="Connecting" xml:space="preserve">
|
||||
<source>Connecting</source>
|
||||
<target>Connecting</target>
|
||||
<note>Relay status label that indicates a relay is connecting.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact" xml:space="preserve">
|
||||
<source>Contact</source>
|
||||
<target>Contact</target>
|
||||
<note>Label to display relay contact information.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Content filters" xml:space="preserve">
|
||||
<source>Content filters</source>
|
||||
<target>Content filters</target>
|
||||
<note>Section title for content filtering/moderation configuration.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Continue" xml:space="preserve">
|
||||
<source>Continue</source>
|
||||
<target>Continue</target>
|
||||
<note>Continue with bookmarks.
|
||||
<note>Button to dismiss suggested users view and continue to the main app
|
||||
Continue with bookmarks.
|
||||
Continue with deleting the user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copied" xml:space="preserve">
|
||||
@@ -346,7 +378,9 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<target>Copy</target>
|
||||
<note>Button to copy a relay server address.</note>
|
||||
<note>Button to copy a relay server address.
|
||||
Button to copy an emoji reaction
|
||||
Context menu option for copying the version of damus.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Account ID" xml:space="preserve">
|
||||
<source>Copy Account ID</source>
|
||||
@@ -521,7 +555,17 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<trans-unit id="Done" xml:space="preserve">
|
||||
<source>Done</source>
|
||||
<target>Done</target>
|
||||
<note>Button that, when tapped, will finish adding a different user's relays to your relay by hiding the + buttons next to the relays.</note>
|
||||
<note>Button to dismiss wallet selection view for paying Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Duplicate relay" xml:space="preserve">
|
||||
<source>Duplicate relay</source>
|
||||
<target>Duplicate relay</target>
|
||||
<note>Title of the duplicate relay error message.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Duration" xml:space="preserve">
|
||||
<source>Duration</source>
|
||||
<target>Duration</target>
|
||||
<note>Label for profile status expiration duration picker.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="EULA" xml:space="preserve">
|
||||
<source>EULA</source>
|
||||
@@ -538,6 +582,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Edit</target>
|
||||
<note>Button to edit user's profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Emoji Reactions" xml:space="preserve">
|
||||
<source>Emoji Reactions</source>
|
||||
<target>Emoji Reactions</target>
|
||||
<note>Section title for emoji reactions that are currently added.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted" xml:space="preserve">
|
||||
<source>Encrypted</source>
|
||||
<target>Encrypted</target>
|
||||
@@ -548,6 +597,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Enter your account key</target>
|
||||
<note>Prompt for user to enter an account key to login.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error" xml:space="preserve">
|
||||
<source>Error</source>
|
||||
<target>Error</target>
|
||||
<note>Relay status label that indicates a relay had an error when connecting</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error fetching lightning invoice" xml:space="preserve">
|
||||
<source>Error fetching lightning invoice</source>
|
||||
<target>Error fetching lightning invoice</target>
|
||||
@@ -588,6 +642,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Follow</target>
|
||||
<note>Button to follow a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Follow All" xml:space="preserve">
|
||||
<source>Follow All</source>
|
||||
<target>Follow All</target>
|
||||
<note>Button to follow all users in this section</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Follow Back" xml:space="preserve">
|
||||
<source>Follow Back</source>
|
||||
<target>Follow Back</target>
|
||||
@@ -643,6 +702,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Follows you</target>
|
||||
<note>Text to indicate that a user is following your profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Font Size" xml:space="preserve">
|
||||
<source>Font Size</source>
|
||||
<target>Font Size</target>
|
||||
<note>Section label for font size settings.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Free" xml:space="preserve">
|
||||
<source>Free</source>
|
||||
<target>Free</target>
|
||||
@@ -658,6 +722,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Get API Key with BTC/Lightning</target>
|
||||
<note>Button to navigate to nokyctranslate website to get a translation API key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Hashtags" xml:space="preserve">
|
||||
<source>Hashtags</source>
|
||||
<target>Hashtags</target>
|
||||
<note>Label for filter for seeing only hashtag follows.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Help build the future of decentralized communication on the web." xml:space="preserve">
|
||||
<source>Help build the future of decentralized communication on the web.</source>
|
||||
<target>Help build the future of decentralized communication on the web.</target>
|
||||
@@ -673,6 +742,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Hide all 🤙's</target>
|
||||
<note>Section footer describing OnlyZaps mode</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Hide notes with #nsfw tags" xml:space="preserve">
|
||||
<source>Hide notes with #nsfw tags</source>
|
||||
<target>Hide notes with #nsfw tags</target>
|
||||
<note>Setting to hide notes with the #nsfw (not safe for work) tags</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Home" xml:space="preserve">
|
||||
<source>Home</source>
|
||||
<target>Home</target>
|
||||
@@ -861,6 +935,16 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>Muted Users</target>
|
||||
<note>Navigation title of view to see list of muted users.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="My Relays" xml:space="preserve">
|
||||
<source>My Relays</source>
|
||||
<target>My Relays</target>
|
||||
<note>Section title for relay servers that the user is connected to.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Never" xml:space="preserve">
|
||||
<source>Never</source>
|
||||
<target>Never</target>
|
||||
<note>Profile status duration setting of never expiring.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New encrypted direct message" xml:space="preserve">
|
||||
<source>New encrypted direct message</source>
|
||||
<target>New encrypted direct message</target>
|
||||
@@ -881,6 +965,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<target>No data available</target>
|
||||
<note>Text indicating that there is no data available to show for specific metadata about a relay server.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No logs to display" xml:space="preserve">
|
||||
<source>No logs to display</source>
|
||||
<target>No logs to display</target>
|
||||
<note>Label to indicate that there are no developer mode logs available to be displayed on the screen</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No mute list found, create a new one? This will overwrite any previous mute lists." xml:space="preserve">
|
||||
<source>No mute list found, create a new one? This will overwrite any previous mute lists.</source>
|
||||
<target>No mute list found, create a new one? This will overwrite any previous mute lists.</target>
|
||||
@@ -939,14 +1028,19 @@ Sentence composed of 2 variables to describe how many people are following a use
|
||||
<trans-unit id="Notes" xml:space="preserve">
|
||||
<source>Notes</source>
|
||||
<target>Notes</target>
|
||||
<note>Label for filter for seeing only notes (instead of notes and replies).
|
||||
Label for filter for seeing only your notes (instead of notes and replies).</note>
|
||||
<note>Label for filter for seeing only your notes (instead of notes and replies).
|
||||
Label for filter for seeing only notes (instead of notes and replies).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notes & Replies" xml:space="preserve">
|
||||
<source>Notes & Replies</source>
|
||||
<target>Notes & Replies</target>
|
||||
<note>Label for filter for seeing notes and replies (instead of only notes).
|
||||
Label for filter for seeing your notes and replies (instead of only your notes).</note>
|
||||
<note>Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
Label for filter for seeing notes and replies (instead of only notes).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content" xml:space="preserve">
|
||||
<source>Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content</source>
|
||||
<target>Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content</target>
|
||||
<note>Section footer clarifying what #nsfw (not safe for work) tags mean</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
|
||||
<source>Nothing to see here. Check back later!</source>
|
||||
@@ -979,6 +1073,11 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>Ok</target>
|
||||
<note>Button to dismiss the alert.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Online" xml:space="preserve">
|
||||
<source>Online</source>
|
||||
<target>Online</target>
|
||||
<note>Relay status label that indicates a relay is connected.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Only you can see this message and who sent it." xml:space="preserve">
|
||||
<source>Only you can see this message and who sent it.</source>
|
||||
<target>Only you can see this message and who sent it.</target>
|
||||
@@ -1024,6 +1123,11 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>Pay the Lightning invoice</target>
|
||||
<note>Navigation bar title for view to pay Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People" xml:space="preserve">
|
||||
<source>People</source>
|
||||
<target>People</target>
|
||||
<note>Label for filter for seeing only people follows.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Permanently Delete Account" xml:space="preserve">
|
||||
<source>Permanently Delete Account</source>
|
||||
<target>Permanently Delete Account</target>
|
||||
@@ -1118,12 +1222,19 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<trans-unit id="Reactions" xml:space="preserve">
|
||||
<source>Reactions</source>
|
||||
<target>Reactions</target>
|
||||
<note>Navigation bar title for Reactions view.</note>
|
||||
<note>Navigation bar title for Reactions view.
|
||||
Section header for reactions settings
|
||||
Title of emoji reactions view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recommended Relays" xml:space="preserve">
|
||||
<source>Recommended Relays</source>
|
||||
<target>Recommended Relays</target>
|
||||
<note>Section title for recommend relay servers that could be added as part of configuration</note>
|
||||
<trans-unit id="Recommended Emojis" xml:space="preserve">
|
||||
<source>Recommended Emojis</source>
|
||||
<target>Recommended Emojis</target>
|
||||
<note>Section title for recommend emojis</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recommended relays" xml:space="preserve">
|
||||
<source>Recommended relays</source>
|
||||
<target>Recommended relays</target>
|
||||
<note>Title for view of recommended relays.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject" xml:space="preserve">
|
||||
<source>Reject</source>
|
||||
@@ -1316,6 +1427,11 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>Select a Lightning wallet</target>
|
||||
<note>Title of section for selecting a Lightning wallet to pay a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select default emoji" xml:space="preserve">
|
||||
<source>Select default emoji</source>
|
||||
<target>Select default emoji</target>
|
||||
<note>Prompt selection of user's default emoji reaction</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select default wallet" xml:space="preserve">
|
||||
<source>Select default wallet</source>
|
||||
<target>Select default wallet</target>
|
||||
@@ -1341,6 +1457,11 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>Service</target>
|
||||
<note>Prompt selection of translation service provider.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Set Status" xml:space="preserve">
|
||||
<source>Set Status</source>
|
||||
<target>Set Status</target>
|
||||
<note>Sidebar menu label to set user status</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Settings" xml:space="preserve">
|
||||
<source>Settings</source>
|
||||
<target>Settings</target>
|
||||
@@ -1370,10 +1491,10 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<note>Button to show a note from a user who has been muted.
|
||||
Toggle to show or hide user's secret account login key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show +" xml:space="preserve">
|
||||
<source>Show +</source>
|
||||
<target>Show +</target>
|
||||
<note>Button that, when tapped, will show + buttons next to a user's relays.</note>
|
||||
<trans-unit id="Show general statuses" xml:space="preserve">
|
||||
<source>Show general statuses</source>
|
||||
<target>Show general statuses</target>
|
||||
<note>Settings toggle for enabling general user statuses</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show less" xml:space="preserve">
|
||||
<source>Show less</source>
|
||||
@@ -1386,6 +1507,11 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<note>Button to show entire note.
|
||||
Button to show more of a long profile description.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show music statuses" xml:space="preserve">
|
||||
<source>Show music statuses</source>
|
||||
<target>Show music statuses</target>
|
||||
<note>Settings toggle for enabling now playing music statuses</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show only from users you follow" xml:space="preserve">
|
||||
<source>Show only from users you follow</source>
|
||||
<target>Show only from users you follow</target>
|
||||
@@ -1416,6 +1542,11 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>Sign out</target>
|
||||
<note>Sidebar menu label to sign out of the account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Skip" xml:space="preserve">
|
||||
<source>Skip</source>
|
||||
<target>Skip</target>
|
||||
<note>Button to dismiss the suggested users screen</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Social media has developed into a key way information flows around the world. Unfortunately, our current social media systems are broken" xml:space="preserve">
|
||||
<source>Social media has developed into a key way information flows around the world. Unfortunately, our current social media systems are broken</source>
|
||||
<target>Social media has developed into a key way information flows around the world. Unfortunately, our current social media systems are broken</target>
|
||||
@@ -1467,6 +1598,13 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>The go-to iOS Nostr client</target>
|
||||
<note>Quick description of what Damus is</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The relay you are trying to add is already added. You're all set!" xml:space="preserve">
|
||||
<source>The relay you are trying to add is already added.
|
||||
You're all set!</source>
|
||||
<target>The relay you are trying to add is already added.
|
||||
You're all set!</target>
|
||||
<note>An error message that appears when the user attempts to add a relay that has already been added.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is a paid relay, you must pay for notes to be accepted." xml:space="preserve">
|
||||
<source>This is a paid relay, you must pay for notes to be accepted.</source>
|
||||
<target>This is a paid relay, you must pay for notes to be accepted.</target>
|
||||
@@ -1487,11 +1625,6 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!</target>
|
||||
<note>Label to describe that a private key is the user's secret account key and what they should do with it.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This relay is already in your list" xml:space="preserve">
|
||||
<source>This relay is already in your list</source>
|
||||
<target>This relay is already in your list</target>
|
||||
<note>An error message that appears when the user attempts to add a relay that has already been added.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Thread" xml:space="preserve">
|
||||
<source>Thread</source>
|
||||
<target>Thread</target>
|
||||
@@ -1593,10 +1726,15 @@ Label for filter for seeing your notes and replies (instead of only your notes).
|
||||
<target>Upload</target>
|
||||
<note>Button to proceed with uploading.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="User Statuses" xml:space="preserve">
|
||||
<source>User Statuses</source>
|
||||
<target>User Statuses</target>
|
||||
<note>Section header for user profile status settings.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="User has been muted" xml:space="preserve">
|
||||
<source>User has been muted</source>
|
||||
<target>User has been muted</target>
|
||||
<note>Alert message that informs a user was d.</note>
|
||||
<note>Alert message that informs a user was muted.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="User muted" xml:space="preserve">
|
||||
<source>User muted</source>
|
||||
@@ -1688,6 +1826,11 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
|
||||
<target>What is Nostr?</target>
|
||||
<note>Heading text for section describing what is Nostr.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Who to Follow" xml:space="preserve">
|
||||
<source>Who to Follow</source>
|
||||
<target>Who to Follow</target>
|
||||
<note>Title for a screen displaying suggestions of who to follow</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Why we need Nostr?" xml:space="preserve">
|
||||
<source>Why we need Nostr?</source>
|
||||
<target>Why we need Nostr?</target>
|
||||
@@ -1768,6 +1911,11 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
|
||||
Setting to enable Zap Local Notification
|
||||
Title for section in zap settings that controls general zap preferences.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="https://example.com" xml:space="preserve">
|
||||
<source>https://example.com</source>
|
||||
<target>https://example.com</target>
|
||||
<note>Placeholder as an example of what the user could set so that the link is opened when the status is tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="https://example.com/pic.jpg" xml:space="preserve">
|
||||
<source>https://example.com/pic.jpg</source>
|
||||
<target>https://example.com/pic.jpg</target>
|
||||
@@ -1923,11 +2071,21 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
|
||||
<target>%@ and %@ zapped you</target>
|
||||
<note>Notification that 2 users zapped the current user's profile</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="⚡" xml:space="preserve">
|
||||
<source>⚡</source>
|
||||
<target>⚡</target>
|
||||
<note>Placeholder example for an emoji reaction</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="📋 Working" xml:space="preserve">
|
||||
<source>📋 Working</source>
|
||||
<target>📋 Working</target>
|
||||
<note>Placeholder as an example of what the user could set as their profile status.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.3.1" build-num="14E300c"/>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.0" build-num="15A240d"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="/followed_by_three_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
/* Privacy - Media Library Usage Description */
|
||||
"NSAppleMusicUsageDescription" = "Damus needs access to your media library for playback statuses";
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "Damus needs access to your camera if you want to upload photos from it";
|
||||
/* Privacy - Face ID Usage Description */
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
"project" : "damus.xcodeproj",
|
||||
"targetLocale" : "en-US",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "14E300c",
|
||||
"toolBuildNumber" : "15A240d",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "14.3.1"
|
||||
"toolVersion" : "15.0"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -175,6 +175,12 @@ class Ndb {
|
||||
func lookup_profile_with_txn<Y>(_ pubkey: Pubkey, txn: NdbTxn<Y>) -> ProfileRecord? {
|
||||
lookup_profile_with_txn_inner(pubkey: pubkey, txn: txn)
|
||||
}
|
||||
|
||||
func process_client_event(_ str: String) -> Bool {
|
||||
return str.withCString { cstr in
|
||||
return ndb_process_client_event(ndb.ndb, cstr, Int32(str.utf8.count)) != 0
|
||||
}
|
||||
}
|
||||
|
||||
func process_event(_ str: String) -> Bool {
|
||||
return str.withCString { cstr in
|
||||
|
||||
@@ -414,7 +414,8 @@ enum ndb_writer_msgtype {
|
||||
|
||||
struct ndb_ingester_event {
|
||||
char *json;
|
||||
int len;
|
||||
unsigned client : 1; // ["EVENT", {...}] messages
|
||||
unsigned len : 31;
|
||||
};
|
||||
|
||||
struct ndb_writer_note {
|
||||
@@ -730,6 +731,41 @@ static int ndb_process_profile_note(struct ndb_note *note,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ndb_ingester_process_note(secp256k1_context *ctx,
|
||||
struct ndb_note *note,
|
||||
size_t note_size,
|
||||
struct ndb_writer_msg *out)
|
||||
{
|
||||
// Verify! If it's an invalid note we don't need to
|
||||
// bothter writing it to the database
|
||||
if (!ndb_note_verify(ctx, note->pubkey, note->id, note->sig)) {
|
||||
ndb_debug("signature verification failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we didn't find anything. let's send it
|
||||
// to the writer thread
|
||||
note = realloc(note, note_size);
|
||||
assert(((uint64_t)note % 4) == 0);
|
||||
|
||||
if (note->kind == 0) {
|
||||
struct ndb_profile_record_builder *b =
|
||||
&out->profile.record;
|
||||
|
||||
ndb_process_profile_note(note, b);
|
||||
|
||||
out->type = NDB_WRITER_PROFILE;
|
||||
out->profile.note.note = note;
|
||||
out->profile.note.note_len = note_size;
|
||||
} else {
|
||||
out->type = NDB_WRITER_NOTE;
|
||||
out->note.note = note;
|
||||
out->note.note_len = note_size;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int ndb_ingester_process_event(secp256k1_context *ctx,
|
||||
struct ndb_ingester *ingester,
|
||||
@@ -739,12 +775,16 @@ static int ndb_ingester_process_event(secp256k1_context *ctx,
|
||||
)
|
||||
{
|
||||
struct ndb_tce tce;
|
||||
struct ndb_fce fce;
|
||||
struct ndb_note *note;
|
||||
struct ndb_ingest_controller controller;
|
||||
struct ndb_id_cb cb;
|
||||
void *buf;
|
||||
int ok;
|
||||
size_t bufsize, note_size;
|
||||
|
||||
ok = 0;
|
||||
|
||||
// we will use this to check if we already have it in the DB during
|
||||
// ID parsing
|
||||
controller.read_txn = read_txn;
|
||||
@@ -762,6 +802,8 @@ static int ndb_ingester_process_event(secp256k1_context *ctx,
|
||||
}
|
||||
|
||||
note_size =
|
||||
ev->client ?
|
||||
ndb_client_event_from_json(ev->json, ev->len, &fce, buf, bufsize, &cb) :
|
||||
ndb_ws_event_from_json(ev->json, ev->len, &tce, buf, bufsize, &cb);
|
||||
|
||||
if (note_size == -42) {
|
||||
@@ -775,54 +817,51 @@ static int ndb_ingester_process_event(secp256k1_context *ctx,
|
||||
|
||||
//ndb_debug("parsed evtype:%d '%.*s'\n", tce.evtype, ev->len, ev->json);
|
||||
|
||||
switch (tce.evtype) {
|
||||
case NDB_TCE_NOTICE: goto cleanup;
|
||||
case NDB_TCE_EOSE: goto cleanup;
|
||||
case NDB_TCE_OK: goto cleanup;
|
||||
case NDB_TCE_EVENT:
|
||||
note = tce.event.note;
|
||||
if (note != buf) {
|
||||
ndb_debug("note buffer not equal to malloc'd buffer\n");
|
||||
goto cleanup;
|
||||
if (ev->client) {
|
||||
switch (fce.evtype) {
|
||||
case NDB_FCE_EVENT:
|
||||
note = fce.event.note;
|
||||
if (note != buf) {
|
||||
ndb_debug("note buffer not equal to malloc'd buffer\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!ndb_ingester_process_note(ctx, note, note_size, out))
|
||||
goto cleanup;
|
||||
else {
|
||||
// we're done with the original json, free it
|
||||
free(ev->json);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (tce.evtype) {
|
||||
case NDB_TCE_NOTICE: goto cleanup;
|
||||
case NDB_TCE_EOSE: goto cleanup;
|
||||
case NDB_TCE_OK: goto cleanup;
|
||||
case NDB_TCE_EVENT:
|
||||
note = tce.event.note;
|
||||
if (note != buf) {
|
||||
ndb_debug("note buffer not equal to malloc'd buffer\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Verify! If it's an invalid note we don't need to
|
||||
// bothter writing it to the database
|
||||
if (!ndb_note_verify(ctx, note->pubkey, note->id, note->sig)) {
|
||||
ndb_debug("signature verification failed\n");
|
||||
goto cleanup;
|
||||
if (!ndb_ingester_process_note(ctx, note, note_size, out))
|
||||
goto cleanup;
|
||||
else {
|
||||
// we're done with the original json, free it
|
||||
free(ev->json);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find anything. let's send it
|
||||
// to the writer thread
|
||||
note = realloc(note, note_size);
|
||||
assert(((uint64_t)note % 4) == 0);
|
||||
|
||||
if (note->kind == 0) {
|
||||
struct ndb_profile_record_builder *b =
|
||||
&out->profile.record;
|
||||
|
||||
ndb_process_profile_note(note, b);
|
||||
|
||||
out->type = NDB_WRITER_PROFILE;
|
||||
out->profile.note.note = note;
|
||||
out->profile.note.note_len = note_size;
|
||||
} else {
|
||||
out->type = NDB_WRITER_NOTE;
|
||||
out->note.note = note;
|
||||
out->note.note_len = note_size;
|
||||
}
|
||||
|
||||
// there's nothing left to do with the original json, so free it
|
||||
free(ev->json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
cleanup:
|
||||
free(ev->json);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static uint64_t ndb_get_last_key(MDB_txn *txn, MDB_dbi db)
|
||||
@@ -1089,7 +1128,7 @@ static void ndb_write_version(struct ndb_lmdb *lmdb, MDB_txn *txn, uint64_t vers
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "writing version %" PRIu64 "\n", version);
|
||||
//fprintf(stderr, "writing version %" PRIu64 "\n", version);
|
||||
}
|
||||
|
||||
static void *ndb_writer_thread(void *data)
|
||||
@@ -1317,13 +1356,14 @@ static int ndb_ingester_destroy(struct ndb_ingester *ingester)
|
||||
}
|
||||
|
||||
static int ndb_ingester_queue_event(struct ndb_ingester *ingester,
|
||||
char *json, int len)
|
||||
char *json, unsigned len, unsigned client)
|
||||
{
|
||||
struct ndb_ingester_msg msg;
|
||||
msg.type = NDB_INGEST_EVENT;
|
||||
|
||||
msg.event.json = json;
|
||||
msg.event.len = len;
|
||||
msg.event.client = client;
|
||||
|
||||
return threadpool_dispatch(&ingester->tp, &msg);
|
||||
}
|
||||
@@ -1431,7 +1471,7 @@ static int ndb_run_migrations(struct ndb *ndb)
|
||||
latest_version = sizeof(MIGRATIONS) / sizeof(MIGRATIONS[0]);
|
||||
|
||||
if ((version = ndb_db_version(ndb)) == -1) {
|
||||
fprintf(stderr, "run_migrations: no version found, assuming new db\n");
|
||||
ndb_debug("run_migrations: no version found, assuming new db\n");
|
||||
version = latest_version;
|
||||
|
||||
// no version found. fresh db?
|
||||
@@ -1442,11 +1482,11 @@ static int ndb_run_migrations(struct ndb *ndb)
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
fprintf(stderr, "ndb: version %" PRIu64 " found\n", version);
|
||||
ndb_debug("ndb: version %" PRIu64 " found\n", version);
|
||||
}
|
||||
|
||||
if (version < latest_version)
|
||||
fprintf(stderr, "nostrdb: migrating v%d -> v%d\n",
|
||||
ndb_debug("nostrdb: migrating v%d -> v%d\n",
|
||||
(int)version, (int)latest_version);
|
||||
|
||||
for (i = version; i < latest_version; i++) {
|
||||
@@ -1516,7 +1556,29 @@ void ndb_destroy(struct ndb *ndb)
|
||||
free(ndb);
|
||||
}
|
||||
|
||||
// Process a nostr event, ie: ["EVENT", "subid", {"content":"..."}...]
|
||||
// Process a nostr event from a client
|
||||
//
|
||||
// ie: ["EVENT", {"content":"..."} ...]
|
||||
//
|
||||
// The client-sent variation of ndb_process_event
|
||||
int ndb_process_client_event(struct ndb *ndb, const char *json, int len)
|
||||
{
|
||||
// Since we need to return as soon as possible, and we're not
|
||||
// making any assumptions about the lifetime of the string, we
|
||||
// definitely need to copy the json here. In the future once we
|
||||
// have our thread that manages a websocket connection, we can
|
||||
// avoid the copy and just use the buffer we get from that
|
||||
// thread.
|
||||
char *json_copy = strdupn(json, len);
|
||||
if (json_copy == NULL)
|
||||
return 0;
|
||||
|
||||
return ndb_ingester_queue_event(&ndb->ingester, json_copy, len, 1);
|
||||
}
|
||||
|
||||
// Process anostr event from a relay,
|
||||
//
|
||||
// ie: ["EVENT", "subid", {"content":"..."}...]
|
||||
//
|
||||
// This function returns as soon as possible, first copying the passed
|
||||
// json and then queueing it up for processing. Worker threads then take
|
||||
@@ -1543,23 +1605,26 @@ int ndb_process_event(struct ndb *ndb, const char *json, int json_len)
|
||||
if (json_copy == NULL)
|
||||
return 0;
|
||||
|
||||
return ndb_ingester_queue_event(&ndb->ingester, json_copy, json_len);
|
||||
return ndb_ingester_queue_event(&ndb->ingester, json_copy, json_len, 0);
|
||||
}
|
||||
|
||||
int ndb_process_events(struct ndb *ndb, const char *ldjson, size_t json_len)
|
||||
|
||||
int _ndb_process_events(struct ndb *ndb, const char *ldjson, size_t json_len, int client)
|
||||
{
|
||||
const char *start, *end, *very_end;
|
||||
start = ldjson;
|
||||
end = start + json_len;
|
||||
very_end = ldjson + json_len;
|
||||
int (* process)(struct ndb *, const char *, int);
|
||||
#if DEBUG
|
||||
int processed = 0;
|
||||
#endif
|
||||
process = client ? ndb_process_client_event : ndb_process_event;
|
||||
|
||||
while ((end = fast_strchr(start, '\n', very_end - start))) {
|
||||
//printf("processing '%.*s'\n", (int)(end-start), start);
|
||||
if (!ndb_process_event(ndb, start, end - start)) {
|
||||
ndb_debug("ndb_process_event failed\n");
|
||||
if (!process(ndb, start, end - start)) {
|
||||
ndb_debug("ndb_process_client_event failed\n");
|
||||
return 0;
|
||||
}
|
||||
start = end + 1;
|
||||
@@ -1573,6 +1638,16 @@ int ndb_process_events(struct ndb *ndb, const char *ldjson, size_t json_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ndb_process_client_events(struct ndb *ndb, const char *ldjson, size_t json_len)
|
||||
{
|
||||
return _ndb_process_events(ndb, ldjson, json_len, 1);
|
||||
}
|
||||
|
||||
int ndb_process_events(struct ndb *ndb, const char *ldjson, size_t json_len)
|
||||
{
|
||||
return _ndb_process_events(ndb, ldjson, json_len, 0);
|
||||
}
|
||||
|
||||
static inline int cursor_push_tag(struct cursor *cur, struct ndb_tag *tag)
|
||||
{
|
||||
return cursor_push_u16(cur, tag->count);
|
||||
@@ -1678,6 +1753,13 @@ static inline int ndb_json_parser_parse(struct ndb_json_parser *p,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int toksize(jsmntok_t *tok)
|
||||
{
|
||||
return tok->end - tok->start;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cursor_push_unescaped_char(struct cursor *cur, char c1, char c2)
|
||||
{
|
||||
switch (c2) {
|
||||
@@ -2074,11 +2156,6 @@ static inline int jsoneq(const char *json, jsmntok_t *tok, int tok_len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int toksize(jsmntok_t *tok)
|
||||
{
|
||||
return tok->end - tok->start;
|
||||
}
|
||||
|
||||
static int ndb_builder_finalize_tag(struct ndb_builder *builder,
|
||||
union ndb_packed_str offset)
|
||||
{
|
||||
@@ -2227,6 +2304,37 @@ static int parse_unsigned_int(const char *start, int len, unsigned int *num)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ndb_client_event_from_json(const char *json, int len, struct ndb_fce *fce,
|
||||
unsigned char *buf, int bufsize, struct ndb_id_cb *cb)
|
||||
{
|
||||
jsmntok_t *tok = NULL;
|
||||
int tok_len, res;
|
||||
struct ndb_json_parser parser;
|
||||
|
||||
ndb_json_parser_init(&parser, json, len, buf, bufsize);
|
||||
|
||||
if ((res = ndb_json_parser_parse(&parser, cb)) < 0)
|
||||
return res;
|
||||
|
||||
if (parser.num_tokens <= 3 || parser.toks[0].type != JSMN_ARRAY)
|
||||
return 0;
|
||||
|
||||
parser.i = 1;
|
||||
tok = &parser.toks[parser.i++];
|
||||
tok_len = toksize(tok);
|
||||
if (tok->type != JSMN_STRING)
|
||||
return 0;
|
||||
|
||||
if (tok_len == 5 && !memcmp("EVENT", json + tok->start, 5)) {
|
||||
fce->evtype = NDB_FCE_EVENT;
|
||||
struct ndb_event *ev = &fce->event;
|
||||
return ndb_parse_json_note(&parser, &ev->note);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce,
|
||||
unsigned char *buf, int bufsize,
|
||||
struct ndb_id_cb *cb)
|
||||
@@ -2239,22 +2347,12 @@ int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce,
|
||||
tce->subid = "";
|
||||
|
||||
ndb_json_parser_init(&parser, json, len, buf, bufsize);
|
||||
|
||||
if ((res = ndb_json_parser_parse(&parser, cb)) < 0)
|
||||
return res;
|
||||
|
||||
if (parser.num_tokens < 3 || parser.toks[0].type != JSMN_ARRAY) {
|
||||
/*
|
||||
tok = &parser.toks[parser.json_parser.toknext-1];
|
||||
ndb_debug("failing at not enough takens (%d) or != JSMN_ARRAY @ '%.*s', '%.*s'\n",
|
||||
parser.num_tokens, 10, json + parser.json_parser.pos,
|
||||
toksize(tok), json + tok->start);
|
||||
tok = &parser.toks[parser.json_parser.toknext-2];
|
||||
ndb_debug("failing at not enough takens (%d) or != JSMN_ARRAY @ '%.*s', '%.*s'\n",
|
||||
parser.num_tokens, 10, json + parser.json_parser.pos,
|
||||
toksize(tok), json + tok->start);
|
||||
*/
|
||||
if (parser.num_tokens < 3 || parser.toks[0].type != JSMN_ARRAY)
|
||||
return 0;
|
||||
}
|
||||
|
||||
parser.i = 1;
|
||||
tok = &parser.toks[parser.i++];
|
||||
|
||||
@@ -44,6 +44,11 @@ struct ndb_txn {
|
||||
void *mdb_txn;
|
||||
};
|
||||
|
||||
// From-client event types
|
||||
enum fce_type {
|
||||
NDB_FCE_EVENT = 0x1
|
||||
};
|
||||
|
||||
// To-client event types
|
||||
enum tce_type {
|
||||
NDB_TCE_EVENT = 0x1,
|
||||
@@ -80,6 +85,14 @@ struct ndb_command_result {
|
||||
};
|
||||
|
||||
|
||||
// From-client event
|
||||
struct ndb_fce {
|
||||
enum fce_type evtype;
|
||||
union {
|
||||
struct ndb_event event;
|
||||
};
|
||||
};
|
||||
|
||||
// To-client event
|
||||
struct ndb_tce {
|
||||
enum tce_type evtype;
|
||||
@@ -177,6 +190,8 @@ int ndb_init(struct ndb **ndb, const char *dbdir, size_t mapsize, int ingester_t
|
||||
int ndb_db_version(struct ndb *ndb);
|
||||
int ndb_process_event(struct ndb *, const char *json, int len);
|
||||
int ndb_process_events(struct ndb *, const char *ldjson, size_t len);
|
||||
int ndb_process_client_event(struct ndb *, const char *json, int len);
|
||||
int ndb_process_client_events(struct ndb *, const char *json, size_t len);
|
||||
int ndb_begin_query(struct ndb *, struct ndb_txn *);
|
||||
int ndb_search_profile(struct ndb_txn *txn, struct ndb_search *search, const char *query);
|
||||
int ndb_search_profile_next(struct ndb_search *search);
|
||||
@@ -192,6 +207,7 @@ void ndb_destroy(struct ndb *);
|
||||
|
||||
// BUILDER
|
||||
int ndb_parse_json_note(struct ndb_json_parser *, struct ndb_note **);
|
||||
int ndb_client_event_from_json(const char *json, int len, struct ndb_fce *fce, unsigned char *buf, int bufsize, struct ndb_id_cb *cb);
|
||||
int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce, unsigned char *buf, int bufsize, struct ndb_id_cb *);
|
||||
int ndb_note_from_json(const char *json, int len, struct ndb_note **, unsigned char *buf, int buflen);
|
||||
int ndb_builder_init(struct ndb_builder *builder, unsigned char *buf, int bufsize);
|
||||
|
||||
Reference in New Issue
Block a user