diff --git a/Threaded.xcodeproj/project.pbxproj b/Threaded.xcodeproj/project.pbxproj index 886870f..c4ff7ae 100644 --- a/Threaded.xcodeproj/project.pbxproj +++ b/Threaded.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ B9EBE8582B474FD600FB594D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EBE8572B474FD600FB594D /* AppDelegate.swift */; }; B9F8FA162B5D3AC30044DAB4 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F8FA152B5D3AC30044DAB4 /* SafariView.swift */; }; B9FA6E752B82367B00D63E30 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9FA6E742B82367B00D63E30 /* AttachmentView.swift */; }; + B9FA6E772B82788A00D63E30 /* AccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9FA6E762B82788A00D63E30 /* AccountRow.swift */; }; B9FB945B2B2DEECE00D81C07 /* ThreadedApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9FB945A2B2DEECE00D81C07 /* ThreadedApp.swift */; }; B9FB945D2B2DEECE00D81C07 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9FB945C2B2DEECE00D81C07 /* ContentView.swift */; }; B9FB94612B2DEECF00D81C07 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B9FB94602B2DEECF00D81C07 /* Assets.xcassets */; }; @@ -172,6 +173,7 @@ B9EBE8572B474FD600FB594D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B9F8FA152B5D3AC30044DAB4 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; }; B9FA6E742B82367B00D63E30 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = ""; }; + B9FA6E762B82788A00D63E30 /* AccountRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRow.swift; sourceTree = ""; }; B9FB94572B2DEECE00D81C07 /* Threaded.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Threaded.app; sourceTree = BUILT_PRODUCTS_DIR; }; B9FB945A2B2DEECE00D81C07 /* ThreadedApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadedApp.swift; sourceTree = ""; }; B9FB945C2B2DEECE00D81C07 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -385,6 +387,7 @@ B999DE5F2B76FB3E00509868 /* ContactRow.swift */, B915C4412B6F908C00042DDB /* ProfileView.swift */, B9D9C6C62B6A590F00C26A41 /* ProfilePicture.swift */, + B9FA6E762B82788A00D63E30 /* AccountRow.swift */, B97491E22B6E96700098BC48 /* SymbolWidth.swift */, B9DC69282B78D9A500E625B9 /* SearchResultView.swift */, ); @@ -587,6 +590,7 @@ B9842C102B2F228C00D9F3C1 /* Status.swift in Sources */, B93B677A2B42EC51000892E9 /* MetaPicker.swift in Sources */, B9D9C6C32B6A576C00C26A41 /* NotificationsView.swift in Sources */, + B9FA6E772B82788A00D63E30 /* AccountRow.swift in Sources */, B9FB94722B2DF49700D81C07 /* ConnectView.swift in Sources */, B9FB945B2B2DEECE00D81C07 /* ThreadedApp.swift in Sources */, B9FB94862B2E211200D81C07 /* Account+Elms.swift in Sources */, diff --git a/Threaded/Components/AccountRow.swift b/Threaded/Components/AccountRow.swift index a73d5f7..1a28d93 100644 --- a/Threaded/Components/AccountRow.swift +++ b/Threaded/Components/AccountRow.swift @@ -2,12 +2,85 @@ import SwiftUI -struct AccountRow: View { +struct AccountRow: View { + @Environment(AccountManager.self) private var accountManager: AccountManager + @EnvironmentObject private var navigator: Navigator + + var acct: String + @ViewBuilder var text: Content + + @State private var account: Account? = nil + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + HStack(spacing: 20) { + if let acc = account { + ProfilePicture(url: acc.avatar, size: 64) + + VStack(alignment: .leading) { + text + + Text(acc.displayName ?? "@\(acc.acct)") + .multilineTextAlignment(.leading) + } + + Spacer() + + Button { + navigator.navigate(to: .account(acc: acc)) + } label: { + Text("account.view") + } + .buttonStyle(LargeButton(filled: true, height: 7.5)) + } else { + Circle() + .fill(Color.gray.opacity(0.45)) + .frame(width: 54, height: 54) + + VStack(alignment: .leading) { + text + .redacted(reason: .placeholder) + + Text(Account.placeholder().displayName ?? "@\(Account.placeholder().acct)") + .redacted(reason: .placeholder) + .multilineTextAlignment(.leading) + } + + Spacer() + + Button { + print(acct) + } label: { + Text("account.view") + .redacted(reason: .placeholder) + } + .buttonStyle(LargeButton(filled: true, height: 7.5)) + } + } + .task { + await findAccount() + } + } + + private func findAccount() async { + guard let client = accountManager.getClient() else { return } + do { + try await Task.sleep(for: .milliseconds(250)) + let results: SearchResults = try await client.get(endpoint: Search.search(query: acct, type: "accounts", offset: nil, following: nil), forceVersion: .v2) + account = results.accounts.first +// let relationships: [Relationship] = try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map(\.id))) +// relationship = relationships.first + } catch { + print(error) + } } } #Preview { - AccountRow() + List { + AccountRow(acct: "@lumaa@techhub.social") { + EmptyView() + } + } + .environmentObject(Navigator()) + .environment(AccountManager.shared) } diff --git a/Threaded/Localizable.xcstrings b/Threaded/Localizable.xcstrings index 0474b6a..1aca093 100644 --- a/Threaded/Localizable.xcstrings +++ b/Threaded/Localizable.xcstrings @@ -303,6 +303,38 @@ } } }, + "accounts.developer" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Threaded Developer" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Développeur de Threaded" + } + } + } + }, + "accounts.official" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Official Account" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compte officiel" + } + } + } + }, "activity" : { "localizations" : { "en" : { @@ -536,7 +568,20 @@ } }, "attachment.alt.action" : { - + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + } + } }, "attachment.close" : { "localizations" : { @@ -586,6 +631,22 @@ } } }, + "discovery.app" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inside Threaded" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Par Threaded" + } + } + } + }, "discovery.results.posts" : { "localizations" : { "en" : { diff --git a/Threaded/Views/AttachmentView.swift b/Threaded/Views/AttachmentView.swift index b0375d6..a41b723 100644 --- a/Threaded/Views/AttachmentView.swift +++ b/Threaded/Views/AttachmentView.swift @@ -15,6 +15,7 @@ struct AttachmentView: View { } @State private var readAlt: Bool = false + @State private var hasSwitch: Bool = false @State private var currentZoom = 0.0 @State private var totalZoom = 1.0 @@ -106,13 +107,23 @@ struct AttachmentView: View { .highPriorityGesture( DragGesture() .onChanged { gesture in - guard totalZoom > 1.1 else { return } - currentPos = gesture.translation + if totalZoom > 1.1 { + currentPos = gesture.translation + } else { + guard !hasSwitch && attachments.count > 1 else { return } + if gesture.translation.width >= 40 || gesture.translation.width <= -40 { + let currentIndex = attachments.firstIndex(where: { $0.id == selectedId }) ?? 0 + let newIndex = gesture.translation.width >= 20 ? loseIndex(currentIndex - 1, max: attachments.count - 1) : loseIndex(currentIndex + 1, max: attachments.count - 1) + selectedId = attachments[newIndex].id + hasSwitch = true + } + } } .onEnded { gesture in totalPos.width += currentPos.width totalPos.height += currentPos.height currentPos = .zero + hasSwitch = false } ) .accessibilityZoomAction { action in @@ -162,6 +173,15 @@ struct AttachmentView: View { } } } + + private func loseIndex(_ index: Int, max: Int) -> Int { + if index < 0 { + return max + } else if index > max { + return 0 + } + return index + } } private extension View { diff --git a/Threaded/Views/DiscoveryView.swift b/Threaded/Views/DiscoveryView.swift index 262bbf8..07f4420 100644 --- a/Threaded/Views/DiscoveryView.swift +++ b/Threaded/Views/DiscoveryView.swift @@ -27,7 +27,6 @@ struct DiscoveryView: View { ScrollView { if results != [:] && !querying { SearchResultView(searchResults: results[searchQuery] ?? .init(accounts: [], statuses: [], hashtags: []), query: searchQuery) - .environmentObject(navigator) Spacer() .foregroundStyle(Color.white) @@ -52,6 +51,14 @@ struct DiscoveryView: View { tagsView + Text("discovery.app") + .multilineTextAlignment(.leading) + .font(.title.bold()) + .padding(.horizontal) + + appView + .padding(.horizontal) + Text("discovery.trending.posts") .multilineTextAlignment(.leading) .font(.title.bold()) @@ -84,6 +91,7 @@ struct DiscoveryView: View { .withAppRouter(navigator) .navigationTitle(Text("discovery")) } + .environmentObject(navigator) .task { await fetchTrending() } @@ -176,6 +184,21 @@ struct DiscoveryView: View { } } + var appView: some View { + VStack(spacing: 7.5) { + AccountRow(acct: "Threaded@mastodon.online") { + Text("accounts.official") + .font(.headline.bold().width(.condensed)) + .foregroundStyle(.green) + } + AccountRow(acct: "lumaa@mastodon.online") { + Text("accounts.developer") + .font(.headline.bold().width(.condensed)) + .foregroundStyle(.blue) + } + } + } + private func search() async { guard let client = accountManager.getClient(), !searchQuery.isEmpty else { return } do {