Compare commits
1 Commits
nip56-conf
...
tyiu/disab
| Author | SHA1 | Date | |
|---|---|---|---|
|
16f6bbb5be
|
25
README.md
25
README.md
@@ -94,32 +94,9 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
|||||||
|
|
||||||
Contributors welcome! Start by examining known issues: https://github.com/damus-io/damus/issues.
|
Contributors welcome! Start by examining known issues: https://github.com/damus-io/damus/issues.
|
||||||
|
|
||||||
### Mailing lists
|
|
||||||
|
|
||||||
We have a few mailing lists that anyone can join to get involved in damus development:
|
|
||||||
|
|
||||||
- [dev][dev-list] - development discussions
|
|
||||||
- [patches][patches-list] - code submission and review
|
|
||||||
- [product][product-list] - product discussions
|
|
||||||
- [design][design-list] - design discussions
|
|
||||||
|
|
||||||
[dev-list]: https://damus.io/list/dev
|
|
||||||
[patches-list]: https://damus.io/list/patches
|
|
||||||
[product-list]: https://damus.io/list/product
|
|
||||||
[design-list]: https://damus.io/list/design
|
|
||||||
|
|
||||||
### Code
|
### Code
|
||||||
|
|
||||||
[Email patches][git-send-email] to patches@damus.io are preferred, but I accept PRs on GitHub as well. Patches sent via email may include a bolt11 lightning invoice, choosing the price you think the patch is worth, and I will pay it once the patch is accepted and if I think the price isn't unreasonable. You can also send an any-amount invoice and I will pay what I think it's worth if you prefer not to choose. You can include the bolt11 in the commit body or email so that it can be paid once it is applied.
|
[Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on GitHub as well.
|
||||||
|
|
||||||
Recommended settings when submitting code via email:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git config sendemail.to "patches@damus.io"
|
|
||||||
$ git config format.subjectPrefix "PATCH damus"
|
|
||||||
$ git config --global sendemail.annotate yes
|
|
||||||
$ git config format.signOff yes
|
|
||||||
```
|
|
||||||
|
|
||||||
[git-send-email]: http://git-send-email.io
|
[git-send-email]: http://git-send-email.io
|
||||||
|
|
||||||
|
|||||||
@@ -304,7 +304,6 @@
|
|||||||
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
|
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
||||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
||||||
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2277EE92A089BD5006C3807 /* Router.swift */; };
|
|
||||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
||||||
E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */; };
|
E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */; };
|
||||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
||||||
@@ -754,7 +753,6 @@
|
|||||||
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachMediaUtility.swift; sourceTree = "<group>"; };
|
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachMediaUtility.swift; sourceTree = "<group>"; };
|
||||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.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>"; };
|
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
||||||
D2277EE92A089BD5006C3807 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
|
||||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
||||||
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = "<group>"; };
|
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = "<group>"; };
|
||||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
||||||
@@ -1157,7 +1155,6 @@
|
|||||||
50B5685229F97CB400A23243 /* CredentialHandler.swift */,
|
50B5685229F97CB400A23243 /* CredentialHandler.swift */,
|
||||||
4C7D09582A05BEAD00943473 /* KeyboardVisible.swift */,
|
4C7D09582A05BEAD00943473 /* KeyboardVisible.swift */,
|
||||||
3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */,
|
3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */,
|
||||||
D2277EE92A089BD5006C3807 /* Router.swift */,
|
|
||||||
);
|
);
|
||||||
path = Util;
|
path = Util;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1310,9 +1307,7 @@
|
|||||||
4CE6DEE427F7A08100C66700 /* Products */,
|
4CE6DEE427F7A08100C66700 /* Products */,
|
||||||
4CEE2AE62804F57B00AB5EEF /* Frameworks */,
|
4CEE2AE62804F57B00AB5EEF /* Frameworks */,
|
||||||
);
|
);
|
||||||
indentWidth = 4;
|
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
tabWidth = 4;
|
|
||||||
};
|
};
|
||||||
4CE6DEE427F7A08100C66700 /* Products */ = {
|
4CE6DEE427F7A08100C66700 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
@@ -1866,7 +1861,6 @@
|
|||||||
5C6E1DAD2A193EC2008FC15A /* GradientButtonStyle.swift in Sources */,
|
5C6E1DAD2A193EC2008FC15A /* GradientButtonStyle.swift in Sources */,
|
||||||
4C8EC52529D1FA6C0085D9A8 /* DamusColors.swift in Sources */,
|
4C8EC52529D1FA6C0085D9A8 /* DamusColors.swift in Sources */,
|
||||||
3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */,
|
3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */,
|
||||||
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */,
|
|
||||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
||||||
4CE1399429F0669900AC6A0B /* BigButton.swift in Sources */,
|
4CE1399429F0669900AC6A0B /* BigButton.swift in Sources */,
|
||||||
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */,
|
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */,
|
||||||
@@ -2242,7 +2236,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 8;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -2260,7 +2254,6 @@
|
|||||||
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -2291,7 +2284,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 8;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -2309,7 +2302,6 @@
|
|||||||
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -2340,7 +2332,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -2360,7 +2352,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
|||||||
@@ -11,10 +11,21 @@ struct UserViewRow: View {
|
|||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
let pubkey: String
|
let pubkey: String
|
||||||
|
|
||||||
|
@State var navigating: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let dest = ProfileView(damus_state: damus_state, pubkey: pubkey)
|
||||||
|
|
||||||
UserView(damus_state: damus_state, pubkey: pubkey)
|
UserView(damus_state: damus_state, pubkey: pubkey)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.background(.clear)
|
.background(
|
||||||
|
NavigationLink(destination: dest, isActive: $navigating) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
navigating = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ struct ZapButton: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var zap_img: String {
|
var zap_img: String {
|
||||||
|
if damus_state.settings.nozaps {
|
||||||
|
return "zap"
|
||||||
|
}
|
||||||
|
|
||||||
switch our_zap {
|
switch our_zap {
|
||||||
case .none:
|
case .none:
|
||||||
return "zap"
|
return "zap"
|
||||||
@@ -49,6 +53,10 @@ struct ZapButton: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var zap_color: Color {
|
var zap_color: Color {
|
||||||
|
if damus_state.settings.nozaps {
|
||||||
|
return Color.gray
|
||||||
|
}
|
||||||
|
|
||||||
if our_zap == nil {
|
if our_zap == nil {
|
||||||
return Color.gray
|
return Color.gray
|
||||||
}
|
}
|
||||||
@@ -106,7 +114,6 @@ struct ZapButton: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
if !damus_state.settings.nozaps || zaps.zap_total > 0 {
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
}, label: {
|
}, label: {
|
||||||
Image(zap_img)
|
Image(zap_img)
|
||||||
@@ -116,9 +123,8 @@ struct ZapButton: View {
|
|||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(width:20, height: 20)
|
.frame(width:20, height: 20)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if zaps.zap_total > 0 {
|
if !damus_state.settings.nozaps && zaps.zap_total > 0 {
|
||||||
Text(verbatim: format_msats_abbrev(zaps.zap_total))
|
Text(verbatim: format_msats_abbrev(zaps.zap_total))
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.foregroundColor(zap_color)
|
.foregroundColor(zap_color)
|
||||||
@@ -126,14 +132,21 @@ struct ZapButton: View {
|
|||||||
}
|
}
|
||||||
.accessibilityLabel(NSLocalizedString("Zap", comment: "Accessibility label for zap button"))
|
.accessibilityLabel(NSLocalizedString("Zap", comment: "Accessibility label for zap button"))
|
||||||
.simultaneousGesture(LongPressGesture().onEnded {_ in
|
.simultaneousGesture(LongPressGesture().onEnded {_ in
|
||||||
guard !damus_state.settings.nozaps else { return }
|
// when we don't have nozaps mode enable, long press shows the zap customizer
|
||||||
|
if !damus_state.settings.nozaps {
|
||||||
present_sheet(.zap(target: target, lnurl: lnurl))
|
present_sheet(.zap(target: target, lnurl: lnurl))
|
||||||
|
}
|
||||||
|
|
||||||
|
// long press does nothing in nozaps mode
|
||||||
})
|
})
|
||||||
.highPriorityGesture(TapGesture().onEnded {
|
.highPriorityGesture(TapGesture().onEnded {
|
||||||
guard !damus_state.settings.nozaps else { return }
|
// when we have appstore mode on, only show the zap customizer as "user zaps"
|
||||||
|
if damus_state.settings.nozaps {
|
||||||
|
present_sheet(.zap(target: target, lnurl: lnurl))
|
||||||
|
} else {
|
||||||
|
// otherwise we restore the original behavior of one-tap zaps
|
||||||
tap()
|
tap()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,14 @@ struct ContentView: View {
|
|||||||
@State var damus_state: DamusState? = nil
|
@State var damus_state: DamusState? = nil
|
||||||
@SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home
|
@SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home
|
||||||
@State var is_deleted_account: Bool = false
|
@State var is_deleted_account: Bool = false
|
||||||
|
@State var active_profile: String? = nil
|
||||||
|
@State var active_search: NostrFilter? = nil
|
||||||
|
@State var active_event: NostrEvent? = nil
|
||||||
|
@State var profile_open: Bool = false
|
||||||
|
@State var thread_open: Bool = false
|
||||||
|
@State var search_open: Bool = false
|
||||||
|
@State var wallet_open: Bool = false
|
||||||
|
@State var active_nwc: WalletConnectURL? = nil
|
||||||
@State var muting: String? = nil
|
@State var muting: String? = nil
|
||||||
@State var confirm_mute: Bool = false
|
@State var confirm_mute: Bool = false
|
||||||
@State var user_muted_confirm: Bool = false
|
@State var user_muted_confirm: Bool = false
|
||||||
@@ -89,7 +97,6 @@ struct ContentView: View {
|
|||||||
@SceneStorage("ContentView.filter_state") var filter_state : FilterState = .posts_and_replies
|
@SceneStorage("ContentView.filter_state") var filter_state : FilterState = .posts_and_replies
|
||||||
@State private var isSideBarOpened = false
|
@State private var isSideBarOpened = false
|
||||||
var home: HomeModel = HomeModel()
|
var home: HomeModel = HomeModel()
|
||||||
@StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator()
|
|
||||||
|
|
||||||
let sub_id = UUID().description
|
let sub_id = UUID().description
|
||||||
|
|
||||||
@@ -121,7 +128,7 @@ struct ContentView: View {
|
|||||||
|
|
||||||
if privkey != nil {
|
if privkey != nil {
|
||||||
PostButtonContainer(is_left_handed: damus_state?.settings.left_handed ?? false) {
|
PostButtonContainer(is_left_handed: damus_state?.settings.left_handed ?? false) {
|
||||||
self.active_sheet = .post(.posting(.none))
|
self.active_sheet = .post(.posting)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +155,10 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func popToRoot() {
|
func popToRoot() {
|
||||||
navigationCoordinator.popToRoot()
|
profile_open = false
|
||||||
|
thread_open = false
|
||||||
|
search_open = false
|
||||||
|
wallet_open = false
|
||||||
isSideBarOpened = false
|
isSideBarOpened = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +169,21 @@ struct ContentView: View {
|
|||||||
|
|
||||||
func MainContent(damus: DamusState) -> some View {
|
func MainContent(damus: DamusState) -> some View {
|
||||||
VStack {
|
VStack {
|
||||||
|
NavigationLink(destination: WalletView(damus_state: damus, model: damus_state!.wallet), isActive: $wallet_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
if let active_event {
|
||||||
|
let thread = ThreadModel(event: active_event, damus_state: damus_state!)
|
||||||
|
NavigationLink(destination: ThreadView(state: damus_state!, thread: thread), isActive: $thread_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NavigationLink(destination: MaybeSearchView, isActive: $search_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
switch selected_timeline {
|
switch selected_timeline {
|
||||||
case .search:
|
case .search:
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
@@ -200,6 +225,28 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MaybeSearchView: some View {
|
||||||
|
Group {
|
||||||
|
if let search = self.active_search {
|
||||||
|
SearchView(appstate: damus_state!, search: SearchModel(state: damus_state!, search: search))
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var MaybeProfileView: some View {
|
||||||
|
Group {
|
||||||
|
if let pk = self.active_profile {
|
||||||
|
let profile_model = ProfileModel(pubkey: pk, damus: damus_state!)
|
||||||
|
let followers = FollowersModel(damus_state: damus_state!, target: pk)
|
||||||
|
ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers)
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func MaybeReportView(target: ReportTarget) -> some View {
|
func MaybeReportView(target: ReportTarget) -> some View {
|
||||||
Group {
|
Group {
|
||||||
if let damus_state {
|
if let damus_state {
|
||||||
@@ -215,30 +262,32 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func open_event(ev: NostrEvent) {
|
func open_event(ev: NostrEvent) {
|
||||||
let thread = ThreadModel(event: ev, damus_state: damus_state!)
|
popToRoot()
|
||||||
navigationCoordinator.push(route: Route.Thread(thread: thread))
|
self.active_event = ev
|
||||||
|
self.thread_open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func open_wallet(nwc: WalletConnectURL) {
|
func open_wallet(nwc: WalletConnectURL) {
|
||||||
self.damus_state!.wallet.new(nwc)
|
self.damus_state!.wallet.new(nwc)
|
||||||
navigationCoordinator.push(route: Route.Wallet(wallet: damus_state!.wallet))
|
self.wallet_open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func open_profile(id: String) {
|
func open_profile(id: String) {
|
||||||
let profile_model = ProfileModel(pubkey: id, damus: damus_state!)
|
popToRoot()
|
||||||
let followers = FollowersModel(damus_state: damus_state!, target: id)
|
self.active_profile = id
|
||||||
navigationCoordinator.push(route: Route.Profile(profile: profile_model, followers: followers))
|
self.profile_open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func open_search(filt: NostrFilter) {
|
func open_search(filt: NostrFilter) {
|
||||||
let search = SearchModel(state: damus_state!, search: filt)
|
popToRoot()
|
||||||
navigationCoordinator.push(route: Route.Search(search: search))
|
self.active_search = filt
|
||||||
|
self.search_open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
if let damus = self.damus_state {
|
if let damus = self.damus_state {
|
||||||
NavigationStack(path: $navigationCoordinator.path) {
|
NavigationView {
|
||||||
TabView { // Prevents navbar appearance change on scroll
|
TabView { // Prevents navbar appearance change on scroll
|
||||||
MainContent(damus: damus)
|
MainContent(damus: damus)
|
||||||
.toolbar() {
|
.toolbar() {
|
||||||
@@ -278,12 +327,6 @@ struct ContentView: View {
|
|||||||
.overlay(
|
.overlay(
|
||||||
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation())
|
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation())
|
||||||
)
|
)
|
||||||
.navigationDestination(for: Route.self) { route in
|
|
||||||
route.view(navigationCordinator: navigationCoordinator, damusState: damus_state!)
|
|
||||||
}
|
|
||||||
.onReceive(handle_notify(.switched_timeline)) { _ in
|
|
||||||
navigationCoordinator.popToRoot()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationViewStyle(.stack)
|
.navigationViewStyle(.stack)
|
||||||
|
|
||||||
@@ -477,8 +520,8 @@ struct ContentView: View {
|
|||||||
switch local.type {
|
switch local.type {
|
||||||
case .dm:
|
case .dm:
|
||||||
selected_timeline = .dms
|
selected_timeline = .dms
|
||||||
damus_state.dms.set_active_dm(target.pubkey)
|
damus_state.dms.open_dm_by_pk(target.pubkey)
|
||||||
navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model))
|
|
||||||
case .like: fallthrough
|
case .like: fallthrough
|
||||||
case .zap: fallthrough
|
case .zap: fallthrough
|
||||||
case .mention: fallthrough
|
case .mention: fallthrough
|
||||||
@@ -645,8 +688,7 @@ struct ContentView: View {
|
|||||||
bootstrap_relays: bootstrap_relays,
|
bootstrap_relays: bootstrap_relays,
|
||||||
replies: ReplyCounter(our_pubkey: pubkey),
|
replies: ReplyCounter(our_pubkey: pubkey),
|
||||||
muted_threads: MutedThreadsManager(keypair: keypair),
|
muted_threads: MutedThreadsManager(keypair: keypair),
|
||||||
wallet: WalletModel(settings: settings),
|
wallet: WalletModel(settings: settings)
|
||||||
nav: self.navigationCoordinator
|
|
||||||
)
|
)
|
||||||
home.damus_state = self.damus_state!
|
home.damus_state = self.damus_state!
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ struct DamusState {
|
|||||||
let replies: ReplyCounter
|
let replies: ReplyCounter
|
||||||
let muted_threads: MutedThreadsManager
|
let muted_threads: MutedThreadsManager
|
||||||
let wallet: WalletModel
|
let wallet: WalletModel
|
||||||
let nav: NavigationCoordinator
|
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func add_zap(zap: Zapping) -> Bool {
|
func add_zap(zap: Zapping) -> Bool {
|
||||||
@@ -58,5 +57,5 @@ struct DamusState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static var empty: DamusState {
|
static var empty: DamusState {
|
||||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts(), events: EventCache(), bookmarks: BookmarksManager(pubkey: ""), postbox: PostBox(pool: RelayPool()), bootstrap_relays: [], replies: ReplyCounter(our_pubkey: ""), muted_threads: MutedThreadsManager(keypair: Keypair(pubkey: "", privkey: nil)), wallet: WalletModel(settings: UserSettingsStore()), nav: NavigationCoordinator()) }
|
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts(), events: EventCache(), bookmarks: BookmarksManager(pubkey: ""), postbox: PostBox(pool: RelayPool()), bootstrap_relays: [], replies: ReplyCounter(our_pubkey: ""), muted_threads: MutedThreadsManager(keypair: Keypair(pubkey: "", privkey: nil)), wallet: WalletModel(settings: UserSettingsStore())) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ class DirectMessagesModel: ObservableObject {
|
|||||||
self.active_model = model
|
self.active_model = model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func open_dm_by_pk(_ pubkey: String) {
|
||||||
|
self.set_active_dm(pubkey)
|
||||||
|
self.open_dm = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func open_dm_by_model(_ model: DirectMessageModel) {
|
||||||
|
self.set_active_dm_model(model)
|
||||||
|
self.open_dm = true
|
||||||
|
}
|
||||||
|
|
||||||
func set_active_dm(_ pubkey: String) {
|
func set_active_dm(_ pubkey: String) {
|
||||||
for model in self.dms where model.pubkey == pubkey {
|
for model in self.dms where model.pubkey == pubkey {
|
||||||
self.set_active_dm_model(model)
|
self.set_active_dm_model(model)
|
||||||
|
|||||||
@@ -752,7 +752,7 @@ func guard_valid_event(events: EventCache, ev: NostrEvent, callback: @escaping (
|
|||||||
|
|
||||||
switch validated {
|
switch validated {
|
||||||
case .unknown:
|
case .unknown:
|
||||||
Task.detached(priority: .medium) {
|
Task {
|
||||||
let result = validate_event(ev: ev)
|
let result = validate_event(ev: ev)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@@ -858,7 +858,6 @@ func load_our_relays(state: DamusState, m_old_ev: NostrEvent?, ev: NostrEvent) {
|
|||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
save_bootstrap_relays(pubkey: state.pubkey, relays: Array(new))
|
save_bootstrap_relays(pubkey: state.pubkey, relays: Array(new))
|
||||||
state.pool.connect()
|
|
||||||
notify(.relays_changed, ())
|
notify(.relays_changed, ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1077,7 +1076,7 @@ func zap_notification_title(_ zap: Zap) -> String {
|
|||||||
|
|
||||||
func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
|
func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
|
||||||
let src = zap.request.ev
|
let src = zap.request.ev
|
||||||
let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey
|
let pk = zap.is_anon ? "anon" : src.pubkey
|
||||||
let profile = profiles.lookup(id: pk)
|
let profile = profiles.lookup(id: pk)
|
||||||
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
|
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
|
||||||
let formattedSats = format_msats_abbrev(zap.invoice.amount)
|
let formattedSats = format_msats_abbrev(zap.invoice.amount)
|
||||||
@@ -1165,15 +1164,13 @@ func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
|||||||
create_local_notification(profiles: damus_state.profiles, notify: notify )
|
create_local_notification(profiles: damus_state.profiles, notify: notify )
|
||||||
}
|
}
|
||||||
} else if type == .boost && damus_state.settings.repost_notification, let inner_ev = ev.get_inner_event(cache: damus_state.events) {
|
} else if type == .boost && damus_state.settings.repost_notification, let inner_ev = ev.get_inner_event(cache: damus_state.events) {
|
||||||
let content = NSAttributedString(render_note_content(ev: inner_ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: inner_ev.content)
|
||||||
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: content)
|
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
||||||
} else if type == .like && damus_state.settings.like_notification,
|
} else if type == .like && damus_state.settings.like_notification,
|
||||||
let evid = ev.referenced_ids.last?.ref_id,
|
let evid = ev.referenced_ids.last?.ref_id,
|
||||||
let liked_event = damus_state.events.lookup(evid)
|
let liked_event = damus_state.events.lookup(evid)
|
||||||
{
|
{
|
||||||
let content = NSAttributedString(render_note_content(ev: liked_event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: liked_event.content)
|
||||||
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: content)
|
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class ProfileModel: ObservableObject, Equatable {
|
|||||||
profile_filter.authors = [pubkey]
|
profile_filter.authors = [pubkey]
|
||||||
|
|
||||||
text_filter.authors = [pubkey]
|
text_filter.authors = [pubkey]
|
||||||
text_filter.limit = 50
|
text_filter.limit = 500
|
||||||
|
|
||||||
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
|
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
|
||||||
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
|
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
|
||||||
|
|||||||
@@ -7,27 +7,11 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ReportType: String, CustomStringConvertible, CaseIterable {
|
enum ReportType: String {
|
||||||
case spam
|
case explicit
|
||||||
case nudity
|
|
||||||
case profanity
|
|
||||||
case illegal
|
case illegal
|
||||||
|
case spam
|
||||||
case impersonation
|
case impersonation
|
||||||
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .spam:
|
|
||||||
return NSLocalizedString("Spam", comment: "Description of report type for spam.")
|
|
||||||
case .nudity:
|
|
||||||
return NSLocalizedString("Nudity", comment: "Description of report type for nudity.")
|
|
||||||
case .profanity:
|
|
||||||
return NSLocalizedString("Profanity", comment: "Description of report type for profanity.")
|
|
||||||
case .illegal:
|
|
||||||
return NSLocalizedString("Illegal Content", comment: "Description of report type for illegal content.")
|
|
||||||
case .impersonation:
|
|
||||||
return NSLocalizedString("Impersonation", comment: "Description of report type for impersonation.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReportNoteTarget {
|
struct ReportNoteTarget {
|
||||||
@@ -47,12 +31,16 @@ struct Report {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func create_report_tags(target: ReportTarget, type: ReportType) -> [[String]] {
|
func create_report_tags(target: ReportTarget, type: ReportType) -> [[String]] {
|
||||||
|
var tags: [[String]]
|
||||||
switch target {
|
switch target {
|
||||||
case .user(let pubkey):
|
case .user(let pubkey):
|
||||||
return [["p", pubkey, type.rawValue]]
|
tags = [["p", pubkey]]
|
||||||
case .note(let notet):
|
case .note(let notet):
|
||||||
return [["e", notet.note_id, type.rawValue], ["p", notet.pubkey]]
|
tags = [["e", notet.note_id], ["p", notet.pubkey]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags.append(["report", type.rawValue])
|
||||||
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func create_report_event(privkey: String, report: Report) -> NostrEvent? {
|
func create_report_event(privkey: String, report: Report) -> NostrEvent? {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ enum DisplayName {
|
|||||||
|
|
||||||
|
|
||||||
func parse_display_name(profile: Profile?, pubkey: String) -> DisplayName {
|
func parse_display_name(profile: Profile?, pubkey: String) -> DisplayName {
|
||||||
if pubkey == ANON_PUBKEY {
|
if pubkey == "anon" {
|
||||||
return .one(NSLocalizedString("Anonymous", comment: "Placeholder display name of anonymous user."))
|
return .one(NSLocalizedString("Anonymous", comment: "Placeholder display name of anonymous user."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import Foundation
|
|||||||
import secp256k1
|
import secp256k1
|
||||||
|
|
||||||
let PUBKEY_HRP = "npub"
|
let PUBKEY_HRP = "npub"
|
||||||
let ANON_PUBKEY = "anon"
|
|
||||||
|
|
||||||
struct FullKeypair: Equatable {
|
struct FullKeypair: Equatable {
|
||||||
let pubkey: String
|
let pubkey: String
|
||||||
|
|||||||
@@ -1,276 +0,0 @@
|
|||||||
//
|
|
||||||
// Router.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Scott Penrose on 5/7/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
enum Route: Hashable {
|
|
||||||
case ProfileByKey(pubkey: String)
|
|
||||||
case Profile(profile: ProfileModel, followers: FollowersModel)
|
|
||||||
case Followers(followers: FollowersModel)
|
|
||||||
case Relay(relay: String, showActionButtons: Binding<Bool>)
|
|
||||||
case RelayDetail(relay: String, metadata: RelayMetadata)
|
|
||||||
case Following(following: FollowingModel)
|
|
||||||
case MuteList(users: [String])
|
|
||||||
case RelayConfig
|
|
||||||
case Bookmarks
|
|
||||||
case Config
|
|
||||||
case EditMetadata
|
|
||||||
case DMChat(dms: DirectMessageModel)
|
|
||||||
case UserRelays(relays: [String])
|
|
||||||
case KeySettings(keypair: Keypair)
|
|
||||||
case AppearanceSettings(settings: UserSettingsStore)
|
|
||||||
case NotificationSettings(settings: UserSettingsStore)
|
|
||||||
case ZapSettings(settings: UserSettingsStore)
|
|
||||||
case TranslationSettings(settings: UserSettingsStore)
|
|
||||||
case SearchSettings(settings: UserSettingsStore)
|
|
||||||
case Thread(thread: ThreadModel)
|
|
||||||
case Reposts(reposts: RepostsModel)
|
|
||||||
case Reactions(reactions: ReactionsModel)
|
|
||||||
case Zaps(target: ZapTarget)
|
|
||||||
case Search(search: SearchModel)
|
|
||||||
case EULA
|
|
||||||
case Login
|
|
||||||
case CreateAccount
|
|
||||||
case SaveKeys(account: CreateAccountModel)
|
|
||||||
case Wallet(wallet: WalletModel)
|
|
||||||
case WalletScanner(result: Binding<WalletScanResult>)
|
|
||||||
case FollowersYouKnow(friendedFollowers: [String], followers: FollowersModel)
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
func view(navigationCordinator: NavigationCoordinator, damusState: DamusState) -> some View {
|
|
||||||
switch self {
|
|
||||||
case .ProfileByKey(let pubkey):
|
|
||||||
ProfileView(damus_state: damusState, pubkey: pubkey)
|
|
||||||
case .Profile(let profile, let followers):
|
|
||||||
ProfileView(damus_state: damusState, profile: profile, followers: followers)
|
|
||||||
case .Followers(let followers):
|
|
||||||
FollowersView(damus_state: damusState, followers: followers)
|
|
||||||
case .Relay(let relay, let showActionButtons):
|
|
||||||
RelayView(state: damusState, relay: relay, showActionButtons: showActionButtons)
|
|
||||||
case .RelayDetail(let relay, let metadata):
|
|
||||||
RelayDetailView(state: damusState, relay: relay, nip11: metadata)
|
|
||||||
case .Following(let following):
|
|
||||||
FollowingView(damus_state: damusState, following: following)
|
|
||||||
case .MuteList(let users):
|
|
||||||
MutelistView(damus_state: damusState, users: users)
|
|
||||||
case .RelayConfig:
|
|
||||||
RelayConfigView(state: damusState)
|
|
||||||
case .Bookmarks:
|
|
||||||
BookmarksView(state: damusState)
|
|
||||||
case .Config:
|
|
||||||
ConfigView(state: damusState)
|
|
||||||
case .EditMetadata:
|
|
||||||
EditMetadataView(damus_state: damusState)
|
|
||||||
case .DMChat(let dms):
|
|
||||||
DMChatView(damus_state: damusState, dms: dms)
|
|
||||||
case .UserRelays(let relays):
|
|
||||||
UserRelaysView(state: damusState, relays: relays)
|
|
||||||
case .KeySettings(let keypair):
|
|
||||||
KeySettingsView(keypair: keypair)
|
|
||||||
case .AppearanceSettings(let settings):
|
|
||||||
AppearanceSettingsView(settings: settings)
|
|
||||||
case .NotificationSettings(let settings):
|
|
||||||
NotificationSettingsView(settings: settings)
|
|
||||||
case .ZapSettings(let settings):
|
|
||||||
ZapSettingsView(settings: settings)
|
|
||||||
case .TranslationSettings(let settings):
|
|
||||||
NotificationSettingsView(settings: settings)
|
|
||||||
case .SearchSettings(let settings):
|
|
||||||
SearchSettingsView(settings: settings)
|
|
||||||
case .Thread(let thread):
|
|
||||||
ThreadView(state: damusState, thread: thread)
|
|
||||||
case .Reposts(let reposts):
|
|
||||||
RepostsView(damus_state: damusState, model: reposts)
|
|
||||||
case .Reactions(let reactions):
|
|
||||||
ReactionsView(damus_state: damusState, model: reactions)
|
|
||||||
case .Zaps(let target):
|
|
||||||
ZapsView(state: damusState, target: target)
|
|
||||||
case .Search(let search):
|
|
||||||
SearchView(appstate: damusState, search: search)
|
|
||||||
case .EULA:
|
|
||||||
EULAView(nav: navigationCordinator)
|
|
||||||
case .Login:
|
|
||||||
LoginView(nav: navigationCordinator)
|
|
||||||
case .CreateAccount:
|
|
||||||
CreateAccountView(nav: navigationCordinator)
|
|
||||||
case .SaveKeys(let account):
|
|
||||||
SaveKeysView(account: account)
|
|
||||||
case .Wallet(let walletModel):
|
|
||||||
WalletView(damus_state: damusState, model: walletModel)
|
|
||||||
case .WalletScanner(let walletScanResult):
|
|
||||||
WalletScannerView(result: walletScanResult)
|
|
||||||
case .FollowersYouKnow(let friendedFollowers, let followers):
|
|
||||||
FollowersYouKnowView(damus_state: damusState, friended_followers: friendedFollowers, followers: followers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func == (lhs: Route, rhs: Route) -> Bool {
|
|
||||||
switch (lhs, rhs) {
|
|
||||||
case (.ProfileByKey (let lhs_pubkey), .ProfileByKey(let rhs_pubkey)):
|
|
||||||
return lhs_pubkey == rhs_pubkey
|
|
||||||
case (.Profile (let lhs_profile, _), .Profile(let rhs_profile, _)):
|
|
||||||
return lhs_profile == rhs_profile
|
|
||||||
case (.Followers (_), .Followers (_)):
|
|
||||||
return true
|
|
||||||
case (.Relay (let lhs_relay, _), .Relay (let rhs_relay, _)):
|
|
||||||
return lhs_relay == rhs_relay
|
|
||||||
case (.RelayDetail(let lhs_relay, _), .RelayDetail(let rhs_relay, _)):
|
|
||||||
return lhs_relay == rhs_relay
|
|
||||||
case (.Following(_), .Following(_)):
|
|
||||||
return true
|
|
||||||
case (.MuteList(let lhs_users), .MuteList(let rhs_users)):
|
|
||||||
return lhs_users == rhs_users
|
|
||||||
case (.RelayConfig, .RelayConfig):
|
|
||||||
return true
|
|
||||||
case (.Bookmarks, .Bookmarks):
|
|
||||||
return true
|
|
||||||
case (.Config, .Config):
|
|
||||||
return true
|
|
||||||
case (.EditMetadata, .EditMetadata):
|
|
||||||
return true
|
|
||||||
case (.DMChat(let lhs_dms), .DMChat(let rhs_dms)):
|
|
||||||
return lhs_dms.our_pubkey == rhs_dms.our_pubkey
|
|
||||||
case (.UserRelays(let lhs_relays), .UserRelays(let rhs_relays)):
|
|
||||||
return lhs_relays == rhs_relays
|
|
||||||
case (.KeySettings(let lhs_keypair), .KeySettings(let rhs_keypair)):
|
|
||||||
return lhs_keypair.pubkey == rhs_keypair.pubkey
|
|
||||||
case (.AppearanceSettings(_), .AppearanceSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.NotificationSettings(_), .NotificationSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.ZapSettings(_), .ZapSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.TranslationSettings(_), .TranslationSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.SearchSettings(_), .SearchSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.Thread(let lhs_threadModel), .Thread(thread: let rhs_threadModel)):
|
|
||||||
return lhs_threadModel.event.id == rhs_threadModel.event.id
|
|
||||||
case (.Reposts(let lhs_reposts), .Reposts(let rhs_reposts)):
|
|
||||||
return lhs_reposts.target == rhs_reposts.target
|
|
||||||
case (.Reactions(let lhs_reactions), .Reactions(let rhs_reactions)):
|
|
||||||
return lhs_reactions.target == rhs_reactions.target
|
|
||||||
case (.Zaps(let lhs_target), .Zaps(let rhs_target)):
|
|
||||||
return lhs_target == rhs_target
|
|
||||||
case (.Search(let lhs_search), .Search(let rhs_search)):
|
|
||||||
return lhs_search.sub_id == rhs_search.sub_id && lhs_search.profiles_subid == rhs_search.profiles_subid
|
|
||||||
case (.EULA, .EULA):
|
|
||||||
return true
|
|
||||||
case (.Login, .Login):
|
|
||||||
return true
|
|
||||||
case (.CreateAccount, .CreateAccount):
|
|
||||||
return true
|
|
||||||
case (.SaveKeys(let lhs_account), .SaveKeys(let rhs_account)):
|
|
||||||
return lhs_account.pubkey == rhs_account.pubkey
|
|
||||||
case (.Wallet(_), .Wallet(_)):
|
|
||||||
return true
|
|
||||||
case (.WalletScanner(_), .WalletScanner(_)):
|
|
||||||
return true
|
|
||||||
case (.FollowersYouKnow(_, _), .FollowersYouKnow(_, _)):
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
switch self {
|
|
||||||
case .ProfileByKey(let pubkey):
|
|
||||||
hasher.combine("profilebykey")
|
|
||||||
hasher.combine(pubkey)
|
|
||||||
case .Profile(let profile, _):
|
|
||||||
hasher.combine("profile")
|
|
||||||
hasher.combine(profile.pubkey)
|
|
||||||
case .Followers(_):
|
|
||||||
hasher.combine("followers")
|
|
||||||
case .Relay(let relay, _):
|
|
||||||
hasher.combine("relay")
|
|
||||||
hasher.combine(relay)
|
|
||||||
case .RelayDetail(let relay, _):
|
|
||||||
hasher.combine("relayDetail")
|
|
||||||
hasher.combine(relay)
|
|
||||||
case .Following(_):
|
|
||||||
hasher.combine("following")
|
|
||||||
case .MuteList(let users):
|
|
||||||
hasher.combine("muteList")
|
|
||||||
hasher.combine(users)
|
|
||||||
case .RelayConfig:
|
|
||||||
hasher.combine("relayConfig")
|
|
||||||
case .Bookmarks:
|
|
||||||
hasher.combine("bookmarks")
|
|
||||||
case .Config:
|
|
||||||
hasher.combine("config")
|
|
||||||
case .EditMetadata:
|
|
||||||
hasher.combine("editMetadata")
|
|
||||||
case .DMChat(let dms):
|
|
||||||
hasher.combine("dms")
|
|
||||||
hasher.combine(dms.our_pubkey)
|
|
||||||
case .UserRelays(let relays):
|
|
||||||
hasher.combine("userRelays")
|
|
||||||
hasher.combine(relays)
|
|
||||||
case .KeySettings(let keypair):
|
|
||||||
hasher.combine("keySettings")
|
|
||||||
hasher.combine(keypair.pubkey)
|
|
||||||
case .AppearanceSettings(_):
|
|
||||||
hasher.combine("appearanceSettings")
|
|
||||||
case .NotificationSettings(_):
|
|
||||||
hasher.combine("notificationSettings")
|
|
||||||
case .ZapSettings(_):
|
|
||||||
hasher.combine("zapSettings")
|
|
||||||
case .TranslationSettings(_):
|
|
||||||
hasher.combine("translationSettings")
|
|
||||||
case .SearchSettings(_):
|
|
||||||
hasher.combine("searchSettings")
|
|
||||||
case .Thread(let threadModel):
|
|
||||||
hasher.combine("thread")
|
|
||||||
hasher.combine(threadModel.event.id)
|
|
||||||
case .Reposts(let reposts):
|
|
||||||
hasher.combine("reposts")
|
|
||||||
hasher.combine(reposts.target)
|
|
||||||
case .Zaps(let target):
|
|
||||||
hasher.combine("zaps")
|
|
||||||
hasher.combine(target.id)
|
|
||||||
hasher.combine(target.pubkey)
|
|
||||||
case .Reactions(let reactions):
|
|
||||||
hasher.combine("reactions")
|
|
||||||
hasher.combine(reactions.target)
|
|
||||||
case .Search(let search):
|
|
||||||
hasher.combine("search")
|
|
||||||
hasher.combine(search.sub_id)
|
|
||||||
hasher.combine(search.profiles_subid)
|
|
||||||
case .EULA:
|
|
||||||
hasher.combine("eula")
|
|
||||||
case .Login:
|
|
||||||
hasher.combine("login")
|
|
||||||
case .CreateAccount:
|
|
||||||
hasher.combine("createAccount")
|
|
||||||
case .SaveKeys(let account):
|
|
||||||
hasher.combine("saveKeys")
|
|
||||||
hasher.combine(account.pubkey)
|
|
||||||
case .Wallet(_):
|
|
||||||
hasher.combine("wallet")
|
|
||||||
case .WalletScanner(_):
|
|
||||||
hasher.combine("walletScanner")
|
|
||||||
case .FollowersYouKnow(let friendedFollowers, let followers):
|
|
||||||
hasher.combine("followersYouKnow")
|
|
||||||
hasher.combine(friendedFollowers)
|
|
||||||
hasher.combine(followers.sub_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NavigationCoordinator: ObservableObject {
|
|
||||||
@Published var path = [Route]()
|
|
||||||
|
|
||||||
func push(route: Route) {
|
|
||||||
path.append(route)
|
|
||||||
}
|
|
||||||
|
|
||||||
func popToRoot() {
|
|
||||||
path = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,13 +19,17 @@ struct EventDetailBar: View {
|
|||||||
self.target = target
|
self.target = target
|
||||||
self.target_pk = target_pk
|
self.target_pk = target_pk
|
||||||
self._bar = ObservedObject(wrappedValue: make_actionbar_model(ev: target, damus: state))
|
self._bar = ObservedObject(wrappedValue: make_actionbar_model(ev: target, damus: state))
|
||||||
|
}
|
||||||
|
|
||||||
|
var ZapDetails: Text {
|
||||||
|
let noun = Text(verbatim: zapsCountString(bar.zaps)).foregroundColor(.gray)
|
||||||
|
return Text("\(Text(verbatim: bar.zaps.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.")
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
if bar.boosts > 0 {
|
if bar.boosts > 0 {
|
||||||
NavigationLink(value: Route.Reposts(reposts: RepostsModel(state: state, target: target))) {
|
NavigationLink(destination: RepostsView(damus_state: state, model: RepostsModel(state: state, target: target))) {
|
||||||
let noun = Text(verbatim: repostsCountString(bar.boosts)).foregroundColor(.gray)
|
let noun = Text(verbatim: repostsCountString(bar.boosts)).foregroundColor(.gray)
|
||||||
Text("\(Text(verbatim: bar.boosts.formatted()).font(.body.bold())) \(noun)", comment: "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'.")
|
Text("\(Text(verbatim: bar.boosts.formatted()).font(.body.bold())) \(noun)", comment: "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'.")
|
||||||
}
|
}
|
||||||
@@ -33,19 +37,18 @@ struct EventDetailBar: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if bar.likes > 0 && !state.settings.onlyzaps_mode {
|
if bar.likes > 0 && !state.settings.onlyzaps_mode {
|
||||||
NavigationLink(value: Route.Reactions(reactions: ReactionsModel(state: state, target: target))) {
|
NavigationLink(destination: ReactionsView(damus_state: state, model: ReactionsModel(state: state, target: target))) {
|
||||||
let noun = Text(verbatim: reactionsCountString(bar.likes)).foregroundColor(.gray)
|
let noun = Text(verbatim: reactionsCountString(bar.likes)).foregroundColor(.gray)
|
||||||
Text("\(Text(verbatim: bar.likes.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reactions there are on a post. In source English, the first variable is the number of reactions, and the second variable is 'Reaction' or 'Reactions'.")
|
Text("\(Text(verbatim: bar.likes.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reactions there are on a post. In source English, the first variable is the number of reactions, and the second variable is 'Reaction' or 'Reactions'.")
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
if bar.zaps > 0 {
|
if !state.settings.nozaps && bar.zaps > 0 {
|
||||||
NavigationLink(value: Route.Zaps(target: .note(id: target, author: target_pk))) {
|
let dst = ZapsView(state: state, target: .note(id: target, author: target_pk))
|
||||||
let noun = Text(verbatim: zapsCountString(bar.zaps)).foregroundColor(.gray)
|
NavigationLink(destination: dst) {
|
||||||
Text("\(Text(verbatim: bar.zaps.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.")
|
ZapDetails
|
||||||
}
|
}.buttonStyle(PlainButtonStyle())
|
||||||
.buttonStyle(PlainButtonStyle())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ struct BookmarksView: View {
|
|||||||
} else {
|
} else {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
InnerTimelineView(events: EventHolder(events: bookmarks, incoming: []), damus: state, filter: noneFilter)
|
InnerTimelineView(events: EventHolder(events: bookmarks, incoming: []), damus: state, filter: noneFilter)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,31 +36,32 @@ struct ConfigView: View {
|
|||||||
ZStack(alignment: .leading) {
|
ZStack(alignment: .leading) {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
NavigationLink(value: Route.KeySettings(keypair: state.keypair)) {
|
NavigationLink(destination: KeySettingsView(keypair: state.keypair)) {
|
||||||
IconLabel(NSLocalizedString("Keys", comment: "Settings section for managing keys"), img_name: "key", color: .purple)
|
IconLabel(NSLocalizedString("Keys", comment: "Settings section for managing keys"), img_name: "key", color: .purple)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.AppearanceSettings(settings: settings)) {
|
NavigationLink(destination: AppearanceSettingsView(settings: settings)) {
|
||||||
IconLabel(NSLocalizedString("Appearance", comment: "Section header for text and appearance settings"), img_name: "eye", color: .red)
|
IconLabel(NSLocalizedString("Appearance", comment: "Section header for text and appearance settings"), img_name: "eye", color: .red)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.SearchSettings(settings: settings)) {
|
NavigationLink(destination: SearchSettingsView(settings: settings)) {
|
||||||
IconLabel(NSLocalizedString("Search/Universe", comment: "Section header for search/universe settings"), img_name: "magnifyingglass", color: .red)
|
IconLabel(NSLocalizedString("Search/Universe", comment: "Section header for search/universe settings"), img_name: "magnifyingglass", color: .red)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.NotificationSettings(settings: settings)) {
|
NavigationLink(destination: NotificationSettingsView(settings: settings)) {
|
||||||
IconLabel(NSLocalizedString("Notifications", comment: "Section header for Damus notifications"), img_name: "notification-bell-on", color: .blue)
|
IconLabel(NSLocalizedString("Notifications", comment: "Section header for Damus notifications"), img_name: "notification-bell-on", color: .blue)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.ZapSettings(settings: settings)) {
|
NavigationLink(destination: ZapSettingsView(settings: settings)) {
|
||||||
IconLabel(NSLocalizedString("Zaps", comment: "Section header for zap settings"), img_name: "zap.fill", color: .orange)
|
IconLabel(NSLocalizedString("Zaps", comment: "Section header for zap settings"), img_name: "zap.fill", color: .orange)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.TranslationSettings(settings: settings)) {
|
NavigationLink(destination: TranslationSettingsView(settings: settings)) {
|
||||||
IconLabel(NSLocalizedString("Translation", comment: "Section header for text and appearance settings"), img_name: "globe", color: .green)
|
IconLabel(NSLocalizedString("Translation", comment: "Section header for text and appearance settings"), img_name: "globe", color: .green)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) {
|
Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
if state.keypair.privkey == nil {
|
if state.keypair.privkey == nil {
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import SwiftUI
|
|||||||
struct CreateAccountView: View {
|
struct CreateAccountView: View {
|
||||||
@StateObject var account: CreateAccountModel = CreateAccountModel()
|
@StateObject var account: CreateAccountModel = CreateAccountModel()
|
||||||
@StateObject var profileUploadViewModel = ProfileUploadingViewModel()
|
@StateObject var profileUploadViewModel = ProfileUploadingViewModel()
|
||||||
var nav: NavigationCoordinator
|
|
||||||
|
@State var is_done: Bool = false
|
||||||
|
|
||||||
func SignupForm<FormContent: View>(@ViewBuilder content: () -> FormContent) -> some View {
|
func SignupForm<FormContent: View>(@ViewBuilder content: () -> FormContent) -> some View {
|
||||||
return VStack(alignment: .leading, spacing: 10.0, content: content)
|
return VStack(alignment: .leading, spacing: 10.0, content: content)
|
||||||
@@ -24,6 +25,10 @@ struct CreateAccountView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
|
NavigationLink(destination: SaveKeysView(account: account), isActive: $is_done) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
ProfilePictureSelector(pubkey: account.pubkey, viewModel: profileUploadViewModel, callback: uploadedProfilePicture(image_url:))
|
ProfilePictureSelector(pubkey: account.pubkey, viewModel: profileUploadViewModel, callback: uploadedProfilePicture(image_url:))
|
||||||
@@ -58,7 +63,7 @@ struct CreateAccountView: View {
|
|||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
nav.push(route: Route.SaveKeys(account: account))
|
self.is_done = true
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Create account now", comment: "Button to create account.")
|
Text("Create account now", comment: "Button to create account.")
|
||||||
@@ -130,7 +135,7 @@ extension View {
|
|||||||
struct CreateAccountView_Previews: PreviewProvider {
|
struct CreateAccountView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let model = CreateAccountModel(real: "", nick: "jb55", about: "")
|
let model = CreateAccountModel(real: "", nick: "jb55", about: "")
|
||||||
return CreateAccountView(account: model, nav: .init())
|
return CreateAccountView(account: model)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ struct DMChatView: View, KeyboardReadable {
|
|||||||
|
|
||||||
var Header: some View {
|
var Header: some View {
|
||||||
let profile = damus_state.profiles.lookup(id: pubkey)
|
let profile = damus_state.profiles.lookup(id: pubkey)
|
||||||
return NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) {
|
let profile_page = ProfileView(damus_state: damus_state, pubkey: pubkey)
|
||||||
|
return NavigationLink(destination: profile_page) {
|
||||||
HStack {
|
HStack {
|
||||||
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ struct DirectMessagesView: View {
|
|||||||
|
|
||||||
func MainContent(requests: Bool) -> some View {
|
func MainContent(requests: Bool) -> some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
let chat = DMChatView(damus_state: damus_state, dms: model.active_model)
|
||||||
|
NavigationLink(destination: chat, isActive: $model.open_dm) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
LazyVStack(spacing: 0) {
|
LazyVStack(spacing: 0) {
|
||||||
if model.dms.isEmpty, !model.loading {
|
if model.dms.isEmpty, !model.loading {
|
||||||
EmptyTimelineView()
|
EmptyTimelineView()
|
||||||
@@ -50,8 +54,7 @@ struct DirectMessagesView: View {
|
|||||||
if ok, let ev = model.events.last {
|
if ok, let ev = model.events.last {
|
||||||
EventView(damus: damus_state, event: ev, pubkey: model.pubkey, options: options)
|
EventView(damus: damus_state, event: ev, pubkey: model.pubkey, options: options)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
self.model.set_active_dm_model(model)
|
self.model.open_dm_by_model(model)
|
||||||
damus_state.nav.push(route: Route.DMChat(dms: self.model.active_model))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|||||||
@@ -56,13 +56,18 @@ By using our Application, you signify your acceptance of this EULA. If you do no
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
struct EULAView: View {
|
struct EULAView: View {
|
||||||
|
@State private var login = false
|
||||||
|
@State var accepted = false
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
var nav: NavigationCoordinator
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
NavigationLink(destination: LoginView(accepted: $accepted), isActive: $login) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
Text(Markdown.parse(content: eula))
|
Text(Markdown.parse(content: eula))
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@@ -91,7 +96,8 @@ struct EULAView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
nav.push(route: Route.Login)
|
accepted = true
|
||||||
|
login.toggle()
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Accept", comment: "Button to accept the end user license agreement before being allowed into the app.")
|
Text("Accept", comment: "Button to accept the end user license agreement before being allowed into the app.")
|
||||||
@@ -120,6 +126,6 @@ struct EULAView: View {
|
|||||||
|
|
||||||
struct EULAView_Previews: PreviewProvider {
|
struct EULAView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
EULAView(nav: .init())
|
EULAView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ struct BuilderEventView: View {
|
|||||||
if let event {
|
if let event {
|
||||||
let ev = event.get_inner_event(cache: damus.events) ?? event
|
let ev = event.get_inner_event(cache: damus.events) ?? event
|
||||||
let thread = ThreadModel(event: ev, damus_state: damus)
|
let thread = ThreadModel(event: ev, damus_state: damus)
|
||||||
NavigationLink(value: Route.Thread(thread: thread)) {
|
let dest = ThreadView(state: damus, thread: thread)
|
||||||
|
NavigationLink(destination: dest) {
|
||||||
EventView(damus: damus, event: event, options: .embedded)
|
EventView(damus: damus, event: event, options: .embedded)
|
||||||
.padding([.top, .bottom], 8)
|
.padding([.top, .bottom], 8)
|
||||||
}.buttonStyle(.plain)
|
}.buttonStyle(.plain)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct EventProfile: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
VStack {
|
VStack {
|
||||||
NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) {
|
NavigationLink(destination: ProfileView(damus_state: damus_state, pubkey: pubkey)) {
|
||||||
ProfilePicView(pubkey: pubkey, size: pfp_size, highlight: .none, profiles: damus_state.profiles, disable_animation: disable_animation)
|
ProfilePicView(pubkey: pubkey, size: pfp_size, highlight: .none, profiles: damus_state.profiles, disable_animation: disable_animation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ struct TextEvent: View {
|
|||||||
|
|
||||||
func ProfileName(is_anon: Bool) -> some View {
|
func ProfileName(is_anon: Bool) -> some View {
|
||||||
let profile = damus.profiles.lookup(id: pubkey)
|
let profile = damus.profiles.lookup(id: pubkey)
|
||||||
let pk = is_anon ? ANON_PUBKEY : pubkey
|
let pk = is_anon ? "anon" : pubkey
|
||||||
return EventProfileName(pubkey: pk, profile: profile, damus: damus, size: .normal)
|
return EventProfileName(pubkey: pk, profile: profile, damus: damus, size: .normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,16 @@ struct FollowUserView: View {
|
|||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
|
|
||||||
static let markdown = Markdown()
|
static let markdown = Markdown()
|
||||||
|
@State var navigating: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let dest = ProfileView(damus_state: damus_state, pubkey: target.pubkey)
|
||||||
|
NavigationLink(destination: dest, isActive: $navigating) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
UserViewRow(damus_state: damus_state, pubkey: target.pubkey)
|
UserViewRow(damus_state: damus_state, pubkey: target.pubkey)
|
||||||
.onTapGesture {
|
|
||||||
damus_state.nav.push(route: Route.ProfileByKey(pubkey: target.pubkey))
|
|
||||||
}
|
|
||||||
|
|
||||||
FollowButtonView(target: target, follows_you: false, follow_state: damus_state.contacts.follow_state(target.pubkey))
|
FollowButtonView(target: target, follows_you: false, follow_state: damus_state.contacts.follow_state(target.pubkey))
|
||||||
}
|
}
|
||||||
@@ -29,7 +32,8 @@ struct FollowUserView: View {
|
|||||||
struct FollowersYouKnowView: View {
|
struct FollowersYouKnowView: View {
|
||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
let friended_followers: [String]
|
let friended_followers: [String]
|
||||||
@ObservedObject var followers: FollowersModel
|
|
||||||
|
@EnvironmentObject var followers: FollowersModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@@ -46,7 +50,8 @@ struct FollowersYouKnowView: View {
|
|||||||
|
|
||||||
struct FollowersView: View {
|
struct FollowersView: View {
|
||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
@ObservedObject var followers: FollowersModel
|
|
||||||
|
@EnvironmentObject var followers: FollowersModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@@ -72,7 +77,6 @@ struct FollowingView: View {
|
|||||||
|
|
||||||
let following: FollowingModel
|
let following: FollowingModel
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
LazyVStack(alignment: .leading) {
|
LazyVStack(alignment: .leading) {
|
||||||
|
|||||||
@@ -33,11 +33,13 @@ enum ParsedKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct LoginView: View {
|
struct LoginView: View {
|
||||||
|
@State private var create_account = false
|
||||||
@State var key: String = ""
|
@State var key: String = ""
|
||||||
@State var is_pubkey: Bool = false
|
@State var is_pubkey: Bool = false
|
||||||
@State var error: String? = nil
|
@State var error: String? = nil
|
||||||
@State private var credential_handler = CredentialHandler()
|
@State private var credential_handler = CredentialHandler()
|
||||||
var nav: NavigationCoordinator
|
|
||||||
|
@Binding var accepted: Bool
|
||||||
|
|
||||||
func get_error(parsed_key: ParsedKey?) -> String? {
|
func get_error(parsed_key: ParsedKey?) -> String? {
|
||||||
if self.error != nil {
|
if self.error != nil {
|
||||||
@@ -53,6 +55,12 @@ struct LoginView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
|
if accepted {
|
||||||
|
NavigationLink(destination: CreateAccountView(), isActive: $create_account) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
SignInHeader()
|
SignInHeader()
|
||||||
.padding(.top, 100)
|
.padding(.top, 100)
|
||||||
@@ -99,7 +107,7 @@ struct LoginView: View {
|
|||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateAccountPrompt(nav: nav)
|
CreateAccountPrompt(create_account: $create_account)
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -329,14 +337,14 @@ struct SignInEntry: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct CreateAccountPrompt: View {
|
struct CreateAccountPrompt: View {
|
||||||
var nav: NavigationCoordinator
|
@Binding var create_account: Bool
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Text("New to Nostr?", comment: "Ask the user if they are new to Nostr")
|
Text("New to Nostr?", comment: "Ask the user if they are new to Nostr")
|
||||||
.foregroundColor(Color("DamusMediumGrey"))
|
.foregroundColor(Color("DamusMediumGrey"))
|
||||||
|
|
||||||
Button(NSLocalizedString("Create account", comment: "Button to navigate to create account view.")) {
|
Button(NSLocalizedString("Create account", comment: "Button to navigate to create account view.")) {
|
||||||
nav.push(route: Route.CreateAccount)
|
create_account.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -350,8 +358,8 @@ struct LoginView_Previews: PreviewProvider {
|
|||||||
let pubkey = "npub18m76awca3y37hkvuneavuw6pjj4525fw90necxmadrvjg0sdy6qsngq955"
|
let pubkey = "npub18m76awca3y37hkvuneavuw6pjj4525fw90necxmadrvjg0sdy6qsngq955"
|
||||||
let bech32_pubkey = "KeyInput"
|
let bech32_pubkey = "KeyInput"
|
||||||
Group {
|
Group {
|
||||||
LoginView(key: pubkey, nav: .init())
|
LoginView(key: pubkey, accepted: .constant(true))
|
||||||
LoginView(key: bech32_pubkey, nav: .init())
|
LoginView(key: bech32_pubkey, accepted: .constant(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,16 @@ enum ReactingTo {
|
|||||||
case your_profile
|
case your_profile
|
||||||
}
|
}
|
||||||
|
|
||||||
func determine_reacting_to(our_pubkey: String, ev: NostrEvent?) -> ReactingTo {
|
func determine_reacting_to(our_pubkey: String, ev: NostrEvent?, group: EventGroupType, nozaps: Bool) -> ReactingTo {
|
||||||
guard let ev else {
|
guard let ev else {
|
||||||
return .your_profile
|
return .your_profile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nozaps && group.is_note_zap {
|
||||||
|
// ZAPPING NOTES IS NOT ALLOWED!!!! EVIL!!!
|
||||||
|
return .your_profile
|
||||||
|
}
|
||||||
|
|
||||||
if ev.pubkey == our_pubkey {
|
if ev.pubkey == our_pubkey {
|
||||||
return .your_note
|
return .your_note
|
||||||
}
|
}
|
||||||
@@ -73,42 +78,19 @@ func event_author_name(profiles: Profiles, pubkey: String) -> String {
|
|||||||
return Profile.displayName(profile: alice_prof, pubkey: pubkey).username.truncate(maxLength: 50)
|
return Profile.displayName(profile: alice_prof, pubkey: pubkey).username.truncate(maxLength: 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
func event_group_unique_pubkeys(profiles: Profiles, group: EventGroupType) -> [String] {
|
func event_group_author_name(profiles: Profiles, ind: Int, group: EventGroupType) -> String {
|
||||||
var seen = Set<String>()
|
|
||||||
var sorted = [String]()
|
|
||||||
|
|
||||||
if let zapgrp = group.zap_group {
|
if let zapgrp = group.zap_group {
|
||||||
let zaps = zapgrp.zaps
|
let zap = zapgrp.zaps[ind]
|
||||||
|
|
||||||
for i in 0..<zaps.count {
|
|
||||||
let zap = zapgrp.zaps[i]
|
|
||||||
let pubkey: String
|
|
||||||
|
|
||||||
if zap.is_anon {
|
if zap.is_anon {
|
||||||
pubkey = ANON_PUBKEY
|
return NSLocalizedString("Anonymous", comment: "Placeholder author name of the anonymous person who zapped an event.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return event_author_name(profiles: profiles, pubkey: zap.request.ev.pubkey)
|
||||||
} else {
|
} else {
|
||||||
pubkey = zap.request.ev.pubkey
|
let ev = group.events[ind]
|
||||||
|
return event_author_name(profiles: profiles, pubkey: ev.pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !seen.contains(pubkey) {
|
|
||||||
seen.insert(pubkey)
|
|
||||||
sorted.append(pubkey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let events = group.events
|
|
||||||
|
|
||||||
for i in 0..<events.count {
|
|
||||||
let ev = events[i]
|
|
||||||
let pubkey = ev.pubkey
|
|
||||||
if !seen.contains(pubkey) {
|
|
||||||
seen.insert(pubkey)
|
|
||||||
sorted.append(pubkey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sorted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,29 +130,29 @@ func event_group_unique_pubkeys(profiles: Profiles, group: EventGroupType) -> [S
|
|||||||
"zapped_your_profile_2" - returned when 2 zaps occurred to the current user's profile
|
"zapped_your_profile_2" - returned when 2 zaps occurred to the current user's profile
|
||||||
"zapped_your_profile_3" - returned when 3 or more zaps occurred to the current user's profile
|
"zapped_your_profile_3" - returned when 3 or more zaps occurred to the current user's profile
|
||||||
*/
|
*/
|
||||||
func reacting_to_text(profiles: Profiles, our_pubkey: String, group: EventGroupType, ev: NostrEvent?, pubkeys: [String], locale: Locale? = nil) -> String {
|
func reacting_to_text(profiles: Profiles, our_pubkey: String, group: EventGroupType, ev: NostrEvent?, nozaps: Bool, locale: Locale? = nil) -> String {
|
||||||
if group.events.count == 0 {
|
if group.events.count == 0 {
|
||||||
return "??"
|
return "??"
|
||||||
}
|
}
|
||||||
|
|
||||||
let verb = reacting_to_verb(group: group)
|
let verb = reacting_to_verb(group: group)
|
||||||
let reacting_to = determine_reacting_to(our_pubkey: our_pubkey, ev: ev)
|
let reacting_to = determine_reacting_to(our_pubkey: our_pubkey, ev: ev, group: group, nozaps: nozaps)
|
||||||
let localization_key = "\(verb)_\(reacting_to)_\(min(pubkeys.count, 3))"
|
let localization_key = "\(verb)_\(reacting_to)_\(min(group.events.count, 3))"
|
||||||
let format = localizedStringFormat(key: localization_key, locale: locale)
|
let format = localizedStringFormat(key: localization_key, locale: locale)
|
||||||
|
|
||||||
switch pubkeys.count {
|
switch group.events.count {
|
||||||
case 1:
|
case 1:
|
||||||
let display_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
|
let display_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
|
||||||
|
|
||||||
return String(format: format, locale: locale, display_name)
|
return String(format: format, locale: locale, display_name)
|
||||||
case 2:
|
case 2:
|
||||||
let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
|
let alice_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
|
||||||
let bob_name = event_author_name(profiles: profiles, pubkey: pubkeys[1])
|
let bob_name = event_group_author_name(profiles: profiles, ind: 1, group: group)
|
||||||
|
|
||||||
return String(format: format, locale: locale, alice_name, bob_name)
|
return String(format: format, locale: locale, alice_name, bob_name)
|
||||||
default:
|
default:
|
||||||
let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
|
let alice_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
|
||||||
let count = pubkeys.count - 1
|
let count = group.events.count - 1
|
||||||
|
|
||||||
return String(format: format, locale: locale, count, alice_name)
|
return String(format: format, locale: locale, count, alice_name)
|
||||||
}
|
}
|
||||||
@@ -193,8 +175,8 @@ struct EventGroupView: View {
|
|||||||
let event: NostrEvent?
|
let event: NostrEvent?
|
||||||
let group: EventGroupType
|
let group: EventGroupType
|
||||||
|
|
||||||
func GroupDescription(_ pubkeys: [String]) -> some View {
|
var GroupDescription: some View {
|
||||||
Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, pubkeys: pubkeys))")
|
Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, nozaps: state.settings.nozaps))")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ZapIcon(_ zapgrp: ZapGroup) -> some View {
|
func ZapIcon(_ zapgrp: ZapGroup) -> some View {
|
||||||
@@ -234,15 +216,15 @@ struct EventGroupView: View {
|
|||||||
.frame(width: PFP_SIZE + 10)
|
.frame(width: PFP_SIZE + 10)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
let unique_pubkeys = event_group_unique_pubkeys(profiles: state.profiles, group: group)
|
ProfilePicturesView(state: state, pubkeys: group.events.map { $0.pubkey })
|
||||||
|
|
||||||
ProfilePicturesView(state: state, pubkeys: unique_pubkeys)
|
|
||||||
|
|
||||||
if let event {
|
if let event {
|
||||||
let thread = ThreadModel(event: event, damus_state: state)
|
let thread = ThreadModel(event: event, damus_state: state)
|
||||||
NavigationLink(value: Route.Thread(thread: thread)) {
|
let dest = ThreadView(state: state, thread: thread)
|
||||||
|
GroupDescription
|
||||||
|
if !state.settings.nozaps || !group.is_note_zap {
|
||||||
|
NavigationLink(destination: dest) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
GroupDescription(unique_pubkeys)
|
|
||||||
EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content])
|
EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content])
|
||||||
.padding([.top], 1)
|
.padding([.top], 1)
|
||||||
.padding([.trailing])
|
.padding([.trailing])
|
||||||
@@ -250,8 +232,9 @@ struct EventGroupView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
GroupDescription(unique_pubkeys)
|
GroupDescription
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ struct NotificationItemView: View {
|
|||||||
EventGroupView(state: state, event: ev, group: .reaction(evgrp))
|
EventGroupView(state: state, event: ev, group: .reaction(evgrp))
|
||||||
|
|
||||||
case .reply(let ev):
|
case .reply(let ev):
|
||||||
NavigationLink(value: Route.Thread(thread: ThreadModel(event: ev, damus_state: state))) {
|
NavigationLink(destination: ThreadView(state: state, thread: ThreadModel(event: ev, damus_state: state))) {
|
||||||
EventView(damus: state, event: ev, options: options)
|
EventView(damus: state, event: ev, options: options)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|||||||
@@ -11,12 +11,19 @@ struct ProfilePicturesView: View {
|
|||||||
let state: DamusState
|
let state: DamusState
|
||||||
let pubkeys: [String]
|
let pubkeys: [String]
|
||||||
|
|
||||||
|
@State var nav_target: String? = nil
|
||||||
|
@State var navigating: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
NavigationLink(destination: ProfileView(damus_state: state, pubkey: nav_target ?? ""), isActive: $navigating) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
HStack {
|
HStack {
|
||||||
ForEach(pubkeys.prefix(8), id: \.self) { pubkey in
|
ForEach(pubkeys.prefix(8), id: \.self) { pubkey in
|
||||||
ProfilePicView(pubkey: pubkey, size: 32.0, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation)
|
ProfilePicView(pubkey: pubkey, size: 32.0, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
state.nav.push(route: Route.ProfileByKey(pubkey: pubkey))
|
nav_target = pubkey
|
||||||
|
navigating = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,15 +19,10 @@ class TagModel: ObservableObject {
|
|||||||
var diff = 0
|
var diff = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PostTarget {
|
|
||||||
case none
|
|
||||||
case user(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PostAction {
|
enum PostAction {
|
||||||
case replying_to(NostrEvent)
|
case replying_to(NostrEvent)
|
||||||
case quoting(NostrEvent)
|
case quoting(NostrEvent)
|
||||||
case posting(PostTarget)
|
case posting
|
||||||
|
|
||||||
var ev: NostrEvent? {
|
var ev: NostrEvent? {
|
||||||
switch self {
|
switch self {
|
||||||
@@ -84,27 +79,13 @@ struct PostView: View {
|
|||||||
|
|
||||||
post.enumerateAttributes(in: NSRange(location: 0, length: post.length), options: []) { attributes, range, stop in
|
post.enumerateAttributes(in: NSRange(location: 0, length: post.length), options: []) { attributes, range, stop in
|
||||||
if let link = attributes[.link] as? String {
|
if let link = attributes[.link] as? String {
|
||||||
let normalized_link: String
|
post.replaceCharacters(in: range, with: link)
|
||||||
if link.hasPrefix("damus:nostr:") {
|
|
||||||
// Replace damus:nostr: URI prefix with nostr: since the former is for internal navigation and not meant to be posted.
|
|
||||||
normalized_link = String(link.dropFirst(6))
|
|
||||||
} else {
|
|
||||||
normalized_link = link
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add zero-width space in case text preceding the mention is not a whitespace.
|
|
||||||
// In the case where the character preceding the mention is a whitespace, the added zero-width space will be stripped out.
|
|
||||||
post.replaceCharacters(in: range, with: "\u{200B}\(normalized_link)\u{200B}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var content = self.post.string
|
var content = self.post.string
|
||||||
// If two zero-width spaces are next to each other, normalize it to just one zero-width space.
|
|
||||||
.replacingOccurrences(of: "\u{200B}\u{200B}", with: "\u{200B}")
|
|
||||||
// If zero-width space is next to an actual whitespace, remove the zero-width space.
|
|
||||||
.replacingOccurrences(of: " \u{200B}", with: " ")
|
|
||||||
.replacingOccurrences(of: "\u{200B} ", with: " ")
|
|
||||||
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||||
|
.replacingOccurrences(of: "\u{200B}", with: "") // these characters are added when adding mentions.
|
||||||
|
|
||||||
let imagesString = uploadedMedias.map { $0.uploadedURL.absoluteString }.joined(separator: " ")
|
let imagesString = uploadedMedias.map { $0.uploadedURL.absoluteString }.joined(separator: " ")
|
||||||
|
|
||||||
@@ -182,20 +163,9 @@ struct PostView: View {
|
|||||||
.clipShape(Capsule())
|
.clipShape(Capsule())
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEmpty() -> Bool {
|
var isEmpty: Bool {
|
||||||
return self.uploadedMedias.count == 0 &&
|
self.uploadedMedias.count == 0 &&
|
||||||
self.post.mutableString.trimmingCharacters(in: .whitespacesAndNewlines) ==
|
self.post.mutableString.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||||
initialString().mutableString.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initialString() -> NSMutableAttributedString {
|
|
||||||
guard case .posting(let target) = action,
|
|
||||||
case .user(let pubkey) = target else {
|
|
||||||
return .init(string: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
let profile = damus_state.profiles.lookup(id: pubkey)
|
|
||||||
return user_tag_attr_string(profile: profile, pubkey: pubkey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func clear_draft() {
|
func clear_draft() {
|
||||||
@@ -210,17 +180,15 @@ struct PostView: View {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func load_draft() -> Bool {
|
func load_draft() {
|
||||||
guard let draft = load_draft_for_post(drafts: self.damus_state.drafts, action: self.action) else {
|
guard let draft = load_draft_for_post(drafts: self.damus_state.drafts, action: self.action) else {
|
||||||
self.post = NSMutableAttributedString("")
|
self.post = NSMutableAttributedString("")
|
||||||
self.uploadedMedias = []
|
self.uploadedMedias = []
|
||||||
|
return
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.uploadedMedias = draft.media
|
self.uploadedMedias = draft.media
|
||||||
self.post = draft.content
|
self.post = draft.content
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func post_changed(post: NSMutableAttributedString, media: [UploadedMedia]) {
|
func post_changed(post: NSMutableAttributedString, media: [UploadedMedia]) {
|
||||||
@@ -361,11 +329,6 @@ struct PostView: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fill_target_content(target: PostTarget) {
|
|
||||||
self.post = initialString()
|
|
||||||
self.tagModel.diff = post.string.count
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { (deviceSize: GeometryProxy) in
|
GeometryReader { (deviceSize: GeometryProxy) in
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
@@ -435,7 +398,7 @@ struct PostView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
let loaded_draft = load_draft()
|
load_draft()
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .replying_to(let replying_to):
|
case .replying_to(let replying_to):
|
||||||
@@ -444,10 +407,8 @@ struct PostView: View {
|
|||||||
case .quoting(let quoting):
|
case .quoting(let quoting):
|
||||||
references = gather_quote_ids(our_pubkey: damus_state.pubkey, from: quoting)
|
references = gather_quote_ids(our_pubkey: damus_state.pubkey, from: quoting)
|
||||||
originalReferences = references
|
originalReferences = references
|
||||||
case .posting(let target):
|
case .posting:
|
||||||
guard !loaded_draft else { break }
|
break
|
||||||
|
|
||||||
fill_target_content(target: target)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
@@ -455,7 +416,7 @@ struct PostView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if isEmpty() {
|
if isEmpty {
|
||||||
clear_draft()
|
clear_draft()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,7 +456,7 @@ func get_searching_string(_ word: String?) -> String? {
|
|||||||
|
|
||||||
struct PostView_Previews: PreviewProvider {
|
struct PostView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
PostView(action: .posting(.none), damus_state: test_damus_state())
|
PostView(action: .posting, damus_state: test_damus_state())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ struct UserSearch: View {
|
|||||||
guard let pk = bech32_pubkey(user.pubkey) else {
|
guard let pk = bech32_pubkey(user.pubkey) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let tagAttributedString = user_tag_attr_string(profile: user.profile, pubkey: pk)
|
let tagAttributedString = createUserTag(for: user, with: pk)
|
||||||
appendUserTag(withTag: tagAttributedString)
|
appendUserTag(withTag: tagAttributedString)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +57,26 @@ struct UserSearch: View {
|
|||||||
newCursorIndex = wordRange.location + tagAttributedString.string.count
|
newCursorIndex = wordRange.location + tagAttributedString.string.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func createUserTag(for user: SearchedUser, with pk: String) -> NSMutableAttributedString {
|
||||||
|
let name = Profile.displayName(profile: user.profile, pubkey: pk).username.truncate(maxLength: 50)
|
||||||
|
let tagString = "@\(name)\u{200B} "
|
||||||
|
|
||||||
|
let tagAttributedString = NSMutableAttributedString(string: tagString,
|
||||||
|
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0),
|
||||||
|
NSAttributedString.Key.link: "nostr:\(pk)"])
|
||||||
|
tagAttributedString.removeAttribute(.link, range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
||||||
|
tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
||||||
|
|
||||||
|
return tagAttributedString
|
||||||
|
}
|
||||||
|
|
||||||
|
private func appendUserTag(_ tagAttributedString: NSMutableAttributedString) {
|
||||||
|
let mutableString = NSMutableAttributedString()
|
||||||
|
mutableString.append(post)
|
||||||
|
mutableString.append(tagAttributedString)
|
||||||
|
post = mutableString
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
Divider()
|
Divider()
|
||||||
@@ -148,18 +168,3 @@ func search_users_for_autocomplete(profiles: Profiles, tags: [[String]], search
|
|||||||
|
|
||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
func user_tag_attr_string(profile: Profile?, pubkey: String) -> NSMutableAttributedString {
|
|
||||||
let display_name = Profile.displayName(profile: profile, pubkey: pubkey)
|
|
||||||
let name = display_name.username.truncate(maxLength: 50)
|
|
||||||
let tagString = "@\(name)\u{200B} "
|
|
||||||
|
|
||||||
let tagAttributedString = NSMutableAttributedString(string: tagString,
|
|
||||||
attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18.0),
|
|
||||||
NSAttributedString.Key.link: "nostr:\(pubkey)"])
|
|
||||||
tagAttributedString.removeAttribute(.link, range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
|
||||||
tagAttributedString.addAttributes([NSAttributedString.Key.foregroundColor: UIColor.label], range: NSRange(location: tagAttributedString.length - 2, length: 2))
|
|
||||||
|
|
||||||
return tagAttributedString
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct MaybeAnonPfpView: View {
|
|||||||
.font(.largeTitle)
|
.font(.largeTitle)
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
} else {
|
} else {
|
||||||
NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) {
|
NavigationLink(destination: ProfileView(damus_state: state, pubkey: pubkey)) {
|
||||||
ProfilePicView(pubkey: pubkey, size: size, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation)
|
ProfilePicView(pubkey: pubkey, size: size, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,6 @@ struct MaybeAnonPfpView: View {
|
|||||||
|
|
||||||
struct MaybeAnonPfpView_Previews: PreviewProvider {
|
struct MaybeAnonPfpView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
MaybeAnonPfpView(state: test_damus_state(), is_anon: true, pubkey: ANON_PUBKEY, size: PFP_SIZE)
|
MaybeAnonPfpView(state: test_damus_state(), is_anon: true, pubkey: "anon", size: PFP_SIZE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ struct EditButton: View {
|
|||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationLink(value: Route.EditMetadata) {
|
NavigationLink(destination: EditMetadataView(damus_state: damus_state)) {
|
||||||
Text("Edit", comment: "Button to edit user's profile.")
|
Text("Edit", comment: "Button to edit user's profile.")
|
||||||
.frame(height: 30)
|
.frame(height: 30)
|
||||||
.padding(.horizontal,25)
|
.padding(.horizontal,25)
|
||||||
@@ -300,7 +300,8 @@ struct ProfileView: View {
|
|||||||
|
|
||||||
var dmButton: some View {
|
var dmButton: some View {
|
||||||
let dm_model = damus_state.dms.lookup_or_create(profile.pubkey)
|
let dm_model = damus_state.dms.lookup_or_create(profile.pubkey)
|
||||||
return NavigationLink(value: Route.DMChat(dms: dm_model)) {
|
let dmview = DMChatView(damus_state: damus_state, dms: dm_model)
|
||||||
|
return NavigationLink(destination: dmview) {
|
||||||
Image("messages")
|
Image("messages")
|
||||||
.profile_button_style(scheme: colorScheme)
|
.profile_button_style(scheme: colorScheme)
|
||||||
}
|
}
|
||||||
@@ -324,7 +325,7 @@ struct ProfileView: View {
|
|||||||
follow_state: damus_state.contacts.follow_state(profile.pubkey)
|
follow_state: damus_state.contacts.follow_state(profile.pubkey)
|
||||||
)
|
)
|
||||||
} else if damus_state.keypair.privkey != nil {
|
} else if damus_state.keypair.privkey != nil {
|
||||||
NavigationLink(value: Route.EditMetadata) {
|
NavigationLink(destination: EditMetadataView(damus_state: damus_state)) {
|
||||||
EditButton(damus_state: damus_state)
|
EditButton(damus_state: damus_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,7 +404,7 @@ struct ProfileView: View {
|
|||||||
if let contact = profile.contacts {
|
if let contact = profile.contacts {
|
||||||
let contacts = contact.referenced_pubkeys.map { $0.ref_id }
|
let contacts = contact.referenced_pubkeys.map { $0.ref_id }
|
||||||
let following_model = FollowingModel(damus_state: damus_state, contacts: contacts)
|
let following_model = FollowingModel(damus_state: damus_state, contacts: contacts)
|
||||||
NavigationLink(value: Route.Following(following: following_model)) {
|
NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model)) {
|
||||||
HStack {
|
HStack {
|
||||||
let noun_text = Text(verbatim: "\(followingCountString(profile.following))").font(.subheadline).foregroundColor(.gray)
|
let noun_text = Text(verbatim: "\(followingCountString(profile.following))").font(.subheadline).foregroundColor(.gray)
|
||||||
Text("\(Text(verbatim: profile.following.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.")
|
Text("\(Text(verbatim: profile.following.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.")
|
||||||
@@ -411,9 +412,10 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
}
|
}
|
||||||
|
let fview = FollowersView(damus_state: damus_state)
|
||||||
|
.environmentObject(followers)
|
||||||
if followers.contacts != nil {
|
if followers.contacts != nil {
|
||||||
NavigationLink(value: Route.Followers(followers: followers)) {
|
NavigationLink(destination: fview) {
|
||||||
followersCount
|
followersCount
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
@@ -431,12 +433,12 @@ struct ProfileView: View {
|
|||||||
let noun_text = Text(verbatim: relaysCountString(relays.keys.count)).font(.subheadline).foregroundColor(.gray)
|
let noun_text = Text(verbatim: relaysCountString(relays.keys.count)).font(.subheadline).foregroundColor(.gray)
|
||||||
let relay_text = Text("\(Text(verbatim: relays.keys.count.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.")
|
let relay_text = Text("\(Text(verbatim: relays.keys.count.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.")
|
||||||
if profile.pubkey == damus_state.pubkey && damus_state.is_privkey_user {
|
if profile.pubkey == damus_state.pubkey && damus_state.is_privkey_user {
|
||||||
NavigationLink(value: Route.RelayConfig) {
|
NavigationLink(destination: RelayConfigView(state: damus_state)) {
|
||||||
relay_text
|
relay_text
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
} else {
|
} else {
|
||||||
NavigationLink(value: Route.UserRelays(relays: Array(relays.keys).sorted())) {
|
NavigationLink(destination: UserRelaysView(state: damus_state, relays: Array(relays.keys).sorted())) {
|
||||||
relay_text
|
relay_text
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
@@ -449,7 +451,7 @@ struct ProfileView: View {
|
|||||||
if !friended_followers.isEmpty {
|
if !friended_followers.isEmpty {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
NavigationLink(value: Route.FollowersYouKnow(friendedFollowers: friended_followers, followers: followers)) {
|
NavigationLink(destination: FollowersYouKnowView(damus_state: damus_state, friended_followers: friended_followers)) {
|
||||||
HStack {
|
HStack {
|
||||||
CondensedProfilePicturesView(state: damus_state, pubkeys: friended_followers, maxPictures: 3)
|
CondensedProfilePicturesView(state: damus_state, pubkeys: friended_followers, maxPictures: 3)
|
||||||
Text(followedByString(friended_followers, profiles: damus_state.profiles))
|
Text(followedByString(friended_followers, profiles: damus_state.profiles))
|
||||||
@@ -464,7 +466,6 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
|
||||||
ScrollView(.vertical) {
|
ScrollView(.vertical) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
bannerSection
|
bannerSection
|
||||||
@@ -520,13 +521,6 @@ struct ProfileView: View {
|
|||||||
.fullScreenCover(isPresented: $show_qr_code) {
|
.fullScreenCover(isPresented: $show_qr_code) {
|
||||||
QRCodeView(damus_state: damus_state, pubkey: profile.pubkey)
|
QRCodeView(damus_state: damus_state, pubkey: profile.pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if damus_state.is_privkey_user {
|
|
||||||
PostButtonContainer(is_left_handed: damus_state.settings.left_handed) {
|
|
||||||
notify(.compose, PostAction.posting(.user(profile.pubkey)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,11 +47,14 @@ struct QRCodeView: View {
|
|||||||
@Environment(\.presentationMode) var presentationMode
|
@Environment(\.presentationMode) var presentationMode
|
||||||
|
|
||||||
@State private var selectedTab = 0
|
@State private var selectedTab = 0
|
||||||
|
|
||||||
@State var scanResult: ProfileScanResult? = nil
|
@State var scanResult: ProfileScanResult? = nil
|
||||||
|
|
||||||
|
@State var showProfileView: Bool = false
|
||||||
@State var profile: Profile? = nil
|
@State var profile: Profile? = nil
|
||||||
@State var error: String? = nil
|
@State var error: String? = nil
|
||||||
@State private var outerTrimEnd: CGFloat = 0
|
|
||||||
|
|
||||||
|
@State private var outerTrimEnd: CGFloat = 0
|
||||||
var animationDuration: Double = 0.5
|
var animationDuration: Double = 0.5
|
||||||
|
|
||||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||||
@@ -206,6 +209,13 @@ struct QRCodeView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
if let scanResult {
|
||||||
|
let dst = ProfileView(damus_state: damus_state, pubkey: scanResult.pubkey)
|
||||||
|
NavigationLink(destination: dst, isActive: $showProfileView) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
@@ -261,10 +271,9 @@ struct QRCodeView: View {
|
|||||||
|
|
||||||
func show_profile_after_delay() {
|
func show_profile_after_delay() {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) {
|
||||||
if let scanResult {
|
showProfileView = true
|
||||||
damus_state.nav.push(route: Route.ProfileByKey(pubkey: scanResult.pubkey))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cameraAnimate(completion: @escaping () -> Void) {
|
func cameraAnimate(completion: @escaping () -> Void) {
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import SwiftUI
|
|||||||
struct RelayFilterView: View {
|
struct RelayFilterView: View {
|
||||||
let state: DamusState
|
let state: DamusState
|
||||||
let timeline: Timeline
|
let timeline: Timeline
|
||||||
|
//@State var relays: [RelayDescriptor]
|
||||||
|
//@EnvironmentObject var user_settings: UserSettingsStore
|
||||||
|
//@State var relays: [RelayDescriptor]
|
||||||
|
|
||||||
init(state: DamusState, timeline: Timeline) {
|
init(state: DamusState, timeline: Timeline) {
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ struct RecommendedRelayView: View {
|
|||||||
Text(relay).layoutPriority(1)
|
Text(relay).layoutPriority(1)
|
||||||
|
|
||||||
if let meta = damus.relay_metadata.lookup(relay_id: relay) {
|
if let meta = damus.relay_metadata.lookup(relay_id: relay) {
|
||||||
NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta)){
|
NavigationLink ( destination:
|
||||||
|
RelayDetailView(state: damus, relay: relay, nip11: meta)
|
||||||
|
){
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
.opacity(0.0)
|
.opacity(0.0)
|
||||||
|
|||||||
@@ -72,9 +72,7 @@ struct RelayDetailView: View {
|
|||||||
|
|
||||||
if let pubkey = nip11.pubkey {
|
if let pubkey = nip11.pubkey {
|
||||||
Section(NSLocalizedString("Admin", comment: "Label to display relay contact user.")) {
|
Section(NSLocalizedString("Admin", comment: "Label to display relay contact user.")) {
|
||||||
NavigationLink(value: Route.ProfileByKey(pubkey: pubkey), label: {
|
|
||||||
UserViewRow(damus_state: state, pubkey: pubkey)
|
UserViewRow(damus_state: state, pubkey: pubkey)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let relay_connection {
|
if let relay_connection {
|
||||||
|
|||||||
@@ -30,9 +30,8 @@ struct RelayView: View {
|
|||||||
if let meta = state.relay_metadata.lookup(relay_id: relay) {
|
if let meta = state.relay_metadata.lookup(relay_id: relay) {
|
||||||
Text(relay)
|
Text(relay)
|
||||||
.background(
|
.background(
|
||||||
NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta), label: {
|
NavigationLink("", destination: RelayDetailView(state: state, relay: relay, nip11: meta)).opacity(0.0)
|
||||||
EmptyView()
|
.disabled(showActionButtons)
|
||||||
}).opacity(0.0).disabled(showActionButtons)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct SignalView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if signal.signal != signal.max_signal {
|
if signal.signal != signal.max_signal {
|
||||||
NavigationLink(value: Route.RelayConfig) {
|
NavigationLink(destination: RelayConfigView(state: state)) {
|
||||||
Text("\(signal.signal)/\(signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
|
Text("\(signal.signal)/\(signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ struct ReportView: View {
|
|||||||
|
|
||||||
@State var report_sent: Bool = false
|
@State var report_sent: Bool = false
|
||||||
@State var report_id: String = ""
|
@State var report_id: String = ""
|
||||||
@State var report_message: String = ""
|
|
||||||
@State var selected_report_type: ReportType?
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if report_sent {
|
if report_sent {
|
||||||
@@ -45,8 +43,8 @@ struct ReportView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|
||||||
func do_send_report() {
|
func do_send_report(type: ReportType) {
|
||||||
guard let selected_report_type, let ev = send_report(privkey: privkey, postbox: postbox, target: target, type: selected_report_type, message: report_message) else {
|
guard let ev = send_report(privkey: privkey, postbox: postbox, target: target, type: type) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,15 +56,6 @@ struct ReportView: View {
|
|||||||
report_id = note_id
|
report_id = note_id
|
||||||
}
|
}
|
||||||
|
|
||||||
var send_report_button_text: String {
|
|
||||||
switch target {
|
|
||||||
case .note:
|
|
||||||
return NSLocalizedString("Report Note", comment: "Button to report a note.")
|
|
||||||
case .user:
|
|
||||||
return NSLocalizedString("Report User", comment: "Button to report a user.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var MainForm: some View {
|
var MainForm: some View {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
||||||
@@ -76,27 +65,23 @@ struct ReportView: View {
|
|||||||
|
|
||||||
Form {
|
Form {
|
||||||
Section(content: {
|
Section(content: {
|
||||||
Picker("", selection: $selected_report_type) {
|
Button(NSLocalizedString("It's spam", comment: "Button for user to report that the account or content has spam.")) {
|
||||||
ForEach(ReportType.allCases, id: \.self) { report_type in
|
do_send_report(type: .spam)
|
||||||
// Impersonation type is not supported when reporting notes.
|
|
||||||
if case .note = target, report_type != .impersonation {
|
|
||||||
Text(verbatim: String(describing: report_type))
|
|
||||||
.tag(Optional(report_type))
|
|
||||||
} else if case .user = target {
|
|
||||||
Text(verbatim: String(describing: report_type))
|
|
||||||
.tag(Optional(report_type))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
.labelsHidden()
|
|
||||||
.pickerStyle(.inline)
|
|
||||||
|
|
||||||
TextField(NSLocalizedString("Additional information (optional)", comment: "Prompt to enter optional additional information when reporting an account or content."), text: $report_message, axis: .vertical)
|
Button(NSLocalizedString("Nudity or explicit content", comment: "Button for user to report that the account or content has nudity or explicit content.")) {
|
||||||
|
do_send_report(type: .explicit)
|
||||||
Button(send_report_button_text) {
|
}
|
||||||
do_send_report()
|
|
||||||
|
Button(NSLocalizedString("Illegal content", comment: "Button for user to report that the account or content has illegal content.")) {
|
||||||
|
do_send_report(type: .illegal)
|
||||||
|
}
|
||||||
|
|
||||||
|
if case .user = target {
|
||||||
|
Button(NSLocalizedString("They are impersonating someone", comment: "Button for user to report that the account is impersonating someone.")) {
|
||||||
|
do_send_report(type: .impersonation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.disabled(selected_report_type == nil)
|
|
||||||
}, header: {
|
}, header: {
|
||||||
Text("What do you want to report?", comment: "Header text to prompt user what issue they want to report.")
|
Text("What do you want to report?", comment: "Header text to prompt user what issue they want to report.")
|
||||||
}, footer: {
|
}, footer: {
|
||||||
@@ -107,8 +92,8 @@ struct ReportView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func send_report(privkey: String, postbox: PostBox, target: ReportTarget, type: ReportType, message: String) -> NostrEvent? {
|
func send_report(privkey: String, postbox: PostBox, target: ReportTarget, type: ReportType) -> NostrEvent? {
|
||||||
let report = Report(type: type, target: target, message: message)
|
let report = Report(type: type, target: target, message: "")
|
||||||
guard let ev = create_report_event(privkey: privkey, report: report) else {
|
guard let ev = create_report_event(privkey: privkey, report: report) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ struct RepostedEvent: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
let prof = damus.profiles.lookup(id: event.pubkey)
|
let prof = damus.profiles.lookup(id: event.pubkey)
|
||||||
|
let booster_profile = ProfileView(damus_state: damus, pubkey: event.pubkey)
|
||||||
|
|
||||||
NavigationLink(value: Route.ProfileByKey(pubkey: event.pubkey)) {
|
NavigationLink(destination: booster_profile) {
|
||||||
Reposted(damus: damus, pubkey: event.pubkey, profile: prof)
|
Reposted(damus: damus, pubkey: event.pubkey, profile: prof)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,12 +100,14 @@ struct SearchingEventView: View {
|
|||||||
.progressViewStyle(.circular)
|
.progressViewStyle(.circular)
|
||||||
}
|
}
|
||||||
case .found(let ev):
|
case .found(let ev):
|
||||||
NavigationLink(value: Route.Thread(thread: ThreadModel(event: ev, damus_state: state))) {
|
NavigationLink(destination: ThreadView(state: state, thread: ThreadModel(event: ev, damus_state: state))) {
|
||||||
|
|
||||||
EventView(damus: state, event: ev)
|
EventView(damus: state, event: ev)
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
case .found_profile(let pk):
|
case .found_profile(let pk):
|
||||||
NavigationLink(value: Route.ProfileByKey(pubkey: pk)) {
|
NavigationLink(destination: ProfileView(damus_state: state, pubkey: pk)) {
|
||||||
|
|
||||||
FollowUserView(target: .pubkey(pk), damus_state: state)
|
FollowUserView(target: .pubkey(pk), damus_state: state)
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
|||||||
@@ -126,6 +126,9 @@ struct SearchHomeView: View {
|
|||||||
struct SearchHomeView_Previews: PreviewProvider {
|
struct SearchHomeView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let state = test_damus_state()
|
let state = test_damus_state()
|
||||||
SearchHomeView(damus_state: state, model: SearchHomeModel(damus_state: state))
|
SearchHomeView(
|
||||||
|
damus_state: state,
|
||||||
|
model: SearchHomeModel(damus_state: state)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ struct InnerSearchResults: View {
|
|||||||
|
|
||||||
func HashtagSearch(_ ht: String) -> some View {
|
func HashtagSearch(_ ht: String) -> some View {
|
||||||
let search_model = SearchModel(state: damus_state, search: .filter_hashtag([ht]))
|
let search_model = SearchModel(state: damus_state, search: .filter_hashtag([ht]))
|
||||||
return NavigationLink(value: Route.Search(search: search_model)) {
|
let dst = SearchView(appstate: damus_state, search: search_model)
|
||||||
|
return NavigationLink(destination: dst) {
|
||||||
Text("Search hashtag: #\(ht)", comment: "Navigation link to search hashtag.")
|
Text("Search hashtag: #\(ht)", comment: "Navigation link to search hashtag.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,16 @@ func hex_col(r: UInt8, g: UInt8, b: UInt8) -> Color {
|
|||||||
|
|
||||||
|
|
||||||
struct SetupView: View {
|
struct SetupView: View {
|
||||||
@StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator()
|
@State private var eula = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack(path: $navigationCoordinator.path) {
|
NavigationView {
|
||||||
ZStack {
|
ZStack {
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
|
NavigationLink(destination: EULAView(), isActive: $eula) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Image("logo-nobg")
|
Image("logo-nobg")
|
||||||
@@ -49,7 +53,7 @@ struct SetupView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
navigationCoordinator.push(route: Route.EULA)
|
eula.toggle()
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Let's get started!", comment: "Button to continue to login page.")
|
Text("Let's get started!", comment: "Button to continue to login page.")
|
||||||
@@ -68,9 +72,6 @@ struct SetupView: View {
|
|||||||
.ignoresSafeArea(),
|
.ignoresSafeArea(),
|
||||||
alignment: .top
|
alignment: .top
|
||||||
)
|
)
|
||||||
.navigationDestination(for: Route.self) { route in
|
|
||||||
route.view(navigationCordinator: navigationCoordinator, damusState: DamusState.empty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ struct SideMenuView: View {
|
|||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
@Binding var isSidebarVisible: Bool
|
@Binding var isSidebarVisible: Bool
|
||||||
@State var confirm_logout: Bool = false
|
@State var confirm_logout: Bool = false
|
||||||
|
|
||||||
@State private var showQRCode = false
|
@State private var showQRCode = false
|
||||||
|
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@@ -44,11 +45,11 @@ struct SideMenuView: View {
|
|||||||
|
|
||||||
func SidemenuItems(profile_model: ProfileModel, followers: FollowersModel) -> some View {
|
func SidemenuItems(profile_model: ProfileModel, followers: FollowersModel) -> some View {
|
||||||
return VStack(spacing: verticalSpacing) {
|
return VStack(spacing: verticalSpacing) {
|
||||||
NavigationLink(value: Route.Profile(profile: profile_model, followers: followers)) {
|
NavigationLink(destination: ProfileView(damus_state: damus_state, profile: profile_model, followers: followers)) {
|
||||||
navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user")
|
navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user")
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.Wallet(wallet: damus_state.wallet)) {
|
NavigationLink(destination: WalletView(damus_state: damus_state, model: damus_state.wallet)) {
|
||||||
navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet")
|
navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet")
|
||||||
/*
|
/*
|
||||||
HStack {
|
HStack {
|
||||||
@@ -63,19 +64,19 @@ struct SideMenuView: View {
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.MuteList(users: get_mutelist_users(damus_state.contacts.mutelist))) {
|
NavigationLink(destination: MutelistView(damus_state: damus_state, users: get_mutelist_users(damus_state.contacts.mutelist) )) {
|
||||||
navLabel(title: NSLocalizedString("Muted", comment: "Sidebar menu label for muted users view."), img: "mute")
|
navLabel(title: NSLocalizedString("Muted", comment: "Sidebar menu label for muted users view."), img: "mute")
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.RelayConfig) {
|
NavigationLink(destination: RelayConfigView(state: damus_state)) {
|
||||||
navLabel(title: NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), img: "world-relays")
|
navLabel(title: NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), img: "world-relays")
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.Bookmarks) {
|
NavigationLink(destination: BookmarksView(state: damus_state)) {
|
||||||
navLabel(title: NSLocalizedString("Bookmarks", comment: "Sidebar menu label for Bookmarks view."), img: "bookmark")
|
navLabel(title: NSLocalizedString("Bookmarks", comment: "Sidebar menu label for Bookmarks view."), img: "bookmark")
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.Config) {
|
NavigationLink(destination: ConfigView(state: damus_state)) {
|
||||||
navLabel(title: NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), img: "settings")
|
navLabel(title: NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), img: "settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,8 @@ struct SideMenuView: View {
|
|||||||
let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey)
|
let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey)
|
||||||
let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state)
|
let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state)
|
||||||
|
|
||||||
NavigationLink(value: Route.Profile(profile: profile_model, followers: followers), label: {
|
NavigationLink(destination: ProfileView(damus_state: damus_state, profile: profile_model, followers: followers)) {
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
ProfilePicView(pubkey: damus_state.pubkey, size: 60, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
ProfilePicView(pubkey: damus_state.pubkey, size: 60, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
||||||
|
|
||||||
@@ -107,7 +109,7 @@ struct SideMenuView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, verticalSpacing)
|
.padding(.bottom, verticalSpacing)
|
||||||
})
|
}
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
|
|||||||
@@ -70,22 +70,18 @@ struct TextViewWrapper: UIViewRepresentable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func processFocusedWordForMention(textView: UITextView) {
|
private func processFocusedWordForMention(textView: UITextView) {
|
||||||
var val: (String?, NSRange?) = (nil, nil)
|
if let selectedRange = textView.selectedTextRange {
|
||||||
|
var val: (String?, NSRange?)
|
||||||
guard let selectedRange = textView.selectedTextRange else { return }
|
if let wordRange = textView.tokenizer.rangeEnclosingPosition(selectedRange.start, with: .word, inDirection: .init(rawValue: UITextLayoutDirection.left.rawValue)) {
|
||||||
|
if let startPosition = textView.position(from: wordRange.start, offset: -1),
|
||||||
let wordRange = textView.tokenizer.rangeEnclosingPosition(selectedRange.start, with: .word, inDirection: .init(rawValue: UITextLayoutDirection.left.rawValue))
|
let cursorPosition = textView.position(from: selectedRange.start, offset: 0) {
|
||||||
|
|
||||||
if let wordRange,
|
|
||||||
let startPosition = textView.position(from: wordRange.start, offset: -1),
|
|
||||||
let cursorPosition = textView.position(from: selectedRange.start, offset: 0)
|
|
||||||
{
|
|
||||||
let word = textView.text(in: textView.textRange(from: startPosition, to: cursorPosition)!)
|
let word = textView.text(in: textView.textRange(from: startPosition, to: cursorPosition)!)
|
||||||
val = (word, convertToNSRange(startPosition, cursorPosition, textView))
|
val = (word, convertToNSRange(startPosition, cursorPosition, textView))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
getFocusWordForMention?(val.0, val.1)
|
getFocusWordForMention?(val.0, val.1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func convertToNSRange( _ startPosition: UITextPosition, _ endPosition: UITextPosition, _ textView: UITextView) -> NSRange? {
|
private func convertToNSRange( _ startPosition: UITextPosition, _ endPosition: UITextPosition, _ textView: UITextView) -> NSRange? {
|
||||||
let startOffset = textView.offset(from: textView.beginningOfDocument, to: startPosition)
|
let startOffset = textView.offset(from: textView.beginningOfDocument, to: startPosition)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ struct InnerTimelineView: View {
|
|||||||
@ObservedObject var events: EventHolder
|
@ObservedObject var events: EventHolder
|
||||||
let state: DamusState
|
let state: DamusState
|
||||||
let filter: (NostrEvent) -> Bool
|
let filter: (NostrEvent) -> Bool
|
||||||
|
@State var nav_target: NostrEvent
|
||||||
|
@State var navigating: Bool = false
|
||||||
|
|
||||||
static var count: Int = 0
|
static var count: Int = 0
|
||||||
|
|
||||||
@@ -21,6 +23,8 @@ struct InnerTimelineView: View {
|
|||||||
self.filter = filter
|
self.filter = filter
|
||||||
print("rendering InnerTimelineView \(InnerTimelineView.count)")
|
print("rendering InnerTimelineView \(InnerTimelineView.count)")
|
||||||
InnerTimelineView.count += 1
|
InnerTimelineView.count += 1
|
||||||
|
// dummy event to avoid MaybeThreadView
|
||||||
|
self._nav_target = State(initialValue: test_event)
|
||||||
}
|
}
|
||||||
|
|
||||||
var event_options: EventViewOptions {
|
var event_options: EventViewOptions {
|
||||||
@@ -32,6 +36,11 @@ struct InnerTimelineView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let thread = ThreadModel(event: nav_target, damus_state: state)
|
||||||
|
let dest = ThreadView(state: state, thread: thread)
|
||||||
|
NavigationLink(destination: dest, isActive: $navigating) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
LazyVStack(spacing: 0) {
|
LazyVStack(spacing: 0) {
|
||||||
let events = self.events.events
|
let events = self.events.events
|
||||||
if events.isEmpty {
|
if events.isEmpty {
|
||||||
@@ -44,9 +53,8 @@ struct InnerTimelineView: View {
|
|||||||
let ind = tup.1
|
let ind = tup.1
|
||||||
EventView(damus: state, event: ev, options: event_options)
|
EventView(damus: state, event: ev, options: event_options)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
let event = ev.get_inner_event(cache: state.events) ?? ev
|
nav_target = ev.get_inner_event(cache: state.events) ?? ev
|
||||||
let thread = ThreadModel(event: event, damus_state: state)
|
navigating = true
|
||||||
state.nav.push(route: Route.Thread(thread: thread))
|
|
||||||
}
|
}
|
||||||
.padding(.top, 7)
|
.padding(.top, 7)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ public class VideoPlayerModel: ObservableObject {
|
|||||||
@Published var has_audio: Bool? = nil
|
@Published var has_audio: Bool? = nil
|
||||||
@Published var contentMode: UIView.ContentMode = .scaleAspectFill
|
@Published var contentMode: UIView.ContentMode = .scaleAspectFill
|
||||||
|
|
||||||
fileprivate var time: CMTime?
|
var time: CMTime = CMTime()
|
||||||
|
|
||||||
var handlers: [VideoHandler] = []
|
var handlers: [VideoHandler] = []
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -263,9 +262,8 @@ extension VideoPlayer: UIViewRepresentable {
|
|||||||
uiView.isMuted = model.muted
|
uiView.isMuted = model.muted
|
||||||
uiView.isAutoReplay = model.autoReplay
|
uiView.isAutoReplay = model.autoReplay
|
||||||
|
|
||||||
if let observerTime = context.coordinator.observerTime, let modelTime = model.time,
|
if let observerTime = context.coordinator.observerTime, model.time != observerTime {
|
||||||
modelTime != observerTime && modelTime.isValid && modelTime.isNumeric {
|
uiView.seek(to: model.time, toleranceBefore: model.time, toleranceAfter: model.time, completion: { _ in })
|
||||||
uiView.seek(to: modelTime, completion: { _ in })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ struct ConnectWalletView: View {
|
|||||||
@State var scanning: Bool = false
|
@State var scanning: Bool = false
|
||||||
@State var error: String? = nil
|
@State var error: String? = nil
|
||||||
@State var wallet_scan_result: WalletScanResult = .scanning
|
@State var wallet_scan_result: WalletScanResult = .scanning
|
||||||
var nav: NavigationCoordinator
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
MainContent
|
MainContent
|
||||||
@@ -65,12 +64,16 @@ struct ConnectWalletView: View {
|
|||||||
|
|
||||||
var ConnectWallet: some View {
|
var ConnectWallet: some View {
|
||||||
VStack {
|
VStack {
|
||||||
|
NavigationLink(destination: WalletScannerView(result: $wallet_scan_result), isActive: $scanning) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
AlbyButton() {
|
AlbyButton() {
|
||||||
openURL(URL(string:"https://nwc.getalby.com/apps/new?c=Damus")!)
|
openURL(URL(string:"https://nwc.getalby.com/apps/new?c=Damus")!)
|
||||||
}
|
}
|
||||||
|
|
||||||
BigButton(NSLocalizedString("Attach Wallet", comment: "Text for button to attach Nostr Wallet Connect lightning wallet.")) {
|
BigButton(NSLocalizedString("Attach Wallet", comment: "Text for button to attach Nostr Wallet Connect lightning wallet.")) {
|
||||||
nav.push(route: Route.WalletScanner(result: $wallet_scan_result))
|
scanning = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let err = self.error {
|
if let err = self.error {
|
||||||
@@ -96,6 +99,6 @@ struct ConnectWalletView: View {
|
|||||||
|
|
||||||
struct ConnectWalletView_Previews: PreviewProvider {
|
struct ConnectWalletView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ConnectWalletView(model: WalletModel(settings: UserSettingsStore()), nav: .init())
|
ConnectWalletView(model: WalletModel(settings: UserSettingsStore()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,9 +155,9 @@ struct WalletView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
switch model.connect_state {
|
switch model.connect_state {
|
||||||
case .new:
|
case .new:
|
||||||
ConnectWalletView(model: model, nav: damus_state.nav)
|
ConnectWalletView(model: model)
|
||||||
case .none:
|
case .none:
|
||||||
ConnectWalletView(model: model, nav: damus_state.nav)
|
ConnectWalletView(model: model)
|
||||||
case .existing(let nwc):
|
case .existing(let nwc):
|
||||||
MainWalletView(nwc: nwc)
|
MainWalletView(nwc: nwc)
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ struct ZapUserView: View {
|
|||||||
|
|
||||||
struct ZapUserView_Previews: PreviewProvider {
|
struct ZapUserView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ZapUserView(state: test_damus_state(), pubkey: ANON_PUBKEY)
|
ZapUserView(state: test_damus_state(), pubkey: "anon")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -18,22 +18,6 @@
|
|||||||
<string>... %d άλλες σημειώσεις ...</string>
|
<string>... %d άλλες σημειώσεις ...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>one</key>
|
|
||||||
<string>Σας ακολούθησε %2$@, %3$@, %4$@ & %1$d ακόμα</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>Σας ακολούθησαν %2$@, %3$@, %4$@ & %1$d ακόμα</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
@@ -333,9 +317,9 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ και %1$d ακόμα σας έστειλε zap</string>
|
<string>%2$@ και %1$d ακόμα έστειλε zap στο προφίλ σας</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ και %1$d ακόμα σας έστειλαν zap</string>
|
<string>%2$@ και %1$d ακόμα έστειλαν zap στο προφίλ σας</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zaps_count</key>
|
<key>zaps_count</key>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
<trans-unit id="%@ %@" xml:space="preserve">
|
<trans-unit id="%@ %@" xml:space="preserve">
|
||||||
<source>%@ %@</source>
|
<source>%@ %@</source>
|
||||||
<target>%@ %@</target>
|
<target>%@ %@</target>
|
||||||
<note>Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.
|
<note>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>
|
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>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="%@ has been muted" xml:space="preserve">
|
<trans-unit id="%@ has been muted" xml:space="preserve">
|
||||||
@@ -276,6 +276,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
|||||||
<note>Sidebar menu label for Bookmarks view.
|
<note>Sidebar menu label for Bookmarks view.
|
||||||
Title of bookmarks view</note>
|
Title of bookmarks view</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Boosts" xml:space="preserve">
|
||||||
|
<source>Boosts</source>
|
||||||
|
<target>Boosts</target>
|
||||||
|
<note>Accessibility label for boosts button</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Broadcast" xml:space="preserve">
|
<trans-unit id="Broadcast" xml:space="preserve">
|
||||||
<source>Broadcast</source>
|
<source>Broadcast</source>
|
||||||
<target>Broadcast</target>
|
<target>Broadcast</target>
|
||||||
@@ -571,31 +576,11 @@ Sentence composed of 2 variables to describe how many people are following a use
|
|||||||
<target>Follow me on Nostr</target>
|
<target>Follow me on Nostr</target>
|
||||||
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
|
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Followed by %@" xml:space="preserve">
|
|
||||||
<source>Followed by %@</source>
|
|
||||||
<target>Followed by %@</target>
|
|
||||||
<note>Text to indicate that the user is followed by one of our follows.</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Followed by %@ & %@" xml:space="preserve">
|
|
||||||
<source>Followed by %1$@ & %2$@</source>
|
|
||||||
<target>Followed by %1$@ & %2$@</target>
|
|
||||||
<note>Text to indicate that the user is followed by two of our follows.</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Followed by %@, %@ & %@" xml:space="preserve">
|
|
||||||
<source>Followed by %1$@, %2$@ & %3$@</source>
|
|
||||||
<target>Followed by %1$@, %2$@ & %3$@</target>
|
|
||||||
<note>Text to indicate that the user is followed by three of our follows.</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Followers" xml:space="preserve">
|
<trans-unit id="Followers" xml:space="preserve">
|
||||||
<source>Followers</source>
|
<source>Followers</source>
|
||||||
<target>Followers</target>
|
<target>Followers</target>
|
||||||
<note>Navigation bar title for view that shows who is following a user.</note>
|
<note>Navigation bar title for view that shows who is following a user.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Followers You Know" xml:space="preserve">
|
|
||||||
<source>Followers You Know</source>
|
|
||||||
<target>Followers You Know</target>
|
|
||||||
<note>Navigation bar title for view that shows who is following a user.</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Following" xml:space="preserve">
|
<trans-unit id="Following" xml:space="preserve">
|
||||||
<source>Following</source>
|
<source>Following</source>
|
||||||
<target>Following</target>
|
<target>Following</target>
|
||||||
@@ -1166,8 +1151,7 @@ Button text to indicate that the zap type is a private zap.</note>
|
|||||||
<trans-unit id="Reposts" xml:space="preserve">
|
<trans-unit id="Reposts" xml:space="preserve">
|
||||||
<source>Reposts</source>
|
<source>Reposts</source>
|
||||||
<target>Reposts</target>
|
<target>Reposts</target>
|
||||||
<note>Accessibility label for boosts button
|
<note>Navigation bar title for Reposts view.
|
||||||
Navigation bar title for Reposts view.
|
|
||||||
Setting to enable Repost Local Notification</note>
|
Setting to enable Repost Local Notification</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Requests" xml:space="preserve">
|
<trans-unit id="Requests" xml:space="preserve">
|
||||||
@@ -1246,9 +1230,9 @@ Button text to indicate that the zap type is a private zap.</note>
|
|||||||
<target>Send a message to start the conversation...</target>
|
<target>Send a message to start the conversation...</target>
|
||||||
<note>Text prompt for user to send a message to the other user.</note>
|
<note>Text prompt for user to send a message to the other user.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Send a message with your zap..." xml:space="preserve">
|
<trans-unit id="Send a reply with your zap..." xml:space="preserve">
|
||||||
<source>Send a message with your zap...</source>
|
<source>Send a reply with your zap...</source>
|
||||||
<target>Send a message with your zap...</target>
|
<target>Send a reply with your zap...</target>
|
||||||
<note>Placeholder text for a comment to send as part of a zap to the user.</note>
|
<note>Placeholder text for a comment to send as part of a zap to the user.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Server" xml:space="preserve">
|
<trans-unit id="Server" xml:space="preserve">
|
||||||
@@ -1636,13 +1620,9 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
|
|||||||
<source>Zap</source>
|
<source>Zap</source>
|
||||||
<target>Zap</target>
|
<target>Zap</target>
|
||||||
<note>Accessibility label for zap button
|
<note>Accessibility label for zap button
|
||||||
|
Button to send a zap.
|
||||||
Title of notification when a non-private zap is received.</note>
|
Title of notification when a non-private zap is received.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Zap User" xml:space="preserve">
|
|
||||||
<source>Zap User</source>
|
|
||||||
<target>Zap User</target>
|
|
||||||
<note>Button to send a zap.</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Zap Vibration" xml:space="preserve">
|
<trans-unit id="Zap Vibration" xml:space="preserve">
|
||||||
<source>Zap Vibration</source>
|
<source>Zap Vibration</source>
|
||||||
<target>Zap Vibration</target>
|
<target>Zap Vibration</target>
|
||||||
@@ -1854,21 +1834,6 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
|
|||||||
<target>%#@NOTES@</target>
|
<target>%#@NOTES@</target>
|
||||||
<note/>
|
<note/>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="/followed_by_three_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
|
||||||
<source>%#@OTHERS@</source>
|
|
||||||
<target>%#@OTHERS@</target>
|
|
||||||
<note/>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="/followed_by_three_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
|
|
||||||
<source>Followed by %2$@, %3$@, %4$@ & %1$d other</source>
|
|
||||||
<target>Followed by %2$@, %3$@, %4$@ & %1$d other</target>
|
|
||||||
<note/>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="/followed_by_three_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
|
|
||||||
<source>Followed by %2$@, %3$@, %4$@ & %1$d others</source>
|
|
||||||
<target>Followed by %2$@, %3$@, %4$@ & %1$d others</target>
|
|
||||||
<note/>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="/followers_count:dict/FOLLOWERS:dict/one:dict/:string" xml:space="preserve">
|
<trans-unit id="/followers_count:dict/FOLLOWERS:dict/one:dict/:string" xml:space="preserve">
|
||||||
<source>Follower</source>
|
<source>Follower</source>
|
||||||
<target>Follower</target>
|
<target>Follower</target>
|
||||||
|
|||||||
Binary file not shown.
@@ -18,22 +18,6 @@
|
|||||||
<string>... %d other notes ...</string>
|
<string>... %d other notes ...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>one</key>
|
|
||||||
<string>Followed by %2$@, %3$@, %4$@ & %1$d other</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>Followed by %2$@, %3$@, %4$@ & %1$d others</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -20,24 +20,6 @@
|
|||||||
<string>... %d otras notas...</string>
|
<string>... %d otras notas...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>one</key>
|
|
||||||
<string>Seguido por %2$@, %3$@, %4$@ y %1$d persona más</string>
|
|
||||||
<key>many</key>
|
|
||||||
<string>Seguido por %2$@, %3$@, %4$@ y %1$d personas más</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>Seguido por %2$@, %3$@, %4$@ y %1$d personas más</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
@@ -85,14 +67,14 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ y %1$d persona más reaccionaron a una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d persona más reaccionaron a una publicación en la que te etiquetaron</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>%2$@ y %1$d personas más reaccionaron a una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d personas más reaccionaron a una publicación en la que te etiquetaron</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ y %1$d personas más reaccionaron a una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d personas más reaccionaron a una publicación en la que te etiquetaron</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reacted_your_note_3</key>
|
<key>reacted_your_post_3</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
<string>%#@REACTED@</string>
|
<string>%#@REACTED@</string>
|
||||||
@@ -103,11 +85,11 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ y %1$d persona más reaccionaron a tu nota</string>
|
<string>%2$@ y %1$d persona más reaccionaron a tu publicación</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>%2$@ y %1$d personas más reaccionaron a tu nota</string>
|
<string>%2$@ y %1$d personas más reaccionaron a tu publicación</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ y %1$d personas más reaccionaron a tu nota</string>
|
<string>%2$@ y %1$d personas más reaccionaron a tu publicación</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reacted_your_profile_3</key>
|
<key>reacted_your_profile_3</key>
|
||||||
@@ -193,14 +175,14 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ y %1$d persona más republicaron una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d persona más republicaron una publicación en la que te etiquetaron</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>%2$@ y %1$d personas más republicaron una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d personas más republicaron una publicación en la que te etiquetaron</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ y %1$d personas más republicaron una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d personas más republicaron una publicación en la que te etiquetaron</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reposted_your_note_3</key>
|
<key>reposted_your_post_3</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
<string>%#@REPOSTED@</string>
|
<string>%#@REPOSTED@</string>
|
||||||
@@ -211,11 +193,11 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ y %1$d persona más republicaron tu nota</string>
|
<string>%2$@ y %1$d persona más republicaron tu publicación</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>%2$@ y %1$d personas más republicaron tu nota</string>
|
<string>%2$@ y %1$d personas más republicaron tu publicación</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ y %1$d personas más republicaron tu nota</string>
|
<string>%2$@ y %1$d personas más republicaron tu publicación</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reposted_your_profile_3</key>
|
<key>reposted_your_profile_3</key>
|
||||||
@@ -319,11 +301,11 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>@</string>
|
<string>@</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>Recibiste %2$@ sat de %3$@: "%4$@"</string>
|
<string>Recibiste %2$@ sat de %3$@: "%4$@"</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>Recibiste %2$@ sats de %3$@: "%4$@"</string>
|
<string>Recibiste %2$@ sats de %3$@: "%4$@"</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>Recibiste %2$@ sats de %3$@: "%4$@"</string>
|
<string>Recibiste %2$@ sats de %3$@: "%4$@"</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zapped_tagged_in_3</key>
|
<key>zapped_tagged_in_3</key>
|
||||||
@@ -337,14 +319,14 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ y %1$d persona más zapearon una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d persona más zapearon una publicación en la que te etiquetaron</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>%2$@ y %1$d personas más zapearon una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d personas más zapearon una publicación en la que te etiquetaron</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ y %1$d personas más zapearon una nota en la que te etiquetaron</string>
|
<string>%2$@ y %1$d personas más zapearon una publicación en la que te etiquetaron</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zapped_your_note_3</key>
|
<key>zapped_your_post_3</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
<string>%#@ZAPPED@</string>
|
<string>%#@ZAPPED@</string>
|
||||||
@@ -355,11 +337,11 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ y %1$d persona más zapearon tu nota</string>
|
<string>%2$@ y %1$d persona más zapearon tu publicación</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>%2$@ y %1$d personas más zapearon tu nota</string>
|
<string>%2$@ y %1$d personas más zapearon tu publicación</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ y %1$d personas más zapearon tu nota</string>
|
<string>%2$@ y %1$d personas más zapearon tu publicación</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zapped_your_profile_3</key>
|
<key>zapped_your_profile_3</key>
|
||||||
@@ -373,11 +355,11 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ y %1$d persona más te zapearon</string>
|
<string>%2$@ y %1$d persona más zapearon tu perfil</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>%2$@ y %1$d personas más te zapearon</string>
|
<string>%2$@ y %1$d personas más zapearon tu perfil</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ y %1$d personas más te zapearon</string>
|
<string>%2$@ y %1$d personas más zapearon tu perfil</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zaps_count</key>
|
<key>zaps_count</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -20,24 +20,6 @@
|
|||||||
<string>... %d otras notas ...</string>
|
<string>... %d otras notas ...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>one</key>
|
|
||||||
<string>Seguido por %2$@, %3$@, %4$@ y %1$d más</string>
|
|
||||||
<key>many</key>
|
|
||||||
<string>Seguido por %2$@, %3$@, %4$@ y %1$d más</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>Seguido por %2$@, %3$@, %4$@ y %1$d más</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
@@ -175,11 +157,11 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>Respondiendo a %2$@, %3$@ y %1$d más</string>
|
<string>Respondiendo a %2$@, %3$@ & %1$d más</string>
|
||||||
<key>many</key>
|
<key>many</key>
|
||||||
<string>Respondiendo a %2$@, %3$@ y %1$d más</string>
|
<string>Respondiendo a %2$@, %3$@ & %1$d más</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>Respondiendo a %2$@, %3$@ y %1$d más</string>
|
<string>Respondiendo a %2$@, %3$@ & %1$d más</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reposted_tagged_in_3</key>
|
<key>reposted_tagged_in_3</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -18,22 +18,6 @@
|
|||||||
<string>... %d további bejegyzések ...</string>
|
<string>... %d további bejegyzések ...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>one</key>
|
|
||||||
<string>Téged követ %2$@, %3$@, %4$@ & %1$d más</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>Téged követ %2$@, %3$@, %4$@ & %1$d mások</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
@@ -82,7 +66,7 @@
|
|||||||
<string>%2$@ és %1$d mások reagáltak egy bejegyzésre amiben meg voltál jelölve</string>
|
<string>%2$@ és %1$d mások reagáltak egy bejegyzésre amiben meg voltál jelölve</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reacted_your_note_3</key>
|
<key>reacted_your_post_3</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
<string>%#@REACTED@</string>
|
<string>%#@REACTED@</string>
|
||||||
@@ -178,7 +162,7 @@
|
|||||||
<string>%2$@ és %1$d mások megosztották azt a bejegyzést amiben meg voltál jelölve</string>
|
<string>%2$@ és %1$d mások megosztották azt a bejegyzést amiben meg voltál jelölve</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reposted_your_note_3</key>
|
<key>reposted_your_post_3</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
<string>%#@REPOSTED@</string>
|
<string>%#@REPOSTED@</string>
|
||||||
@@ -285,9 +269,9 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>@</string>
|
<string>@</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ sat kaptál %3$@: "%4$@"-tól</string>
|
<string>%2$@ sat kaptál %3$@: "%4$@"-tól</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ sats kaptál %3$@: "%4$@"-tól</string>
|
<string>%2$@ sats kaptál %3$@: "%4$@"-tól</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zapped_tagged_in_3</key>
|
<key>zapped_tagged_in_3</key>
|
||||||
@@ -306,7 +290,7 @@
|
|||||||
<string>%2$@ és %1$d mások Zap-eltek egy bejegyzést amiben meg voltál jelölve</string>
|
<string>%2$@ és %1$d mások Zap-eltek egy bejegyzést amiben meg voltál jelölve</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zapped_your_note_3</key>
|
<key>zapped_your_post_3</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
<string>%#@ZAPPED@</string>
|
<string>%#@ZAPPED@</string>
|
||||||
@@ -333,9 +317,9 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ és %1$d más Zap-elt téged</string>
|
<string>%2$@ és %1$d más Zap-elte a profilodat</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ és %1$d mások Zap-elt téged</string>
|
<string>%2$@ és %1$d mások Zap-elték a profilodat</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zaps_count</key>
|
<key>zaps_count</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -16,20 +16,6 @@
|
|||||||
<string>... 他%d件の投稿 ...</string>
|
<string>... 他%d件の投稿 ...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string> %2$@、%3$@、%4$@他%1$d人にフォローされています</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -18,22 +18,6 @@
|
|||||||
<string>... %d andere notities ...</string>
|
<string>... %d andere notities ...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>one</key>
|
|
||||||
<string>Gevolgd door %2$@, %3$@, %4$@ en %1$d andere gebruiker</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>Gevolgd door %2$@, %3$@, %4$@ en %1$d andere gebruikers</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -18,22 +18,6 @@
|
|||||||
<string>...%d andra anteckningar...</string>
|
<string>...%d andra anteckningar...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>one</key>
|
|
||||||
<string>Följd av %2$@, %3$@, %4$@ & %1$d andra</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>Följd av %2$@, %3$@, %4$@ & %1$d andra</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
@@ -333,9 +317,9 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%2$@ och %1$d andra zapped dig</string>
|
<string>%2$@ och %1$d annan zappade din profil</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%2$@ och %1$d andra zapped dig</string>
|
<string>%2$@ och %1$d andra zappade din profil</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>zaps_count</key>
|
<key>zaps_count</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -16,20 +16,6 @@
|
|||||||
<string>... %d 条更多笔记...</string>
|
<string>... %d 条更多笔记...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>被 %2$@、%3$@、%4$@ 和 %1$d 个其他用户关注</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
@@ -139,7 +125,7 @@
|
|||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>d</string>
|
<string>d</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>正在回复%2$@, %3$@ 和 %1$d 个其他用户</string>
|
<string>正在回复%2$@, %3$@ & %1$d 个其他用户</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>reposted_tagged_in_3</key>
|
<key>reposted_tagged_in_3</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -16,20 +16,6 @@
|
|||||||
<string>...還有%d 条筆記...</string>
|
<string>...還有%d 条筆記...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>被 %2$@、%3$@、%4$@ 和 %1$d 個其他用戶關注</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -16,20 +16,6 @@
|
|||||||
<string>...還有%d 条筆記...</string>
|
<string>...還有%d 条筆記...</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>followed_by_three_and_others</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
|
||||||
<string>%#@OTHERS@</string>
|
|
||||||
<key>OTHERS</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSStringFormatSpecTypeKey</key>
|
|
||||||
<string>NSStringPluralRuleType</string>
|
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
|
||||||
<string>d</string>
|
|
||||||
<key>other</key>
|
|
||||||
<string>被 %2$@、%3$@、%4$@ 和 %1$d 個其他用戶關注</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>followers_count</key>
|
<key>followers_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
@@ -18,27 +18,6 @@ final class EventGroupViewTests: XCTestCase {
|
|||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEventAuthorName() {
|
|
||||||
let damusState = test_damus_state()
|
|
||||||
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "pk1"), "pk1:pk1")
|
|
||||||
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "pk2"), "pk2:pk2")
|
|
||||||
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "anon"), "Anonymous")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testEventGroupUniquePubkeys() {
|
|
||||||
let damusState = test_damus_state()
|
|
||||||
|
|
||||||
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
|
|
||||||
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
|
||||||
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
|
||||||
let repost3 = NostrEvent(id: "", content: encodedPost, pubkey: "pk3", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
|
||||||
|
|
||||||
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: []))), [])
|
|
||||||
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1]))), ["pk1"])
|
|
||||||
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1, repost2]))), ["pk1", "pk2"])
|
|
||||||
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1, repost2, repost3]))), ["pk1", "pk2", "pk3"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testReactingToText() throws {
|
func testReactingToText() throws {
|
||||||
let enUsLocale = Locale(identifier: "en-US")
|
let enUsLocale = Locale(identifier: "en-US")
|
||||||
let damusState = test_damus_state()
|
let damusState = test_damus_state()
|
||||||
@@ -46,19 +25,18 @@ final class EventGroupViewTests: XCTestCase {
|
|||||||
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
|
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
|
||||||
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
||||||
let repost3 = NostrEvent(id: "", content: encodedPost, pubkey: "pk3", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
|
|
||||||
|
|
||||||
let nozaps = true
|
let nozaps = true
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, pubkeys: [], locale: enUsLocale), "??")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "??")
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, pubkeys: ["pk1"], locale: enUsLocale), "pk1:pk1 reposted a note you were tagged in")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 reposted a note you were tagged in")
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, pubkeys: ["pk1", "pk2"], locale: enUsLocale), "pk1:pk1 and pk2:pk2 reposted a note you were tagged in")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 and pk2:pk2 reposted a note you were tagged in")
|
||||||
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, pubkeys: ["pk1", "pk2", "pk3"], locale: enUsLocale), "pk1:pk1 and 2 others reposted a note you were tagged in")
|
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 and 2 others reposted a note you were tagged in")
|
||||||
|
|
||||||
Bundle.main.localizations.map { Locale(identifier: $0) }.forEach {
|
Bundle.main.localizations.map { Locale(identifier: $0) }.forEach {
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, pubkeys: [], locale: $0), "??")
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, locale: $0), "??")
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, pubkeys: ["pk1"], locale: $0))
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, locale: $0))
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, pubkeys: ["pk1", "pk2"], locale: $0))
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, locale: $0))
|
||||||
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost3])), ev: test_event, pubkeys: ["pk1", "pk2", "pk3"], locale: $0))
|
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, nozaps: nozaps, locale: $0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user