mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-12-22 07:06:40 +00:00
Explore + Account polish + Status editor WIP
This commit is contained in:
parent
0679559ced
commit
189037b53d
21 changed files with 358 additions and 59 deletions
|
@ -17,6 +17,8 @@
|
|||
9F398AA92935FFDB00A889F2 /* Account in Frameworks */ = {isa = PBXBuildFile; productRef = 9F398AA82935FFDB00A889F2 /* Account */; };
|
||||
9F398AAB2935FFDB00A889F2 /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 9F398AAA2935FFDB00A889F2 /* Models */; };
|
||||
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F398AB229360A4C00A889F2 /* TimelineTab.swift */; };
|
||||
9F55C68D2955968700F94077 /* ExploreTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F55C68C2955968700F94077 /* ExploreTab.swift */; };
|
||||
9F55C6902955993C00F94077 /* Explore in Frameworks */ = {isa = PBXBuildFile; productRef = 9F55C68F2955993C00F94077 /* Explore */; };
|
||||
9F5E581929545BE700A53960 /* Env in Frameworks */ = {isa = PBXBuildFile; productRef = 9F5E581829545BE700A53960 /* Env */; };
|
||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAE4ACA293783B000772766 /* SettingsTab.swift */; };
|
||||
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; };
|
||||
|
@ -41,6 +43,8 @@
|
|||
9F398AA52935FE8A00A889F2 /* AppRouteur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouteur.swift; sourceTree = "<group>"; };
|
||||
9F398AAC2936005300A889F2 /* Account */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Account; path = Packages/Account; sourceTree = "<group>"; };
|
||||
9F398AB229360A4C00A889F2 /* TimelineTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTab.swift; sourceTree = "<group>"; };
|
||||
9F55C68C2955968700F94077 /* ExploreTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreTab.swift; sourceTree = "<group>"; };
|
||||
9F55C68E295598F900F94077 /* Explore */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Explore; path = Packages/Explore; sourceTree = "<group>"; };
|
||||
9F5E581729545B5500A53960 /* Env */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Env; path = Packages/Env; sourceTree = "<group>"; };
|
||||
9FAE4AC8293774FF00772766 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
9FAE4ACA293783B000772766 /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = "<group>"; };
|
||||
|
@ -58,6 +62,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9F55C6902955993C00F94077 /* Explore in Frameworks */,
|
||||
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */,
|
||||
9F398AA92935FFDB00A889F2 /* Account in Frameworks */,
|
||||
9FBFE64E292A72BD00C250E9 /* Network in Frameworks */,
|
||||
|
@ -98,6 +103,7 @@
|
|||
9F398AB229360A4C00A889F2 /* TimelineTab.swift */,
|
||||
9F35DB4629506F6600B3281A /* NotificationTab.swift */,
|
||||
9F35DB4B2952005C00B3281A /* AccountTab.swift */,
|
||||
9F55C68C2955968700F94077 /* ExploreTab.swift */,
|
||||
);
|
||||
path = Tabs;
|
||||
sourceTree = "<group>";
|
||||
|
@ -119,6 +125,7 @@
|
|||
9FBFE64C292A72BD00C250E9 /* Frameworks */,
|
||||
9F398AAC2936005300A889F2 /* Account */,
|
||||
9F35DB45294FA04C00B3281A /* DesignSystem */,
|
||||
9F55C68E295598F900F94077 /* Explore */,
|
||||
9F5E581729545B5500A53960 /* Env */,
|
||||
9F398AA32935F90100A889F2 /* Models */,
|
||||
9F29553D292B67B600E0E81B /* Network */,
|
||||
|
@ -189,6 +196,7 @@
|
|||
9F35DB43294F9A7D00B3281A /* Status */,
|
||||
9F35DB4929506FA100B3281A /* Notifications */,
|
||||
9F5E581829545BE700A53960 /* Env */,
|
||||
9F55C68F2955993C00F94077 /* Explore */,
|
||||
);
|
||||
productName = IceCubesApp;
|
||||
productReference = 9FBFE639292A715500C250E9 /* IceCubesApp.app */;
|
||||
|
@ -256,6 +264,7 @@
|
|||
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */,
|
||||
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */,
|
||||
9FAE4AD129379AD600772766 /* AppAccount.swift in Sources */,
|
||||
9F55C68D2955968700F94077 /* ExploreTab.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -516,6 +525,10 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Models;
|
||||
};
|
||||
9F55C68F2955993C00F94077 /* Explore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Explore;
|
||||
};
|
||||
9F5E581829545BE700A53960 /* Env */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Env;
|
||||
|
|
|
@ -24,8 +24,8 @@ extension View {
|
|||
func withSheetDestinations(sheetDestinations: Binding<SheetDestinations?>) -> some View {
|
||||
self.sheet(item: sheetDestinations) { destination in
|
||||
switch destination {
|
||||
default:
|
||||
EmptyView()
|
||||
case .statusEditor:
|
||||
StatusEditorView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,19 +11,25 @@ struct IceCubesApp: App {
|
|||
@StateObject private var appAccountsManager = AppAccountsManager()
|
||||
@StateObject private var currentAccount = CurrentAccount()
|
||||
@StateObject private var quickLook = QuickLook()
|
||||
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
TabView {
|
||||
TimelineTab()
|
||||
.tabItem {
|
||||
Label("Home", systemImage: "globe")
|
||||
Label("Timeline", systemImage: "rectangle.on.rectangle")
|
||||
}
|
||||
if appAccountsManager.currentClient.isAuth {
|
||||
NotificationsTab()
|
||||
.tabItem {
|
||||
Label("Notifications", systemImage: "bell")
|
||||
}
|
||||
}
|
||||
ExploreTab()
|
||||
.tabItem {
|
||||
Label("Explore", systemImage: "magnifyingglass")
|
||||
}
|
||||
if appAccountsManager.currentClient.isAuth {
|
||||
AccountTab()
|
||||
.tabItem {
|
||||
Label("Profile", systemImage: "person.circle")
|
||||
|
|
18
IceCubesApp/App/Tabs/ExploreTab.swift
Normal file
18
IceCubesApp/App/Tabs/ExploreTab.swift
Normal file
|
@ -0,0 +1,18 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Models
|
||||
import Shimmer
|
||||
import Explore
|
||||
|
||||
struct ExploreTab: View {
|
||||
@StateObject private var routeurPath = RouterPath()
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $routeurPath.path) {
|
||||
ExploreView()
|
||||
.withAppRouteur()
|
||||
.withSheetDestinations(sheetDestinations: $routeurPath.presentedSheet)
|
||||
}
|
||||
.environmentObject(routeurPath)
|
||||
}
|
||||
}
|
|
@ -11,6 +11,16 @@ struct TimelineTab: View {
|
|||
TimelineView()
|
||||
.withAppRouteur()
|
||||
.withSheetDestinations(sheetDestinations: $routeurPath.presentedSheet)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .statusEditor(replyToStatus: nil)
|
||||
} label: {
|
||||
Image(systemName: "square.and.pencil")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.environmentObject(routeurPath)
|
||||
}
|
||||
|
|
|
@ -66,24 +66,11 @@ struct AccountDetailHeaderView: View {
|
|||
|
||||
private var accountAvatarView: some View {
|
||||
HStack {
|
||||
AsyncImage(
|
||||
url: account.avatar,
|
||||
content: { image in
|
||||
image.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.cornerRadius(4)
|
||||
.frame(maxWidth: 80, maxHeight: 80)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(.white, lineWidth: 1)
|
||||
)
|
||||
},
|
||||
placeholder: {
|
||||
ProgressView()
|
||||
.frame(maxWidth: 80, maxHeight: 80)
|
||||
}
|
||||
)
|
||||
.contentShape(Rectangle())
|
||||
AvatarView(url: account.avatar, size: .account)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(.white, lineWidth: 1)
|
||||
)
|
||||
.onTapGesture {
|
||||
Task {
|
||||
await quickLook.prepareFor(urls: [account.avatar], selectedURL: account.avatar)
|
||||
|
|
|
@ -77,7 +77,7 @@ public struct AccountDetailView: View {
|
|||
}
|
||||
}
|
||||
.edgesIgnoringSafeArea(.top)
|
||||
.navigationTitle(Text(scrollOffset < -20 ? viewModel.title : ""))
|
||||
.navigationTitle(Text(scrollOffset < -200 ? viewModel.title : ""))
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -205,21 +205,9 @@ public struct AccountDetailView: View {
|
|||
private func makeTagsListView(tags: [Tag]) -> some View {
|
||||
Group {
|
||||
ForEach(tags) { tag in
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("#\(tag.name)")
|
||||
.font(.headline)
|
||||
Text("\(tag.totalUses) posts from \(tag.totalAccounts) participants")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, DS.Constants.layoutPadding)
|
||||
.padding(.vertical, 8)
|
||||
.onTapGesture {
|
||||
routeurPath.navigate(to: .hashTag(tag: tag.name, account: nil))
|
||||
}
|
||||
TagRowView(tag: tag)
|
||||
.padding(.horizontal, DS.Constants.layoutPadding)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,30 +3,41 @@ import Shimmer
|
|||
|
||||
public struct AvatarView: View {
|
||||
public enum Size {
|
||||
case profile, badge
|
||||
case account, status, badge
|
||||
|
||||
var size: CGSize {
|
||||
switch self {
|
||||
case .profile:
|
||||
case .account:
|
||||
return .init(width: 80, height: 80)
|
||||
case .status:
|
||||
return .init(width: 40, height: 40)
|
||||
case .badge:
|
||||
return .init(width: 28, height: 28)
|
||||
}
|
||||
}
|
||||
|
||||
var cornerRadius: CGFloat {
|
||||
switch self {
|
||||
case .badge:
|
||||
return size.width / 2
|
||||
default:
|
||||
return 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(\.redactionReasons) private var reasons
|
||||
public let url: URL
|
||||
public let size: Size
|
||||
|
||||
public init(url: URL, size: Size = .profile) {
|
||||
public init(url: URL, size: Size = .status) {
|
||||
self.url = url
|
||||
self.size = size
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
if reasons == .placeholder {
|
||||
RoundedRectangle(cornerRadius: size == .profile ? 4 : size.size.width / 2)
|
||||
RoundedRectangle(cornerRadius: size.cornerRadius)
|
||||
.fill(.gray)
|
||||
.frame(maxWidth: size.size.width, maxHeight: size.size.height)
|
||||
} else {
|
||||
|
@ -39,7 +50,7 @@ public struct AvatarView: View {
|
|||
.frame(width: size.size.width, height: size.size.height)
|
||||
.shimmering()
|
||||
} else {
|
||||
RoundedRectangle(cornerRadius: size == .profile ? 4 : size.size.width / 2)
|
||||
RoundedRectangle(cornerRadius: size.cornerRadius)
|
||||
.fill(.gray)
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
.shimmering()
|
||||
|
@ -47,7 +58,7 @@ public struct AvatarView: View {
|
|||
case let .success(image):
|
||||
image.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.cornerRadius(size == .profile ? 4 : size.size.width / 2)
|
||||
.cornerRadius(size.cornerRadius)
|
||||
.frame(maxWidth: size.size.width, maxHeight: size.size.height)
|
||||
case .failure:
|
||||
EmptyView()
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import Models
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
public struct TagRowView: View {
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
|
||||
let tag: Tag
|
||||
|
||||
public init(tag: Tag) {
|
||||
self.tag = tag
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("#\(tag.name)")
|
||||
.font(.headline)
|
||||
Text("\(tag.totalUses) posts from \(tag.totalAccounts) participants")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.onTapGesture {
|
||||
routeurPath.navigate(to: .hashTag(tag: tag.name, account: nil))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,10 +10,12 @@ public enum RouteurDestinations: Hashable {
|
|||
}
|
||||
|
||||
public enum SheetDestinations: Identifiable {
|
||||
case statusEditor(replyToStatus: String?)
|
||||
|
||||
public var id: String {
|
||||
switch self {
|
||||
default:
|
||||
break
|
||||
case .statusEditor:
|
||||
return "statusEditor"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9
Packages/Explore/.gitignore
vendored
Normal file
9
Packages/Explore/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/config/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
35
Packages/Explore/Package.swift
Normal file
35
Packages/Explore/Package.swift
Normal file
|
@ -0,0 +1,35 @@
|
|||
// swift-tools-version: 5.7
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Explore",
|
||||
platforms: [
|
||||
.iOS(.v16),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "Explore",
|
||||
targets: ["Explore"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Network", path: "../Network"),
|
||||
.package(name: "Models", path: "../Models"),
|
||||
.package(name: "Env", path: "../Env"),
|
||||
.package(name: "Status", path: "../Status"),
|
||||
.package(url: "https://github.com/markiv/SwiftUI-Shimmer", exact: "1.1.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "Explore",
|
||||
dependencies: [
|
||||
.product(name: "Network", package: "Network"),
|
||||
.product(name: "Models", package: "Models"),
|
||||
.product(name: "Env", package: "Env"),
|
||||
.product(name: "Status", package: "Status"),
|
||||
.product(name: "Shimmer", package: "SwiftUI-Shimmer")
|
||||
])
|
||||
]
|
||||
)
|
||||
|
3
Packages/Explore/README.md
Normal file
3
Packages/Explore/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Explore
|
||||
|
||||
A description of this package.
|
95
Packages/Explore/Sources/Explore/ExploreView.swift
Normal file
95
Packages/Explore/Sources/Explore/ExploreView.swift
Normal file
|
@ -0,0 +1,95 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Network
|
||||
import DesignSystem
|
||||
import Models
|
||||
import Status
|
||||
|
||||
public struct ExploreView: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
|
||||
@StateObject private var viewModel = ExploreViewModel()
|
||||
@State private var searchQuery: String = ""
|
||||
|
||||
public init() { }
|
||||
|
||||
public var body: some View {
|
||||
List {
|
||||
Section("Trending Tags") {
|
||||
ForEach(viewModel.trendingTags
|
||||
.prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count)) { tag in
|
||||
TagRowView(tag: tag)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
NavigationLink {
|
||||
List {
|
||||
ForEach(viewModel.trendingTags) { tag in
|
||||
TagRowView(tag: tag)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationTitle("Trending Tags")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
} label: {
|
||||
Text("See more")
|
||||
.foregroundColor(.brand)
|
||||
}
|
||||
}
|
||||
|
||||
Section("Trending Posts") {
|
||||
ForEach(viewModel.trendingStatuses
|
||||
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count)) { status in
|
||||
StatusRowView(viewModel: .init(status: status, isEmbed: false))
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
||||
NavigationLink {
|
||||
List {
|
||||
ForEach(viewModel.trendingStatuses) { status in
|
||||
StatusRowView(viewModel: .init(status: status, isEmbed: false))
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationTitle("Trending Posts")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
} label: {
|
||||
Text("See more")
|
||||
.foregroundColor(.brand)
|
||||
}
|
||||
}
|
||||
|
||||
Section("Trending Links") {
|
||||
ForEach(viewModel.trendingLinks
|
||||
.prefix(upTo: viewModel.trendingLinks.count > 3 ? 3 : viewModel.trendingLinks.count)) { card in
|
||||
StatusCardView(card: card)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
NavigationLink {
|
||||
List {
|
||||
ForEach(viewModel.trendingLinks) { card in
|
||||
StatusCardView(card: card)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationTitle("Trending Links")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
} label: {
|
||||
Text("See more")
|
||||
.foregroundColor(.brand)
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
viewModel.client = client
|
||||
await viewModel.fetchTrending()
|
||||
}
|
||||
.listStyle(.grouped)
|
||||
.navigationTitle("Explore")
|
||||
.searchable(text: $searchQuery)
|
||||
}
|
||||
|
||||
}
|
25
Packages/Explore/Sources/Explore/ExploreViewModel.swift
Normal file
25
Packages/Explore/Sources/Explore/ExploreViewModel.swift
Normal file
|
@ -0,0 +1,25 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
|
||||
@MainActor
|
||||
class ExploreViewModel: ObservableObject {
|
||||
var client: Client?
|
||||
|
||||
@Published var trendingTags: [Tag] = []
|
||||
@Published var trendingStatuses: [Status] = []
|
||||
@Published var trendingLinks: [Card] = []
|
||||
|
||||
func fetchTrending() async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
async let trendingTags: [Tag] = client.get(endpoint: Trends.tags)
|
||||
async let trendingStatuses: [Status] = client.get(endpoint: Trends.statuses)
|
||||
async let trendingLinks: [Card] = client.get(endpoint: Trends.links)
|
||||
|
||||
self.trendingTags = try await trendingTags
|
||||
self.trendingStatuses = try await trendingStatuses
|
||||
self.trendingLinks = try await trendingLinks
|
||||
} catch { }
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
import Foundation
|
||||
|
||||
public struct Card: Codable {
|
||||
public struct Card: Codable, Identifiable {
|
||||
public var id: String {
|
||||
url.absoluteString
|
||||
}
|
||||
|
||||
public let url: URL
|
||||
public let title: String?
|
||||
public let description: String?
|
||||
|
|
25
Packages/Network/Sources/Network/Endpoint/Trends.swift
Normal file
25
Packages/Network/Sources/Network/Endpoint/Trends.swift
Normal file
|
@ -0,0 +1,25 @@
|
|||
import Foundation
|
||||
|
||||
public enum Trends: Endpoint {
|
||||
case tags
|
||||
case statuses
|
||||
case links
|
||||
|
||||
public func path() -> String {
|
||||
switch self {
|
||||
case .tags:
|
||||
return "trends/tags"
|
||||
case .statuses:
|
||||
return "trends/statuses"
|
||||
case .links:
|
||||
return "trends/links"
|
||||
}
|
||||
}
|
||||
|
||||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
36
Packages/Status/Sources/Status/Editor/StatusEditorView.swift
Normal file
36
Packages/Status/Sources/Status/Editor/StatusEditorView.swift
Normal file
|
@ -0,0 +1,36 @@
|
|||
import SwiftUI
|
||||
|
||||
public struct StatusEditorView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var statusText: String = ""
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
TextEditor(text: $statusText)
|
||||
}
|
||||
.navigationTitle("Post a toot")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("Post")
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import Env
|
|||
import Network
|
||||
|
||||
struct StatusActionsView: View {
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
|
||||
@MainActor
|
||||
|
@ -62,6 +63,8 @@ struct StatusActionsView: View {
|
|||
private func handleAction(action: Actions) {
|
||||
Task {
|
||||
switch action {
|
||||
case .respond:
|
||||
routeurPath.navigate(to: .statusDetail(id: viewModel.status.reblog?.id ?? viewModel.status.id))
|
||||
case .favourite:
|
||||
if viewModel.isFavourited {
|
||||
await viewModel.unFavourite()
|
||||
|
|
|
@ -2,12 +2,16 @@ import SwiftUI
|
|||
import Models
|
||||
import Shimmer
|
||||
|
||||
struct StatusCardView: View {
|
||||
public struct StatusCardView: View {
|
||||
@Environment(\.openURL) private var openURL
|
||||
let status: AnyStatus
|
||||
let card: Card
|
||||
|
||||
var body: some View {
|
||||
if let card = status.card, let title = card.title {
|
||||
public init(card: Card) {
|
||||
self.card = card
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
if let title = card.title {
|
||||
VStack(alignment: .leading) {
|
||||
if let imageURL = card.image {
|
||||
AsyncImage(
|
||||
|
@ -59,9 +63,3 @@ struct StatusCardView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StatusCardView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
StatusCardView(status: Status.placeholder())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,14 +67,16 @@ public struct StatusRowView: View {
|
|||
StatusMediaPreviewView(attachements: status.mediaAttachments)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
StatusCardView(status: status)
|
||||
if let card = status.card {
|
||||
StatusCardView(card: card)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func makeAccountView(status: AnyStatus) -> some View {
|
||||
AvatarView(url: status.account.avatar)
|
||||
AvatarView(url: status.account.avatar, size: .status)
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
status.account.displayNameWithEmojis
|
||||
.font(.subheadline)
|
||||
|
|
Loading…
Reference in a new issue