refactor: settings and translation view

This commit is contained in:
William Casarin
2023-02-03 09:25:07 -08:00
parent 006f8d79e0
commit 1696e0365e
15 changed files with 266 additions and 257 deletions

View File

@@ -28,7 +28,6 @@
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; }; 4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F90280F6528000448DE /* ChatView.swift */; }; 4C0A3F91280F6528000448DE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F90280F6528000448DE /* ChatView.swift */; };
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; }; 4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */; };
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F96280F8E02000448DE /* ThreadView.swift */; }; 4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F96280F8E02000448DE /* ThreadView.swift */; };
4C216F32286E388800040376 /* DMChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F31286E388800040376 /* DMChatView.swift */; }; 4C216F32286E388800040376 /* DMChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F31286E388800040376 /* DMChatView.swift */; };
4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; }; 4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; };
@@ -86,6 +85,7 @@
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */; }; 4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */; };
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */; }; 4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */; };
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */; }; 4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */; };
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C42812B298C848200DBF26F /* TranslateView.swift */; };
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C477C9D282C3A4800033AA3 /* TipCounter.swift */; }; 4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C477C9D282C3A4800033AA3 /* TipCounter.swift */; };
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */; }; 4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */; };
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */; }; 4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */; };
@@ -138,6 +138,7 @@
4CB883B0297705DD00DC99E7 /* ZapButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883AF297705DD00DC99E7 /* ZapButton.swift */; }; 4CB883B0297705DD00DC99E7 /* ZapButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883AF297705DD00DC99E7 /* ZapButton.swift */; };
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883B5297730E400DC99E7 /* LNUrls.swift */; }; 4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883B5297730E400DC99E7 /* LNUrls.swift */; };
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */; }; 4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */; };
4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAE6297EFA7B00430951 /* Zap.swift */; };
4CC7AAEB297F0AEC00430951 /* BuilderEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */; }; 4CC7AAEB297F0AEC00430951 /* BuilderEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */; };
4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEC297F0B9E00430951 /* Highlight.swift */; }; 4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEC297F0B9E00430951 /* Highlight.swift */; };
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEF297F11C700430951 /* SelectedEventView.swift */; }; 4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEF297F11C700430951 /* SelectedEventView.swift */; };
@@ -146,7 +147,6 @@
4CC7AAF6297F1A6A00430951 /* EventBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF5297F1A6A00430951 /* EventBody.swift */; }; 4CC7AAF6297F1A6A00430951 /* EventBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF5297F1A6A00430951 /* EventBody.swift */; };
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */; }; 4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */; };
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF9297F64AC00430951 /* EventMenu.swift */; }; 4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF9297F64AC00430951 /* EventMenu.swift */; };
4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAE6297EFA7B00430951 /* Zap.swift */; };
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; }; 4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; }; 4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; };
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; }; 4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; };
@@ -274,7 +274,6 @@
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; }; 4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
4C0A3F90280F6528000448DE /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; }; 4C0A3F90280F6528000448DE /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; }; 4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyQuoteView.swift; sourceTree = "<group>"; };
4C0A3F96280F8E02000448DE /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; }; 4C0A3F96280F8E02000448DE /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; };
4C216F31286E388800040376 /* DMChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMChatView.swift; sourceTree = "<group>"; }; 4C216F31286E388800040376 /* DMChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMChatView.swift; sourceTree = "<group>"; };
4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; }; 4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; };
@@ -361,6 +360,7 @@
4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceTests.swift; sourceTree = "<group>"; }; 4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceTests.swift; sourceTree = "<group>"; };
4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoicesView.swift; sourceTree = "<group>"; }; 4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoicesView.swift; sourceTree = "<group>"; };
4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceView.swift; sourceTree = "<group>"; }; 4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceView.swift; sourceTree = "<group>"; };
4C42812B298C848200DBF26F /* TranslateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslateView.swift; sourceTree = "<group>"; };
4C477C9D282C3A4800033AA3 /* TipCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipCounter.swift; sourceTree = "<group>"; }; 4C477C9D282C3A4800033AA3 /* TipCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipCounter.swift; sourceTree = "<group>"; };
4C4A3A5A288A1B2200453788 /* damus.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = damus.entitlements; sourceTree = "<group>"; }; 4C4A3A5A288A1B2200453788 /* damus.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = damus.entitlements; sourceTree = "<group>"; };
4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHomeModel.swift; sourceTree = "<group>"; }; 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHomeModel.swift; sourceTree = "<group>"; };
@@ -414,6 +414,7 @@
4CB883AF297705DD00DC99E7 /* ZapButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapButton.swift; sourceTree = "<group>"; }; 4CB883AF297705DD00DC99E7 /* ZapButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapButton.swift; sourceTree = "<group>"; };
4CB883B5297730E400DC99E7 /* LNUrls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LNUrls.swift; sourceTree = "<group>"; }; 4CB883B5297730E400DC99E7 /* LNUrls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LNUrls.swift; sourceTree = "<group>"; };
4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteLink.swift; sourceTree = "<group>"; }; 4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteLink.swift; sourceTree = "<group>"; };
4CC7AAE6297EFA7B00430951 /* Zap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zap.swift; sourceTree = "<group>"; };
4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuilderEventView.swift; sourceTree = "<group>"; }; 4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuilderEventView.swift; sourceTree = "<group>"; };
4CC7AAEC297F0B9E00430951 /* Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Highlight.swift; sourceTree = "<group>"; }; 4CC7AAEC297F0B9E00430951 /* Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Highlight.swift; sourceTree = "<group>"; };
4CC7AAEF297F11C700430951 /* SelectedEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedEventView.swift; sourceTree = "<group>"; }; 4CC7AAEF297F11C700430951 /* SelectedEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedEventView.swift; sourceTree = "<group>"; };
@@ -422,7 +423,6 @@
4CC7AAF5297F1A6A00430951 /* EventBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBody.swift; sourceTree = "<group>"; }; 4CC7AAF5297F1A6A00430951 /* EventBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBody.swift; sourceTree = "<group>"; };
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfile.swift; sourceTree = "<group>"; }; 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfile.swift; sourceTree = "<group>"; };
4CC7AAF9297F64AC00430951 /* EventMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMenu.swift; sourceTree = "<group>"; }; 4CC7AAF9297F64AC00430951 /* EventMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMenu.swift; sourceTree = "<group>"; };
4CC7AAE6297EFA7B00430951 /* Zap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zap.swift; sourceTree = "<group>"; };
4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; }; 4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; };
4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; }; 4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; }; 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
@@ -659,7 +659,6 @@
4C8682862814DE470026224F /* ProfileView.swift */, 4C8682862814DE470026224F /* ProfileView.swift */,
4C3AC7A42836987600E1F516 /* MainTabView.swift */, 4C3AC7A42836987600E1F516 /* MainTabView.swift */,
4C363A8B28236B92006E126D /* PubkeyView.swift */, 4C363A8B28236B92006E126D /* PubkeyView.swift */,
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */, 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
F7F0BA262978E54D009531F3 /* ParicipantsView.swift */, F7F0BA262978E54D009531F3 /* ParicipantsView.swift */,
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */, 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
@@ -793,6 +792,7 @@
4CF0ABE22981BC7D00D66079 /* UserView.swift */, 4CF0ABE22981BC7D00D66079 /* UserView.swift */,
7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */, 7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */,
4CB883AF297705DD00DC99E7 /* ZapButton.swift */, 4CB883AF297705DD00DC99E7 /* ZapButton.swift */,
4C42812B298C848200DBF26F /* TranslateView.swift */,
); );
path = Components; path = Components;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1177,6 +1177,7 @@
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */, 4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */, 4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */, 4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */,
4C363A9C282838B9006E126D /* EventRef.swift in Sources */, 4C363A9C282838B9006E126D /* EventRef.swift in Sources */,
3AA24802297E3DC20090C62D /* RepostView.swift in Sources */, 3AA24802297E3DC20090C62D /* RepostView.swift in Sources */,
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */, 4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */,
@@ -1215,7 +1216,6 @@
4C3AC7A728369BA200E1F516 /* SearchHomeView.swift in Sources */, 4C3AC7A728369BA200E1F516 /* SearchHomeView.swift in Sources */,
4CB883B0297705DD00DC99E7 /* ZapButton.swift in Sources */, 4CB883B0297705DD00DC99E7 /* ZapButton.swift in Sources */,
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */, 4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */,
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */, 4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */, 4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
4CF0ABDE2981A69500D66079 /* MutelistModel.swift in Sources */, 4CF0ABDE2981A69500D66079 /* MutelistModel.swift in Sources */,

