diff --git a/IceCubesApp.xcodeproj/project.pbxproj b/IceCubesApp.xcodeproj/project.pbxproj index 66d4f0c2..8fbb0bb5 100644 --- a/IceCubesApp.xcodeproj/project.pbxproj +++ b/IceCubesApp.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 9F295540292B6C3400E0E81B /* Timeline in Frameworks */ = {isa = PBXBuildFile; productRef = 9F29553F292B6C3400E0E81B /* Timeline */; }; 9FBFE63D292A715500C250E9 /* IceCubesAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesAppApp.swift */; }; - 9FBFE63F292A715500C250E9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63E292A715500C250E9 /* ContentView.swift */; }; 9FBFE641292A715600C250E9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FBFE640292A715600C250E9 /* Assets.xcassets */; }; 9FBFE645292A715600C250E9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FBFE644292A715600C250E9 /* Preview Assets.xcassets */; }; 9FBFE64E292A72BD00C250E9 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBFE64D292A72BD00C250E9 /* Network */; }; @@ -16,9 +16,9 @@ /* Begin PBXFileReference section */ 9F29553D292B67B600E0E81B /* Network */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Network; path = Packages/Network; sourceTree = ""; }; + 9F29553E292B6AF600E0E81B /* Timeline */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Timeline; path = Packages/Timeline; sourceTree = ""; }; 9FBFE639292A715500C250E9 /* IceCubesApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IceCubesApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9FBFE63C292A715500C250E9 /* IceCubesAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IceCubesAppApp.swift; sourceTree = ""; }; - 9FBFE63E292A715500C250E9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 9FBFE640292A715600C250E9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 9FBFE642292A715600C250E9 /* IceCubesApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesApp.entitlements; sourceTree = ""; }; 9FBFE644292A715600C250E9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; @@ -30,6 +30,7 @@ buildActionMask = 2147483647; files = ( 9FBFE64E292A72BD00C250E9 /* Network in Frameworks */, + 9F295540292B6C3400E0E81B /* Timeline in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -42,6 +43,7 @@ 9FBFE63B292A715500C250E9 /* IceCubesApp */, 9FBFE63A292A715500C250E9 /* Products */, 9FBFE64C292A72BD00C250E9 /* Frameworks */, + 9F29553E292B6AF600E0E81B /* Timeline */, 9F29553D292B67B600E0E81B /* Network */, ); sourceTree = ""; @@ -58,7 +60,6 @@ isa = PBXGroup; children = ( 9FBFE63C292A715500C250E9 /* IceCubesAppApp.swift */, - 9FBFE63E292A715500C250E9 /* ContentView.swift */, 9FBFE640292A715600C250E9 /* Assets.xcassets */, 9FBFE642292A715600C250E9 /* IceCubesApp.entitlements */, 9FBFE643292A715600C250E9 /* Preview Content */, @@ -99,6 +100,7 @@ name = IceCubesApp; packageProductDependencies = ( 9FBFE64D292A72BD00C250E9 /* Network */, + 9F29553F292B6C3400E0E81B /* Timeline */, ); productName = IceCubesApp; productReference = 9FBFE639292A715500C250E9 /* IceCubesApp.app */; @@ -154,7 +156,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9FBFE63F292A715500C250E9 /* ContentView.swift in Sources */, 9FBFE63D292A715500C250E9 /* IceCubesAppApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -371,6 +372,10 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ + 9F29553F292B6C3400E0E81B /* Timeline */ = { + isa = XCSwiftPackageProductDependency; + productName = Timeline; + }; 9FBFE64D292A72BD00C250E9 /* Network */ = { isa = XCSwiftPackageProductDependency; productName = Network; diff --git a/IceCubesApp/ContentView.swift b/IceCubesApp/ContentView.swift deleted file mode 100644 index 95c2b62e..00000000 --- a/IceCubesApp/ContentView.swift +++ /dev/null @@ -1,43 +0,0 @@ -import SwiftUI -import Network - -struct ContentView: View { - @State private var statuses: [Status] = [] - @State private var client = Client(server: "mastodon.social") - - var body: some View { - List(statuses) { status in - VStack(alignment: .leading) { - HStack { - AsyncImage( - url: status.account.avatar, - content: { image in - image.resizable() - .aspectRatio(contentMode: .fit) - .cornerRadius(13) - .frame(maxWidth: 26, maxHeight: 26) - }, - placeholder: { - ProgressView() - } - ) - Text(status.account.username) - } - Text(status.content) - } - } - .task { - do { - self.statuses = try await client.fetchArray(endpoint: Timeline.pub) - } catch { - print(error.localizedDescription) - } - } - } -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff --git a/IceCubesApp/IceCubesAppApp.swift b/IceCubesApp/IceCubesAppApp.swift index 5651ec3d..6747bebe 100644 --- a/IceCubesApp/IceCubesAppApp.swift +++ b/IceCubesApp/IceCubesAppApp.swift @@ -1,10 +1,15 @@ import SwiftUI +import Timeline +import Network @main struct IceCubesAppApp: App { + @StateObject private var client = Client(server: "mastodon.social") + var body: some Scene { WindowGroup { - ContentView() + TimelineView(kind: .pub) + .environmentObject(client) } } } diff --git a/Packages/Network/Sources/Network/Client.swift b/Packages/Network/Sources/Network/Client.swift index 61c9907c..1796c9d5 100644 --- a/Packages/Network/Sources/Network/Client.swift +++ b/Packages/Network/Sources/Network/Client.swift @@ -1,6 +1,7 @@ import Foundation +import SwiftUI -public struct Client { +public class Client: ObservableObject { public enum Version: String { case v1 } diff --git a/Packages/Timeline/.gitignore b/Packages/Timeline/.gitignore new file mode 100644 index 00000000..3b298120 --- /dev/null +++ b/Packages/Timeline/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Packages/Timeline/Package.swift b/Packages/Timeline/Package.swift new file mode 100644 index 00000000..2cef5ac6 --- /dev/null +++ b/Packages/Timeline/Package.swift @@ -0,0 +1,29 @@ +// 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: "Timeline", + platforms: [ + .iOS(.v16), + ], + products: [ + .library( + name: "Timeline", + targets: ["Timeline"]), + ], + dependencies: [ + .package(name: "Network", path: "../Network"), + ], + targets: [ + .target( + name: "Timeline", + dependencies: [ + .product(name: "Network", package: "Network") + ]), + .testTarget( + name: "TimelineTests", + dependencies: ["Timeline"]), + ] +) diff --git a/Packages/Timeline/README.md b/Packages/Timeline/README.md new file mode 100644 index 00000000..6b45695c --- /dev/null +++ b/Packages/Timeline/README.md @@ -0,0 +1,3 @@ +# Timeline + +A description of this package. diff --git a/Packages/Timeline/Sources/Timeline/Status/StatusRowView.swift b/Packages/Timeline/Sources/Timeline/Status/StatusRowView.swift new file mode 100644 index 00000000..9c90184f --- /dev/null +++ b/Packages/Timeline/Sources/Timeline/Status/StatusRowView.swift @@ -0,0 +1,27 @@ +import SwiftUI +import Network + +struct StatusRowView: View { + let status: Status + + var body: some View { + VStack(alignment: .leading) { + HStack { + AsyncImage( + url: status.account.avatar, + content: { image in + image.resizable() + .aspectRatio(contentMode: .fit) + .cornerRadius(13) + .frame(maxWidth: 26, maxHeight: 26) + }, + placeholder: { + ProgressView() + } + ) + Text(status.account.username) + } + Text(status.content) + } + } +} diff --git a/Packages/Timeline/Sources/Timeline/TimelineView.swift b/Packages/Timeline/Sources/Timeline/TimelineView.swift new file mode 100644 index 00000000..4e4c8e26 --- /dev/null +++ b/Packages/Timeline/Sources/Timeline/TimelineView.swift @@ -0,0 +1,31 @@ +import SwiftUI +import Network + +public struct TimelineView: View { + public enum Kind { + case pub, hastah, home, list + } + + @EnvironmentObject private var client: Client + + @State private var statuses: [Status] = [] + + private let kind: Kind + + public init(kind: Kind) { + self.kind = kind + } + + public var body: some View { + List(statuses) { status in + StatusRowView(status: status) + } + .task { + do { + self.statuses = try await client.fetchArray(endpoint: Timeline.pub) + } catch { + print(error.localizedDescription) + } + } + } +} diff --git a/Packages/Timeline/Tests/TimelineTests/TimelineTests.swift b/Packages/Timeline/Tests/TimelineTests/TimelineTests.swift new file mode 100644 index 00000000..2a366067 --- /dev/null +++ b/Packages/Timeline/Tests/TimelineTests/TimelineTests.swift @@ -0,0 +1,11 @@ +import XCTest +@testable import Timeline + +final class TimelineTests: XCTestCase { + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(Timeline().text, "Hello, World!") + } +}