Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
8678ca7144
|
@@ -94,33 +94,10 @@ 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
|
||||||
|
|
||||||
### Privacy
|
### Privacy
|
||||||
|
|||||||
@@ -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,19 +114,17 @@ 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)
|
.resizable()
|
||||||
.resizable()
|
.foregroundColor(zap_color)
|
||||||
.foregroundColor(zap_color)
|
.font(.footnote.weight(.medium))
|
||||||
.font(.footnote.weight(.medium))
|
.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 {
|
||||||
tap()
|
present_sheet(.zap(target: target, lnurl: lnurl))
|
||||||
|
} else {
|
||||||
|
// otherwise we restore the original behavior of one-tap zaps
|
||||||
|
tap()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-22
@@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,16 @@ struct ConfigView: View {
|
|||||||
@State var delete_account_warning: Bool = false
|
@State var delete_account_warning: Bool = false
|
||||||
@State var confirm_delete_account: Bool = false
|
@State var confirm_delete_account: Bool = false
|
||||||
@State var delete_text: String = ""
|
@State var delete_text: String = ""
|
||||||
|
|
||||||
@ObservedObject var settings: UserSettingsStore
|
@ObservedObject var settings: UserSettingsStore
|
||||||
|
|
||||||
private let DELETE_KEYWORD = "DELETE"
|
private let DELETE_KEYWORD = "DELETE"
|
||||||
|
|
||||||
init(state: DamusState) {
|
init(state: DamusState) {
|
||||||
self.state = state
|
self.state = state
|
||||||
_settings = ObservedObject(initialValue: state.settings)
|
_settings = ObservedObject(initialValue: state.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
func textColor() -> Color {
|
func textColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.black : DamusColors.white
|
colorScheme == .light ? DamusColors.black : DamusColors.white
|
||||||
}
|
}
|
||||||
@@ -36,30 +36,31 @@ 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: {
|
||||||
@@ -115,11 +116,11 @@ struct ConfigView: View {
|
|||||||
guard let full_kp = state.keypair.to_full() else {
|
guard let full_kp = state.keypair.to_full() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard delete_text == DELETE_KEYWORD else {
|
guard delete_text == DELETE_KEYWORD else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let ev = created_deleted_account_profile(keypair: full_kp)
|
let ev = created_deleted_account_profile(keypair: full_kp)
|
||||||
state.postbox.send(ev)
|
state.postbox.send(ev)
|
||||||
notify(.logout, ())
|
notify(.logout, ())
|
||||||
@@ -163,7 +164,7 @@ func handle_string_amount(new_value: String) -> Int? {
|
|||||||
guard let amt = NumberFormatter().number(from: filtered) as? Int else {
|
guard let amt = NumberFormatter().number(from: filtered) as? Int else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return amt
|
return amt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,13 @@ struct DirectMessagesView: View {
|
|||||||
@State var dm_type: DMType = .friend
|
@State var dm_type: DMType = .friend
|
||||||
@ObservedObject var model: DirectMessagesModel
|
@ObservedObject var model: DirectMessagesModel
|
||||||
@ObservedObject var settings: UserSettingsStore
|
@ObservedObject var settings: UserSettingsStore
|
||||||
|
|
||||||
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 {
|
||||||
@@ -71,7 +76,6 @@ struct FollowingView: View {
|
|||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
|
|
||||||
let following: FollowingModel
|
let following: FollowingModel
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|||||||
@@ -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 {
|
if zap.is_anon {
|
||||||
let zap = zapgrp.zaps[i]
|
return NSLocalizedString("Anonymous", comment: "Placeholder author name of the anonymous person who zapped an event.")
|
||||||
let pubkey: String
|
|
||||||
|
|
||||||
if zap.is_anon {
|
|
||||||
pubkey = ANON_PUBKEY
|
|
||||||
} else {
|
|
||||||
pubkey = zap.request.ev.pubkey
|
|
||||||
}
|
|
||||||
|
|
||||||
if !seen.contains(pubkey) {
|
|
||||||
seen.insert(pubkey)
|
|
||||||
sorted.append(pubkey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return event_author_name(profiles: profiles, pubkey: zap.request.ev.pubkey)
|
||||||
} else {
|
} else {
|
||||||
let events = group.events
|
let ev = group.events[ind]
|
||||||
|
return event_author_name(profiles: profiles, pubkey: ev.pubkey)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -192,9 +174,9 @@ struct EventGroupView: View {
|
|||||||
let state: DamusState
|
let state: DamusState
|
||||||
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,24 +216,25 @@ 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)
|
||||||
VStack(alignment: .leading) {
|
GroupDescription
|
||||||
GroupDescription(unique_pubkeys)
|
if !state.settings.nozaps || !group.is_note_zap {
|
||||||
EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content])
|
NavigationLink(destination: dest) {
|
||||||
.padding([.top], 1)
|
VStack(alignment: .leading) {
|
||||||
.padding([.trailing])
|
EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content])
|
||||||
.foregroundColor(.gray)
|
.padding([.top], 1)
|
||||||
|
.padding([.trailing])
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.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)
|
||||||
|
|||||||
@@ -10,13 +10,20 @@ import SwiftUI
|
|||||||
struct ProfilePicturesView: View {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-63
@@ -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: " ")
|
||||||
|
|
||||||
@@ -130,14 +111,6 @@ struct PostView: View {
|
|||||||
var is_post_empty: Bool {
|
var is_post_empty: Bool {
|
||||||
return post.string.allSatisfy { $0.isWhitespace } && uploadedMedias.isEmpty
|
return post.string.allSatisfy { $0.isWhitespace } && uploadedMedias.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
var uploading_disabled: Bool {
|
|
||||||
return image_upload.progress != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var posting_disabled: Bool {
|
|
||||||
return is_post_empty || uploading_disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
var ImageButton: some View {
|
var ImageButton: some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
@@ -162,7 +135,7 @@ struct PostView: View {
|
|||||||
ImageButton
|
ImageButton
|
||||||
CameraButton
|
CameraButton
|
||||||
}
|
}
|
||||||
.disabled(uploading_disabled)
|
.disabled(image_upload.progress != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
var PostButton: some View {
|
var PostButton: some View {
|
||||||
@@ -173,29 +146,18 @@ struct PostView: View {
|
|||||||
self.send_post()
|
self.send_post()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.disabled(posting_disabled)
|
.disabled(is_post_empty)
|
||||||
.font(.system(size: 14, weight: .bold))
|
.font(.system(size: 14, weight: .bold))
|
||||||
.frame(width: 80, height: 30)
|
.frame(width: 80, height: 30)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.background(LINEAR_GRADIENT)
|
.background(LINEAR_GRADIENT)
|
||||||
.opacity(posting_disabled ? 0.5 : 1.0)
|
.opacity(is_post_empty ? 0.5 : 1.0)
|
||||||
.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 +172,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 +321,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 +390,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 +399,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 +408,7 @@ struct PostView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if isEmpty() {
|
if isEmpty {
|
||||||
clear_draft()
|
clear_draft()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,7 +448,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,11 +73,11 @@ func followedByString(_ friend_intersection: [String], profiles: Profiles, local
|
|||||||
|
|
||||||
struct EditButton: View {
|
struct EditButton: View {
|
||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
|
|
||||||
@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)
|
||||||
@@ -92,11 +92,11 @@ struct EditButton: View {
|
|||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillColor() -> Color {
|
func fillColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.black : DamusColors.white
|
colorScheme == .light ? DamusColors.black : DamusColors.white
|
||||||
}
|
}
|
||||||
|
|
||||||
func borderColor() -> Color {
|
func borderColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.black : DamusColors.white
|
colorScheme == .light ? DamusColors.black : DamusColors.white
|
||||||
}
|
}
|
||||||
@@ -104,11 +104,11 @@ struct EditButton: View {
|
|||||||
|
|
||||||
struct VisualEffectView: UIViewRepresentable {
|
struct VisualEffectView: UIViewRepresentable {
|
||||||
var effect: UIVisualEffect?
|
var effect: UIVisualEffect?
|
||||||
|
|
||||||
func makeUIView(context: UIViewRepresentableContext<Self>) -> UIVisualEffectView {
|
func makeUIView(context: UIViewRepresentableContext<Self>) -> UIVisualEffectView {
|
||||||
UIVisualEffectView()
|
UIVisualEffectView()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext<Self>) {
|
func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext<Self>) {
|
||||||
uiView.effect = effect
|
uiView.effect = effect
|
||||||
}
|
}
|
||||||
@@ -120,52 +120,52 @@ struct ProfileView: View {
|
|||||||
let bannerHeight: CGFloat = 150.0
|
let bannerHeight: CGFloat = 150.0
|
||||||
|
|
||||||
static let markdown = Markdown()
|
static let markdown = Markdown()
|
||||||
|
|
||||||
@State var is_zoomed: Bool = false
|
@State var is_zoomed: Bool = false
|
||||||
@State var show_share_sheet: Bool = false
|
@State var show_share_sheet: Bool = false
|
||||||
@State var show_qr_code: Bool = false
|
@State var show_qr_code: Bool = false
|
||||||
@State var action_sheet_presented: Bool = false
|
@State var action_sheet_presented: Bool = false
|
||||||
@State var filter_state : FilterState = .posts
|
@State var filter_state : FilterState = .posts
|
||||||
@State var yOffset: CGFloat = 0
|
@State var yOffset: CGFloat = 0
|
||||||
|
|
||||||
@StateObject var profile: ProfileModel
|
@StateObject var profile: ProfileModel
|
||||||
@StateObject var followers: FollowersModel
|
@StateObject var followers: FollowersModel
|
||||||
@StateObject var zap_button_model: ZapButtonModel = ZapButtonModel()
|
@StateObject var zap_button_model: ZapButtonModel = ZapButtonModel()
|
||||||
|
|
||||||
init(damus_state: DamusState, profile: ProfileModel, followers: FollowersModel) {
|
init(damus_state: DamusState, profile: ProfileModel, followers: FollowersModel) {
|
||||||
self.damus_state = damus_state
|
self.damus_state = damus_state
|
||||||
self._profile = StateObject(wrappedValue: profile)
|
self._profile = StateObject(wrappedValue: profile)
|
||||||
self._followers = StateObject(wrappedValue: followers)
|
self._followers = StateObject(wrappedValue: followers)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(damus_state: DamusState, pubkey: String) {
|
init(damus_state: DamusState, pubkey: String) {
|
||||||
self.damus_state = damus_state
|
self.damus_state = damus_state
|
||||||
self._profile = StateObject(wrappedValue: ProfileModel(pubkey: pubkey, damus: damus_state))
|
self._profile = StateObject(wrappedValue: ProfileModel(pubkey: pubkey, damus: damus_state))
|
||||||
self._followers = StateObject(wrappedValue: FollowersModel(damus_state: damus_state, target: pubkey))
|
self._followers = StateObject(wrappedValue: FollowersModel(damus_state: damus_state, target: pubkey))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@Environment(\.presentationMode) var presentationMode
|
@Environment(\.presentationMode) var presentationMode
|
||||||
|
|
||||||
func imageBorderColor() -> Color {
|
func imageBorderColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.white : DamusColors.black
|
colorScheme == .light ? DamusColors.white : DamusColors.black
|
||||||
}
|
}
|
||||||
|
|
||||||
func bannerBlurViewOpacity() -> Double {
|
func bannerBlurViewOpacity() -> Double {
|
||||||
let progress = -(yOffset + navbarHeight) / 100
|
let progress = -(yOffset + navbarHeight) / 100
|
||||||
return Double(-yOffset > navbarHeight ? progress : 0)
|
return Double(-yOffset > navbarHeight ? progress : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var bannerSection: some View {
|
var bannerSection: some View {
|
||||||
GeometryReader { proxy -> AnyView in
|
GeometryReader { proxy -> AnyView in
|
||||||
|
|
||||||
let minY = proxy.frame(in: .global).minY
|
let minY = proxy.frame(in: .global).minY
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.yOffset = minY
|
self.yOffset = minY
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnyView(
|
return AnyView(
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ZStack {
|
ZStack {
|
||||||
@@ -173,10 +173,10 @@ struct ProfileView: View {
|
|||||||
.aspectRatio(contentMode: .fill)
|
.aspectRatio(contentMode: .fill)
|
||||||
.frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight)
|
.frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight)
|
||||||
.clipped()
|
.clipped()
|
||||||
|
|
||||||
VisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial)).opacity(bannerBlurViewOpacity())
|
VisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial)).opacity(bannerBlurViewOpacity())
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider().opacity(bannerBlurViewOpacity())
|
Divider().opacity(bannerBlurViewOpacity())
|
||||||
}
|
}
|
||||||
.frame(height: minY > 0 ? bannerHeight + minY : nil)
|
.frame(height: minY > 0 ? bannerHeight + minY : nil)
|
||||||
@@ -187,11 +187,11 @@ struct ProfileView: View {
|
|||||||
.frame(height: bannerHeight)
|
.frame(height: bannerHeight)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var navbarHeight: CGFloat {
|
var navbarHeight: CGFloat {
|
||||||
return 100.0 - (Theme.safeAreaInsets?.top ?? 0)
|
return 100.0 - (Theme.safeAreaInsets?.top ?? 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func navImage(img: String) -> some View {
|
func navImage(img: String) -> some View {
|
||||||
Image(img)
|
Image(img)
|
||||||
@@ -199,7 +199,7 @@ struct ProfileView: View {
|
|||||||
.background(Color.black.opacity(0.6))
|
.background(Color.black.opacity(0.6))
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
}
|
}
|
||||||
|
|
||||||
var navBackButton: some View {
|
var navBackButton: some View {
|
||||||
Button {
|
Button {
|
||||||
presentationMode.wrappedValue.dismiss()
|
presentationMode.wrappedValue.dismiss()
|
||||||
@@ -207,7 +207,7 @@ struct ProfileView: View {
|
|||||||
navImage(img: "chevron-left")
|
navImage(img: "chevron-left")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var navActionSheetButton: some View {
|
var navActionSheetButton: some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
action_sheet_presented = true
|
action_sheet_presented = true
|
||||||
@@ -218,7 +218,7 @@ struct ProfileView: View {
|
|||||||
Button(NSLocalizedString("Share", comment: "Button to share the link to a profile.")) {
|
Button(NSLocalizedString("Share", comment: "Button to share the link to a profile.")) {
|
||||||
show_share_sheet = true
|
show_share_sheet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(NSLocalizedString("QR Code", comment: "Button to view profile's qr code.")) {
|
Button(NSLocalizedString("QR Code", comment: "Button to view profile's qr code.")) {
|
||||||
show_qr_code = true
|
show_qr_code = true
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ struct ProfileView: View {
|
|||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let new_ev = remove_from_mutelist(keypair: keypair, prev: mutelist, to_remove: profile.pubkey) else {
|
guard let new_ev = remove_from_mutelist(keypair: keypair, prev: mutelist, to_remove: profile.pubkey) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -254,7 +254,7 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var customNavbar: some View {
|
var customNavbar: some View {
|
||||||
HStack {
|
HStack {
|
||||||
navBackButton
|
navBackButton
|
||||||
@@ -265,7 +265,7 @@ struct ProfileView: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.accentColor(DamusColors.white)
|
.accentColor(DamusColors.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
func lnButton(lnurl: String, profile: Profile) -> some View {
|
func lnButton(lnurl: String, profile: Profile) -> some View {
|
||||||
let button_img = profile.reactions == false ? "zap.fill" : "zap"
|
let button_img = profile.reactions == false ? "zap.fill" : "zap"
|
||||||
return Button(action: {
|
return Button(action: {
|
||||||
@@ -278,7 +278,7 @@ struct ProfileView: View {
|
|||||||
if profile.reactions == false {
|
if profile.reactions == false {
|
||||||
Text("OnlyZaps Enabled", comment: "Non-tappable text in context menu that shows up when the zap button on profile is long pressed to indicate that the user has enabled OnlyZaps, meaning that they would like to be only zapped and not accept reactions to their notes.")
|
Text("OnlyZaps Enabled", comment: "Non-tappable text in context menu that shows up when the zap button on profile is long pressed to indicate that the user has enabled OnlyZaps, meaning that they would like to be only zapped and not accept reactions to their notes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let addr = profile.lud16 {
|
if let addr = profile.lud16 {
|
||||||
Button {
|
Button {
|
||||||
UIPasteboard.general.string = addr
|
UIPasteboard.general.string = addr
|
||||||
@@ -293,30 +293,31 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.cornerRadius(24)
|
.cornerRadius(24)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func actionSection(profile_data: Profile?) -> some View {
|
func actionSection(profile_data: Profile?) -> some View {
|
||||||
return Group {
|
return Group {
|
||||||
|
|
||||||
if let profile = profile_data {
|
if let profile = profile_data {
|
||||||
if let lnurl = profile.lnurl, lnurl != "" {
|
if let lnurl = profile.lnurl, lnurl != "" {
|
||||||
lnButton(lnurl: lnurl, profile: profile)
|
lnButton(lnurl: lnurl, profile: profile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dmButton
|
dmButton
|
||||||
|
|
||||||
if profile.pubkey != damus_state.pubkey {
|
if profile.pubkey != damus_state.pubkey {
|
||||||
FollowButtonView(
|
FollowButtonView(
|
||||||
target: profile.get_follow_target(),
|
target: profile.get_follow_target(),
|
||||||
@@ -324,26 +325,26 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pfpOffset() -> CGFloat {
|
func pfpOffset() -> CGFloat {
|
||||||
let progress = -yOffset / navbarHeight
|
let progress = -yOffset / navbarHeight
|
||||||
let offset = (pfp_size / 4.0) * (progress < 1.0 ? progress : 1)
|
let offset = (pfp_size / 4.0) * (progress < 1.0 ? progress : 1)
|
||||||
return offset > 0 ? offset : 0
|
return offset > 0 ? offset : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func pfpScale() -> CGFloat {
|
func pfpScale() -> CGFloat {
|
||||||
let progress = -yOffset / navbarHeight
|
let progress = -yOffset / navbarHeight
|
||||||
let scale = 1.0 - (0.5 * (progress < 1.0 ? progress : 1))
|
let scale = 1.0 - (0.5 * (progress < 1.0 ? progress : 1))
|
||||||
return scale < 1 ? scale : 1
|
return scale < 1 ? scale : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func nameSection(profile_data: Profile?) -> some View {
|
func nameSection(profile_data: Profile?) -> some View {
|
||||||
return Group {
|
return Group {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
@@ -357,17 +358,17 @@ struct ProfileView: View {
|
|||||||
.fullScreenCover(isPresented: $is_zoomed) {
|
.fullScreenCover(isPresented: $is_zoomed) {
|
||||||
ProfilePicImageView(pubkey: profile.pubkey, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
ProfilePicImageView(pubkey: profile.pubkey, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
actionSection(profile_data: profile_data)
|
actionSection(profile_data: profile_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
let follows_you = profile.pubkey != damus_state.pubkey && profile.follows(pubkey: damus_state.pubkey)
|
let follows_you = profile.pubkey != damus_state.pubkey && profile.follows(pubkey: damus_state.pubkey)
|
||||||
ProfileNameView(pubkey: profile.pubkey, profile: profile_data, follows_you: follows_you, damus: damus_state)
|
ProfileNameView(pubkey: profile.pubkey, profile: profile_data, follows_you: follows_you, damus: damus_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var followersCount: some View {
|
var followersCount: some View {
|
||||||
HStack {
|
HStack {
|
||||||
if followers.count == nil {
|
if followers.count == nil {
|
||||||
@@ -384,26 +385,26 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var aboutSection: some View {
|
var aboutSection: some View {
|
||||||
VStack(alignment: .leading, spacing: 8.0) {
|
VStack(alignment: .leading, spacing: 8.0) {
|
||||||
let profile_data = damus_state.profiles.lookup(id: profile.pubkey)
|
let profile_data = damus_state.profiles.lookup(id: profile.pubkey)
|
||||||
|
|
||||||
nameSection(profile_data: profile_data)
|
nameSection(profile_data: profile_data)
|
||||||
|
|
||||||
if let about = profile_data?.about {
|
if let about = profile_data?.about {
|
||||||
AboutView(state: damus_state, about: about)
|
AboutView(state: damus_state, about: about)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let url = profile_data?.website_url {
|
if let url = profile_data?.website_url {
|
||||||
WebsiteLink(url: url)
|
WebsiteLink(url: url)
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
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())
|
||||||
@@ -425,18 +427,18 @@ struct ProfileView: View {
|
|||||||
followers.subscribe()
|
followers.subscribe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let relays = profile.relays {
|
if let relays = profile.relays {
|
||||||
// Only open relay config view if the user is logged in with private key and they are looking at their own profile.
|
// Only open relay config view if the user is logged in with private key and they are looking at their own profile.
|
||||||
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))
|
||||||
@@ -462,7 +464,7 @@ struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
ScrollView(.vertical) {
|
ScrollView(.vertical) {
|
||||||
@@ -521,9 +523,9 @@ struct ProfileView: View {
|
|||||||
QRCodeView(damus_state: damus_state, pubkey: profile.pubkey)
|
QRCodeView(damus_state: damus_state, pubkey: profile.pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if damus_state.is_privkey_user {
|
if profile.pubkey == damus_state.pubkey && damus_state.is_privkey_user {
|
||||||
PostButtonContainer(is_left_handed: damus_state.settings.left_handed) {
|
PostButtonContainer(is_left_handed: damus_state.settings.left_handed) {
|
||||||
notify(.compose, PostAction.posting(.user(profile.pubkey)))
|
notify(.compose, PostAction.posting)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,7 +542,7 @@ struct ProfileView_Previews: PreviewProvider {
|
|||||||
func test_damus_state() -> DamusState {
|
func test_damus_state() -> DamusState {
|
||||||
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||||
let damus = DamusState.empty
|
let damus = DamusState.empty
|
||||||
|
|
||||||
let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io", damus_donation: nil)
|
let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io", damus_donation: nil)
|
||||||
let tsprof = TimestampedProfile(profile: prof, timestamp: 0, event: test_event)
|
let tsprof = TimestampedProfile(profile: prof, timestamp: 0, event: test_event)
|
||||||
damus.profiles.add(id: pubkey, profile: tsprof)
|
damus.profiles.add(id: pubkey, profile: tsprof)
|
||||||
@@ -549,15 +551,15 @@ func test_damus_state() -> DamusState {
|
|||||||
|
|
||||||
struct KeyView: View {
|
struct KeyView: View {
|
||||||
let pubkey: String
|
let pubkey: String
|
||||||
|
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
|
||||||
@State private var isCopied = false
|
@State private var isCopied = false
|
||||||
|
|
||||||
func keyColor() -> Color {
|
func keyColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.black : DamusColors.white
|
colorScheme == .light ? DamusColors.black : DamusColors.white
|
||||||
}
|
}
|
||||||
|
|
||||||
private func copyPubkey(_ pubkey: String) {
|
private func copyPubkey(_ pubkey: String) {
|
||||||
UIPasteboard.general.string = pubkey
|
UIPasteboard.general.string = pubkey
|
||||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||||
@@ -570,10 +572,10 @@ struct KeyView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let bech32 = bech32_pubkey(pubkey) ?? pubkey
|
let bech32 = bech32_pubkey(pubkey) ?? pubkey
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Text(verbatim: "\(abbrev_pubkey(bech32, amount: 16))")
|
Text(verbatim: "\(abbrev_pubkey(bech32, amount: 16))")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
@@ -581,7 +583,7 @@ struct KeyView: View {
|
|||||||
.padding(5)
|
.padding(5)
|
||||||
.padding([.leading, .trailing], 5)
|
.padding([.leading, .trailing], 5)
|
||||||
.background(RoundedRectangle(cornerRadius: 11).foregroundColor(DamusColors.adaptableGrey))
|
.background(RoundedRectangle(cornerRadius: 11).foregroundColor(DamusColors.adaptableGrey))
|
||||||
|
|
||||||
if isCopied != true {
|
if isCopied != true {
|
||||||
Button {
|
Button {
|
||||||
copyPubkey(bech32)
|
copyPubkey(bech32)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,15 +55,6 @@ struct ReportView: View {
|
|||||||
report_sent = true
|
report_sent = true
|
||||||
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))
|
Button(NSLocalizedString("Nudity or explicit content", comment: "Button for user to report that the account or content has nudity or explicit content.")) {
|
||||||
.tag(Optional(report_type))
|
do_send_report(type: .explicit)
|
||||||
} else if case .user = target {
|
}
|
||||||
Text(verbatim: String(describing: report_type))
|
|
||||||
.tag(Optional(report_type))
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.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(send_report_button_text) {
|
|
||||||
do_send_report()
|
|
||||||
}
|
|
||||||
.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,22 +11,23 @@ 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
|
||||||
|
|
||||||
var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0)
|
var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0)
|
||||||
let verticalSpacing: CGFloat = 20
|
let verticalSpacing: CGFloat = 20
|
||||||
let padding: CGFloat = 30
|
let padding: CGFloat = 30
|
||||||
|
|
||||||
func fillColor() -> Color {
|
func fillColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.white : DamusColors.black
|
colorScheme == .light ? DamusColors.white : DamusColors.black
|
||||||
}
|
}
|
||||||
|
|
||||||
func textColor() -> Color {
|
func textColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.black : DamusColors.white
|
colorScheme == .light ? DamusColors.black : DamusColors.white
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
GeometryReader { _ in
|
GeometryReader { _ in
|
||||||
@@ -41,20 +42,20 @@ struct SideMenuView: View {
|
|||||||
content
|
content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
Image("wallet")
|
Image("wallet")
|
||||||
.tint(DamusColors.adaptableBlack)
|
.tint(DamusColors.adaptableBlack)
|
||||||
|
|
||||||
Text(NSLocalizedString("wallet", comment: "Sidebar menu label for Wallet view."))
|
Text(NSLocalizedString("wallet", comment: "Sidebar menu label for Wallet view."))
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.foregroundColor(textColor())
|
.foregroundColor(textColor())
|
||||||
@@ -62,35 +63,36 @@ struct SideMenuView: View {
|
|||||||
.dynamicTypeSize(.xSmall)
|
.dynamicTypeSize(.xSmall)
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var MainSidemenu: some View {
|
var MainSidemenu: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
let profile = damus_state.profiles.lookup(id: damus_state.pubkey)
|
let profile = damus_state.profiles.lookup(id: damus_state.pubkey)
|
||||||
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)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
if let display_name = profile?.display_name {
|
if let display_name = profile?.display_name {
|
||||||
Text(display_name)
|
Text(display_name)
|
||||||
@@ -107,10 +109,10 @@ struct SideMenuView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, verticalSpacing)
|
.padding(.bottom, verticalSpacing)
|
||||||
})
|
}
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
SidemenuItems(profile_model: profile_model, followers: followers)
|
SidemenuItems(profile_model: profile_model, followers: followers)
|
||||||
.labelStyle(SideMenuLabelStyle())
|
.labelStyle(SideMenuLabelStyle())
|
||||||
@@ -118,21 +120,21 @@ struct SideMenuView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var content: some View {
|
var content: some View {
|
||||||
HStack(alignment: .top) {
|
HStack(alignment: .top) {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
fillColor()
|
fillColor()
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
MainSidemenu
|
MainSidemenu
|
||||||
.simultaneousGesture(TapGesture().onEnded {
|
.simultaneousGesture(TapGesture().onEnded {
|
||||||
isSidebarVisible = false
|
isSidebarVisible = false
|
||||||
})
|
})
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
HStack() {
|
HStack() {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
//ConfigView(state: damus_state)
|
//ConfigView(state: damus_state)
|
||||||
@@ -148,9 +150,9 @@ struct SideMenuView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.dynamicTypeSize(.xSmall)
|
.dynamicTypeSize(.xSmall)
|
||||||
})
|
})
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
showQRCode.toggle()
|
showQRCode.toggle()
|
||||||
}, label: {
|
}, label: {
|
||||||
@@ -184,20 +186,20 @@ struct SideMenuView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func navLabel(title: String, img: String) -> some View {
|
func navLabel(title: String, img: String) -> some View {
|
||||||
Image(img)
|
Image(img)
|
||||||
.tint(DamusColors.adaptableBlack)
|
.tint(DamusColors.adaptableBlack)
|
||||||
|
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.foregroundColor(textColor())
|
.foregroundColor(textColor())
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.dynamicTypeSize(.xSmall)
|
.dynamicTypeSize(.xSmall)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SideMenuLabelStyle: LabelStyle {
|
struct SideMenuLabelStyle: LabelStyle {
|
||||||
func makeBody(configuration: Configuration) -> some View {
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
HStack(alignment: .center, spacing: 8) {
|
HStack(alignment: .center, spacing: 8) {
|
||||||
|
|||||||
@@ -70,21 +70,17 @@ 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) {
|
||||||
|
let word = textView.text(in: textView.textRange(from: startPosition, to: cursorPosition)!)
|
||||||
if let wordRange,
|
val = (word, convertToNSRange(startPosition, cursorPosition, textView))
|
||||||
let startPosition = textView.position(from: wordRange.start, offset: -1),
|
}
|
||||||
let cursorPosition = textView.position(from: selectedRange.start, offset: 0)
|
}
|
||||||
{
|
getFocusWordForMention?(val.0, val.1)
|
||||||
let word = textView.text(in: textView.textRange(from: startPosition, to: cursorPosition)!)
|
|
||||||
val = (word, convertToNSRange(startPosition, cursorPosition, textView))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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? {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -64,13 +63,17 @@ 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