View File

@@ -0,0 +1,138 @@
//
// TranslateButton.swift
// damus
//
// Created by William Casarin on 2023-02-02.
//
import SwiftUI
import NaturalLanguage
struct TranslateView: View {
let damus_state: DamusState
let event: NostrEvent
let size: EventViewKind
@State var checkingTranslationStatus: Bool = false
@State var currentLanguage: String = "en"
@State var noteLanguage: String? = nil
@State var translated_note: String? = nil
@State var show_translated_note: Bool = false
@State var translated_artifacts: NoteArtifacts? = nil
var TranslateButton: some View {
Button(NSLocalizedString("Translate Note", comment: "Button to translate note from different language.")) {
show_translated_note = true
}
.translate_button_style()
}
func Translated(lang: String, artifacts: NoteArtifacts) -> some View {
return Group {
Button(NSLocalizedString("Translated from \(lang)", comment: "Button to indicate that the note has been translated from a different language.")) {
show_translated_note = false
}
.translate_button_style()
Text(artifacts.content)
.font(eventviewsize_to_font(size))
.fixedSize(horizontal: false, vertical: true)
}
}
func CheckingStatus(lang: String) -> some View {
return Button(NSLocalizedString("Translating from \(lang)...", comment: "Button to indicate that the note is in the process of being translated from a different language.")) {
show_translated_note = false
}
.translate_button_style()
}
func MainContent(note_lang: String) -> some View {
return Group {
let languageName = Locale.current.localizedString(forLanguageCode: note_lang)
if let lang = languageName, show_translated_note {
if checkingTranslationStatus {
CheckingStatus(lang: lang)
} else if let artifacts = translated_artifacts {
Translated(lang: lang, artifacts: artifacts)
}
} else {
TranslateButton
}
}
}
var body: some View {
Group {
if let note_lang = noteLanguage, noteLanguage != currentLanguage {
MainContent(note_lang: note_lang)
} else {
Text("")
}
}
.task {
let translate_url = damus_state.settings.libretranslate_url
let api_key = damus_state.settings.libretranslate_api_key
guard noteLanguage == nil && !checkingTranslationStatus && translate_url != "" else {
return
}
checkingTranslationStatus = true
if #available(iOS 16, *) {
currentLanguage = Locale.current.language.languageCode?.identifier ?? "en"
} else {
currentLanguage = Locale.current.languageCode ?? "en"
}
// Rely on Apple's NLLanguageRecognizer to tell us which language it thinks the note is in.
let content = event.get_content(damus_state.keypair.privkey)
noteLanguage = NLLanguageRecognizer.dominantLanguage(for: content)?.rawValue ?? currentLanguage
if let lang = noteLanguage, noteLanguage != currentLanguage {
// If the detected dominant language is a variant, remove the variant component and just take the language part as LibreTranslate typically only supports the variant-less language.
if #available(iOS 16, *) {
noteLanguage = Locale.LanguageCode(stringLiteral: lang).identifier(.alpha2)
} else {
noteLanguage = Locale.canonicalLanguageIdentifier(from: lang)
}
}
guard let note_lang = noteLanguage else {
noteLanguage = currentLanguage
translated_note = nil
checkingTranslationStatus = false
return
}
if note_lang != currentLanguage {
do {
// If the note language is different from our language, send a translation request.
let translator = Translator(translate_url, apiKey: api_key)
translated_note = try await translator.translate(content, from: note_lang, to: currentLanguage)
} catch {
// If for whatever reason we're not able to figure out the language of the note, or translate the note, fail gracefully and do not retry. It's not the end of the world. Don't want to take down someone's translation server with an accidental denial of service attack.
noteLanguage = currentLanguage
translated_note = nil
}
}
if let translated = translated_note {
// Render translated note.
let blocks = event.get_blocks(content: translated)
translated_artifacts = render_blocks(blocks: blocks, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
}
checkingTranslationStatus = false
}
}
}
struct TranslateView_Previews: PreviewProvider {
static var previews: some View {
let ds = test_damus_state()
TranslateView(damus_state: ds, event: test_event, size: .selected)
}
}

View File

@@ -89,7 +89,6 @@ struct ContentView: View {
@State var filter_state : FilterState = .posts_and_replies @State var filter_state : FilterState = .posts_and_replies
@State private var isSideBarOpened = false @State private var isSideBarOpened = false
@StateObject var home: HomeModel = HomeModel() @StateObject var home: HomeModel = HomeModel()
@StateObject var user_settings = UserSettingsStore()
// connect retry timer // connect retry timer
let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect() let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect()
@@ -112,7 +111,7 @@ struct ContentView: View {
.tabViewStyle(.page(indexDisplayMode: .never)) .tabViewStyle(.page(indexDisplayMode: .never))
if privkey != nil { if privkey != nil {
PostButtonContainer(userSettings: user_settings) { PostButtonContainer(is_left_handed: damus_state?.settings.left_handed ?? false) {
self.active_sheet = .post self.active_sheet = .post
} }
} }
@@ -286,7 +285,6 @@ struct ContentView: View {
.padding([.bottom], 8) .padding([.bottom], 8)
} }
} }
.environmentObject(user_settings)
.onAppear() { .onAppear() {
self.connect() self.connect()
//KingfisherManager.shared.cache.clearDiskCache() //KingfisherManager.shared.cache.clearDiskCache()
@@ -563,7 +561,8 @@ struct ContentView: View {
dms: home.dms, dms: home.dms,
previews: PreviewCache(), previews: PreviewCache(),
zaps: Zaps(our_pubkey: pubkey), zaps: Zaps(our_pubkey: pubkey),
lnurls: LNUrls() lnurls: LNUrls(),
settings: UserSettingsStore()
) )
home.damus_state = self.damus_state! home.damus_state = self.damus_state!

View File

@@ -20,6 +20,7 @@ struct DamusState {
let previews: PreviewCache let previews: PreviewCache
let zaps: Zaps let zaps: Zaps
let lnurls: LNUrls let lnurls: LNUrls
let settings: UserSettingsStore
var pubkey: String { var pubkey: String {
return keypair.pubkey return keypair.pubkey
@@ -31,6 +32,6 @@ 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: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls()) return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore())
} }
} }

View File

@@ -52,7 +52,7 @@ struct LightningInvoice<T> {
switch description { switch description {
case .description(let string): case .description(let string):
return string return string
case .description_hash(let data): case .description_hash:
return "" return ""
} }
} }

View File

@@ -22,6 +22,22 @@ func get_default_wallet(_ pubkey: String) -> Wallet {
} }
} }
func get_libretranslate_server(_ pubkey: String) -> LibreTranslateServer? {
guard let server_name = UserDefaults.standard.string(forKey: "libretranslate_server") else {
return nil
}
return LibreTranslateServer(rawValue: server_name)
}
func get_libretranslate_url(_ pubkey: String, server: LibreTranslateServer) -> String? {
if let url = server.model.url {
return url
}
return UserDefaults.standard.object(forKey: "libretranslate_url") as? String
}
class UserSettingsStore: ObservableObject { class UserSettingsStore: ObservableObject {
@Published var default_wallet: Wallet { @Published var default_wallet: Wallet {
didSet { didSet {
@@ -82,15 +98,14 @@ class UserSettingsStore: ObservableObject {
init() { init() {
// TODO: pubkey-scoped settings // TODO: pubkey-scoped settings
let pubkey = "" let pubkey = ""
self.default_wallet = get_default_wallet("") self.default_wallet = get_default_wallet(pubkey)
show_wallet_selector = should_show_wallet_selector("") show_wallet_selector = should_show_wallet_selector(pubkey)
left_handed = UserDefaults.standard.object(forKey: "left_handed") as? Bool ?? false left_handed = UserDefaults.standard.object(forKey: "left_handed") as? Bool ?? false
if let translationServerName = UserDefaults.standard.string(forKey: "libretranslate_server"), if let server = get_libretranslate_server(pubkey) {
let translationServer = LibreTranslateServer(rawValue: translationServerName) { self.libretranslate_server = server
self.libretranslate_server = translationServer self.libretranslate_url = get_libretranslate_url(pubkey, server: server) ?? ""
libretranslate_url = translationServer.model.url ?? UserDefaults.standard.object(forKey: "libretranslate_url") as? String ?? ""
} else { } else {
// Note from @tyiu: // Note from @tyiu:
// Default server is disabled by default for now until we gain some confidence that it is working well in production. // Default server is disabled by default for now until we gain some confidence that it is working well in production.
@@ -101,7 +116,7 @@ class UserSettingsStore: ObservableObject {
libretranslate_server = .none libretranslate_server = .none
libretranslate_url = "" libretranslate_url = ""
} }
do { do {
libretranslate_api_key = try Vault.getPrivateKey(keychainConfiguration: DamusLibreTranslateKeychainConfiguration()) libretranslate_api_key = try Vault.getPrivateKey(keychainConfiguration: DamusLibreTranslateKeychainConfiguration())
} catch { } catch {

View File

@@ -96,17 +96,24 @@ struct ChatView: View {
if let ref_id = thread.replies.lookup(event.id) { if let ref_id = thread.replies.lookup(event.id) {
if !is_reply_to_prev() { if !is_reply_to_prev() {
/*
ReplyQuoteView(keypair: damus_state.keypair, quoter: event, event_id: ref_id, profiles: damus_state.profiles, previews: damus_state.previews) ReplyQuoteView(keypair: damus_state.keypair, quoter: event, event_id: ref_id, profiles: damus_state.profiles, previews: damus_state.previews)
.frame(maxHeight: expand_reply ? nil : 100) .frame(maxHeight: expand_reply ? nil : 100)
.environmentObject(thread) .environmentObject(thread)
.onTapGesture { .onTapGesture {
expand_reply = !expand_reply expand_reply = !expand_reply
} }
*/
ReplyDescription ReplyDescription
} }
} }
NoteContentView(keypair: damus_state.keypair, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal) let show_images = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
NoteContentView(damus_state: damus_state,
event: event,
show_images: show_images,
artifacts: .just_content(event.content),
size: .normal)
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
let bar = make_actionbar_model(ev: event, damus: damus_state) let bar = make_actionbar_model(ev: event, damus: damus_state)

View File

@@ -19,13 +19,15 @@ struct ConfigView: View {
@State var privkey_copied: Bool = false @State var privkey_copied: Bool = false
@State var pubkey_copied: Bool = false @State var pubkey_copied: Bool = false
@State var delete_text: String = "" @State var delete_text: String = ""
@EnvironmentObject var user_settings: UserSettingsStore
@ObservedObject var settings: UserSettingsStore
let generator = UIImpactFeedbackGenerator(style: .light) let generator = UIImpactFeedbackGenerator(style: .light)
init(state: DamusState) { init(state: DamusState) {
self.state = state self.state = state
_privkey = State(initialValue: self.state.keypair.privkey_bech32 ?? "") _privkey = State(initialValue: self.state.keypair.privkey_bech32 ?? "")
_settings = ObservedObject(initialValue: state.settings)
} }
// TODO: (jb55) could be more general but not gonna worry about it atm // TODO: (jb55) could be more general but not gonna worry about it atm
@@ -72,9 +74,9 @@ struct ConfigView: View {
} }
Section(NSLocalizedString("Wallet Selector", comment: "Section title for selection of wallet.")) { Section(NSLocalizedString("Wallet Selector", comment: "Section title for selection of wallet.")) {
Toggle(NSLocalizedString("Show wallet selector", comment: "Toggle to show or hide selection of wallet."), isOn: $user_settings.show_wallet_selector).toggleStyle(.switch) Toggle(NSLocalizedString("Show wallet selector", comment: "Toggle to show or hide selection of wallet."), isOn: $settings.show_wallet_selector).toggleStyle(.switch)
Picker(NSLocalizedString("Select default wallet", comment: "Prompt selection of user's default wallet"), Picker(NSLocalizedString("Select default wallet", comment: "Prompt selection of user's default wallet"),
selection: $user_settings.default_wallet) { selection: $settings.default_wallet) {
ForEach(Wallet.allCases, id: \.self) { wallet in ForEach(Wallet.allCases, id: \.self) { wallet in
Text(wallet.model.displayName) Text(wallet.model.displayName)
.tag(wallet.model.tag) .tag(wallet.model.tag)
@@ -83,28 +85,28 @@ struct ConfigView: View {
} }
Section(NSLocalizedString("LibreTranslate Translations", comment: "Section title for selecting the server that hosts the LibreTranslate machine translation API.")) { Section(NSLocalizedString("LibreTranslate Translations", comment: "Section title for selecting the server that hosts the LibreTranslate machine translation API.")) {
Picker(NSLocalizedString("Server", comment: "Prompt selection of LibreTranslate server to perform machine translations on notes"), selection: $user_settings.libretranslate_server) { Picker(NSLocalizedString("Server", comment: "Prompt selection of LibreTranslate server to perform machine translations on notes"), selection: $settings.libretranslate_server) {
ForEach(LibreTranslateServer.allCases, id: \.self) { server in ForEach(LibreTranslateServer.allCases, id: \.self) { server in
Text(server.model.displayName) Text(server.model.displayName)
.tag(server.model.tag) .tag(server.model.tag)
} }
} }
if user_settings.libretranslate_server != .none { if settings.libretranslate_server != .none {
TextField(NSLocalizedString("URL", comment: "Example URL to LibreTranslate server"), text: $user_settings.libretranslate_url) TextField(NSLocalizedString("URL", comment: "Example URL to LibreTranslate server"), text: $settings.libretranslate_url)
.disableAutocorrection(true) .disableAutocorrection(true)
.disabled(user_settings.libretranslate_server != .custom) .disabled(settings.libretranslate_server != .custom)
.autocapitalization(UITextAutocapitalizationType.none) .autocapitalization(UITextAutocapitalizationType.none)
HStack { HStack {
if show_libretranslate_api_key { if show_libretranslate_api_key {
TextField(NSLocalizedString("API Key (optional)", comment: "Example URL to LibreTranslate server"), text: $user_settings.libretranslate_api_key) TextField(NSLocalizedString("API Key (optional)", comment: "Example URL to LibreTranslate server"), text: $settings.libretranslate_api_key)
.disableAutocorrection(true) .disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none) .autocapitalization(UITextAutocapitalizationType.none)
Button(NSLocalizedString("Hide API Key", comment: "Button to hide the LibreTranslate server API key.")) { Button(NSLocalizedString("Hide API Key", comment: "Button to hide the LibreTranslate server API key.")) {
show_libretranslate_api_key = false show_libretranslate_api_key = false
} }
} else { } else {
SecureField(NSLocalizedString("API Key (optional)", comment: "Example URL to LibreTranslate server"), text: $user_settings.libretranslate_api_key) SecureField(NSLocalizedString("API Key (optional)", comment: "Example URL to LibreTranslate server"), text: $settings.libretranslate_api_key)
.disableAutocorrection(true) .disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none) .autocapitalization(UITextAutocapitalizationType.none)
Button(NSLocalizedString("Show API Key", comment: "Button to hide the LibreTranslate server API key.")) { Button(NSLocalizedString("Show API Key", comment: "Button to hide the LibreTranslate server API key.")) {
@@ -116,7 +118,7 @@ struct ConfigView: View {
} }
Section(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen")) { Section(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen")) {
Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $user_settings.left_handed) Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $settings.left_handed)
.toggleStyle(.switch) .toggleStyle(.switch)
} }

View File

@@ -23,7 +23,7 @@ struct DMView: View {
let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey) let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
NoteContentView(keypair: damus_state.keypair, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal) NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
.foregroundColor(is_ours ? Color.white : Color.primary) .foregroundColor(is_ours ? Color.white : Color.primary)
.padding(10) .padding(10)
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15)) .background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))

View File

@@ -23,7 +23,7 @@ struct EventBody: View {
let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey, booster_pubkey: nil) let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey, booster_pubkey: nil)
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_img, artifacts: .just_content(content), size: size) NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, artifacts: .just_content(content), size: size)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
} }
} }

View File

@@ -13,108 +13,16 @@ import NaturalLanguage
import FoundationNetworking import FoundationNetworking
#endif #endif
struct NoteArtifacts {
let content: AttributedString
let images: [URL]
let invoices: [Invoice]
let links: [URL]
static func just_content(_ content: String) -> NoteArtifacts {
NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: [])
}
}
func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -> NoteArtifacts {
let blocks = ev.blocks(privkey)
return render_blocks(blocks: blocks, profiles: profiles, privkey: privkey)
}
func render_blocks(blocks: [Block], profiles: Profiles, privkey: String?) -> NoteArtifacts {
var invoices: [Invoice] = []
var img_urls: [URL] = []
var link_urls: [URL] = []
let txt: AttributedString = blocks.reduce("") { str, block in
switch block {
case .mention(let m):
return str + mention_str(m, profiles: profiles)
case .text(let txt):
return str + AttributedString(stringLiteral: txt)
case .hashtag(let htag):
return str + hashtag_str(htag)
case .invoice(let invoice):
invoices.append(invoice)
return str
case .url(let url):
// Handle Image URLs
if is_image_url(url) {
// Append Image
img_urls.append(url)
return str
} else {
link_urls.append(url)
return str + url_str(url)
}
}
}
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices, links: link_urls)
}
func is_image_url(_ url: URL) -> Bool {
let str = url.lastPathComponent.lowercased()
return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif")
}
struct NoteContentView: View { struct NoteContentView: View {
let keypair: Keypair let damus_state: DamusState
let event: NostrEvent let event: NostrEvent
let profiles: Profiles
let previews: PreviewCache
let show_images: Bool let show_images: Bool
@State var checkingTranslationStatus: Bool = false
@State var currentLanguage: String = "en"
@State var noteLanguage: String? = nil
@State var translated_note: String? = nil
@State var show_translated_note: Bool = false
@State var translated_artifacts: NoteArtifacts? = nil
@State var artifacts: NoteArtifacts @State var artifacts: NoteArtifacts
@State var preview: LinkViewRepresentable? = nil
let size: EventViewKind let size: EventViewKind
@EnvironmentObject var user_settings: UserSettingsStore
var TranslateButton: some View { @State var preview: LinkViewRepresentable? = nil
Group {
let languageName = Locale.current.localizedString(forLanguageCode: noteLanguage!)
if show_translated_note {
if checkingTranslationStatus {
Button(NSLocalizedString("Translating from \(languageName!)...", comment: "Button to indicate that the note is in the process of being translated from a different language.")) {
show_translated_note = false
}
.translate_button_style()
} else if translated_artifacts != nil {
Button(NSLocalizedString("Translated from \(languageName!)", comment: "Button to indicate that the note has been translated from a different language.")) {
show_translated_note = false
}
.translate_button_style()
Text(translated_artifacts!.content)
.font(eventviewsize_to_font(size))
.fixedSize(horizontal: false, vertical: true)
}
} else {
Button(NSLocalizedString("Translate Note", comment: "Button to translate note from different language.")) {
show_translated_note = true
}
.translate_button_style()
}
}
}
func MainContent() -> some View { func MainContent() -> some View {
return VStack(alignment: .leading) { return VStack(alignment: .leading) {
@@ -122,8 +30,8 @@ struct NoteContentView: View {
.font(eventviewsize_to_font(size)) .font(eventviewsize_to_font(size))
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
if size == .selected && noteLanguage != nil && noteLanguage != currentLanguage { if size == .selected {
TranslateButton TranslateView(damus_state: damus_state, event: event, size: size)
} }
if show_images && artifacts.images.count > 0 { if show_images && artifacts.images.count > 0 {
@@ -138,7 +46,7 @@ struct NoteContentView: View {
.cornerRadius(10) .cornerRadius(10)
} }
if artifacts.invoices.count > 0 { if artifacts.invoices.count > 0 {
InvoicesView(our_pubkey: keypair.pubkey, invoices: artifacts.invoices) InvoicesView(our_pubkey: damus_state.keypair.pubkey, invoices: artifacts.invoices)
} }
if let preview = self.preview, show_images { if let preview = self.preview, show_images {
@@ -157,16 +65,16 @@ struct NoteContentView: View {
var body: some View { var body: some View {
MainContent() MainContent()
.onAppear() { .onAppear() {
self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: keypair.privkey) self.artifacts = render_note_content(ev: event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
} }
.onReceive(handle_notify(.profile_updated)) { notif in .onReceive(handle_notify(.profile_updated)) { notif in
let profile = notif.object as! ProfileUpdate let profile = notif.object as! ProfileUpdate
let blocks = event.blocks(keypair.privkey) let blocks = event.blocks(damus_state.keypair.privkey)
for block in blocks { for block in blocks {
switch block { switch block {
case .mention(let m): case .mention(let m):
if m.type == .pubkey && m.ref.ref_id == profile.pubkey { if m.type == .pubkey && m.ref.ref_id == profile.pubkey {
self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: keypair.privkey) self.artifacts = render_note_content(ev: event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
} }
case .text: return case .text: return
case .hashtag: return case .hashtag: return
@@ -176,7 +84,7 @@ struct NoteContentView: View {
} }
} }
.task { .task {
if let preview = previews.lookup(self.event.id) { if let preview = damus_state.previews.lookup(self.event.id) {
switch preview { switch preview {
case .value(let view): case .value(let view):
self.preview = view self.preview = view
@@ -190,54 +98,10 @@ struct NoteContentView: View {
let meta = await getMetaData(for: artifacts.links.first!) let meta = await getMetaData(for: artifacts.links.first!)
let view = meta.map { LinkViewRepresentable(meta: .linkmeta($0)) } let view = meta.map { LinkViewRepresentable(meta: .linkmeta($0)) }
previews.store(evid: self.event.id, preview: view) damus_state.previews.store(evid: self.event.id, preview: view)
self.preview = view self.preview = view
} }
if size == .selected && noteLanguage == nil && !checkingTranslationStatus && user_settings.libretranslate_url != "" {
checkingTranslationStatus = true
if #available(iOS 16, *) {
currentLanguage = Locale.current.language.languageCode?.identifier ?? "en"
} else {
currentLanguage = Locale.current.languageCode ?? "en"
}
// Rely on Apple's NLLanguageRecognizer to tell us which language it thinks the note is in.
noteLanguage = NLLanguageRecognizer.dominantLanguage(for: event.content)?.rawValue ?? currentLanguage
if noteLanguage != currentLanguage {
// If the detected dominant language is a variant, remove the variant component and just take the language part as LibreTranslate typically only supports the variant-less language.
if #available(iOS 16, *) {
noteLanguage = Locale.LanguageCode(stringLiteral: noteLanguage!).identifier(.alpha2)
} else {
noteLanguage = Locale.canonicalLanguageIdentifier(from: noteLanguage!)
}
}
if noteLanguage == nil {
noteLanguage = currentLanguage
translated_note = nil
} else if noteLanguage != currentLanguage {
do {
// If the note language is different from our language, send a translation request.
let translator = Translator(user_settings.libretranslate_url, apiKey: user_settings.libretranslate_api_key)
translated_note = try await translator.translate(event.content, from: noteLanguage!, to: currentLanguage)
} catch {
// If for whatever reason we're not able to figure out the language of the note, or translate the note, fail gracefully and do not retry. It's not the end of the world. Don't want to take down someone's translation server with an accidental denial of service attack.
noteLanguage = currentLanguage
translated_note = nil
}
}
if translated_note != nil {
// Render translated note.
let blocks = event.get_blocks(content: translated_note!)
translated_artifacts = render_blocks(blocks: blocks, profiles: profiles, privkey: privkey)
}
checkingTranslationStatus = false
}
} }
} }
@@ -372,7 +236,7 @@ struct NoteContentView_Previews: PreviewProvider {
let state = test_damus_state() let state = test_damus_state()
let content = "hi there ¯\\_(ツ)_/¯ https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg" let content = "hi there ¯\\_(ツ)_/¯ https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg"
let artifacts = NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: []) let artifacts = NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: [])
NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, previews: PreviewCache(), show_images: true, artifacts: artifacts, size: .normal) NoteContentView(damus_state: state, event: NostrEvent(content: content, pubkey: "pk"), show_images: true, artifacts: artifacts, size: .normal)
} }
} }
@@ -385,3 +249,56 @@ extension View {
.padding([.top, .bottom], 10) .padding([.top, .bottom], 10)
} }
} }
struct NoteArtifacts {
let content: AttributedString
let images: [URL]
let invoices: [Invoice]
let links: [URL]
static func just_content(_ content: String) -> NoteArtifacts {
NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: [])
}
}
func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -> NoteArtifacts {
let blocks = ev.blocks(privkey)
return render_blocks(blocks: blocks, profiles: profiles, privkey: privkey)
}
func render_blocks(blocks: [Block], profiles: Profiles, privkey: String?) -> NoteArtifacts {
var invoices: [Invoice] = []
var img_urls: [URL] = []
var link_urls: [URL] = []
let txt: AttributedString = blocks.reduce("") { str, block in
switch block {
case .mention(let m):
return str + mention_str(m, profiles: profiles)
case .text(let txt):
return str + AttributedString(stringLiteral: txt)
case .hashtag(let htag):
return str + hashtag_str(htag)
case .invoice(let invoice):
invoices.append(invoice)
return str
case .url(let url):
// Handle Image URLs
if is_image_url(url) {
// Append Image
img_urls.append(url)
return str
} else {
link_urls.append(url)
return str + url_str(url)
}
}
}
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices, links: link_urls)
}
func is_image_url(_ url: URL) -> Bool {
let str = url.lastPathComponent.lowercased()
return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif")
}

View File

@@ -34,8 +34,7 @@ func PostButton(action: @escaping () -> ()) -> some View {
.keyboardShortcut("n", modifiers: [.command, .shift]) .keyboardShortcut("n", modifiers: [.command, .shift])
} }
func PostButtonContainer(userSettings: UserSettingsStore, action: @escaping () -> Void) -> some View { func PostButtonContainer(is_left_handed: Bool, action: @escaping () -> Void) -> some View {
let is_left_handed = userSettings.left_handed.self
return VStack { return VStack {
Spacer() Spacer()

View File

@@ -116,7 +116,6 @@ struct ProfileView: View {
@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 action_sheet_presented: Bool = false @State var action_sheet_presented: Bool = false
@EnvironmentObject var user_settings: UserSettingsStore
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@@ -142,10 +141,10 @@ struct ProfileView: View {
func LNButton(lnurl: String, profile: Profile) -> some View { func LNButton(lnurl: String, profile: Profile) -> some View {
Button(action: { Button(action: {
if user_settings.show_wallet_selector { if damus_state.settings.show_wallet_selector {
showing_select_wallet = true showing_select_wallet = true
} else { } else {
open_with_wallet(wallet: user_settings.default_wallet.model, invoice: lnurl) open_with_wallet(wallet: damus_state.settings.default_wallet.model, invoice: lnurl)
} }
}) { }) {
Image(systemName: "bolt.circle") Image(systemName: "bolt.circle")
@@ -162,7 +161,6 @@ struct ProfileView: View {
.cornerRadius(24) .cornerRadius(24)
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) { .sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
SelectWalletView(showingSelectWallet: $showing_select_wallet, our_pubkey: damus_state.pubkey, invoice: lnurl) SelectWalletView(showingSelectWallet: $showing_select_wallet, our_pubkey: damus_state.pubkey, invoice: lnurl)
.environmentObject(user_settings)
} }
} }
@@ -409,7 +407,7 @@ struct ProfileView_Previews: PreviewProvider {
func test_damus_state() -> DamusState { func test_damus_state() -> DamusState {
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681" let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(our_pubkey: pubkey), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: pubkey), previews: PreviewCache(), zaps: Zaps(our_pubkey: pubkey), lnurls: LNUrls()) 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") 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")
let tsprof = TimestampedProfile(profile: prof, timestamp: 0) let tsprof = TimestampedProfile(profile: prof, timestamp: 0)

View File

@@ -1,65 +0,0 @@
//
// SwiftUIView.swift
// damus
//
// Created by William Casarin on 2022-04-19.
//
import SwiftUI
struct ReplyQuoteView: View {
let keypair: Keypair
let quoter: NostrEvent
let event_id: String
let profiles: Profiles
let previews: PreviewCache
@EnvironmentObject var thread: ThreadModel
func MainContent(event: NostrEvent) -> some View {
HStack(alignment: .top) {
Rectangle()
.frame(width: 2)
.padding([.leading], 4)
.foregroundColor(.accentColor)
VStack(alignment: .leading) {
HStack(alignment: .top) {
ProfilePicView(pubkey: event.pubkey, size: 16, highlight: .reply, profiles: profiles)
Text(Profile.displayName(profile: profiles.lookup(id: event.pubkey), pubkey: event.pubkey))
.foregroundColor(.accentColor)
Text("\(format_relative_time(event.created_at))", comment: "Amount of time that has passed since reply quote event occurred.")
.foregroundColor(.gray)
}
NoteContentView(keypair: keypair, event: event, profiles: profiles, previews: previews, show_images: false, artifacts: .just_content(event.content), size: .normal)
.font(.callout)
.foregroundColor(.accentColor)
//Spacer()
}
//.border(Color.red)
}
//.border(Color.green)
}
var body: some View {
Group {
if let event = thread.lookup(event_id) {
MainContent(event: event)
.padding(4)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
}
}
}
}
struct ReplyQuoteView_Previews: PreviewProvider {
static var previews: some View {
let s = test_damus_state()
let quoter = NostrEvent(content: "a\nb\nc", pubkey: "pubkey")
ReplyQuoteView(keypair: s.keypair, quoter: quoter, event_id: "pubkey2", profiles: s.profiles, previews: PreviewCache())
.environmentObject(ThreadModel(event: quoter, damus_state: s))
}
}

View File

@@ -10,9 +10,7 @@ import SwiftUI
struct SideMenuView: View { 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
@EnvironmentObject var user_settings: UserSettingsStore
@State private var showQRCode = false @State private var showQRCode = false
@@ -121,7 +119,7 @@ struct SideMenuView: View {
.foregroundColor(textColor()) .foregroundColor(textColor())
} }
NavigationLink(destination: ConfigView(state: damus_state).environmentObject(user_settings)) { NavigationLink(destination: ConfigView(state: damus_state)) {
Label(NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), systemImage: "gear") Label(NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), systemImage: "gear")
.font(.title2) .font(.title2)
.foregroundColor(textColor()) .foregroundColor(textColor())