Skip to content

Commit

Permalink
Replace Twitter with Mastodon feed (#488)
Browse files Browse the repository at this point in the history
  • Loading branch information
mltbnz authored Jul 11, 2023
1 parent 9e1a141 commit 61ad9f4
Show file tree
Hide file tree
Showing 44 changed files with 737 additions and 1,204 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
"version" : "0.2.0"
}
},
{
"identity" : "mastodonkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mltbnz/MastodonKit.git",
"state" : {
"branch" : "master",
"revision" : "db829c74ba16f809c9b894c356ee2df3ffb284ab"
}
},
{
"identity" : "swift-case-paths",
"kind" : "remoteSourceControl",
Expand Down
20 changes: 12 additions & 8 deletions CriticalMapsKit/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ let package = Package(
.library(name: "GuideFeature", targets: ["GuideFeature"]),
.library(name: "MapFeature", targets: ["MapFeature"]),
.library(name: "SettingsFeature", targets: ["SettingsFeature"]),
.library(name: "TwitterFeature", targets: ["SocialFeature"])
.library(name: "MastodonFeedFeature", targets: ["SocialFeature"]),
.library(name: "Styleguide", targets: ["Styleguide"])
],
dependencies: [
.package(url: "https://github.com/MarcoEidinger/SwiftFormatPlugin", from: "0.49.18"),
Expand All @@ -31,7 +32,8 @@ let package = Package(
),
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "0.1.0"),
.package(url: "https://github.com/vtourraine/AcknowList.git", .upToNextMajor(from: "2.1.0")),
.package(url: "https://github.com/lucaszischka/BottomSheet.git", from: "3.1.0")
.package(url: "https://github.com/lucaszischka/BottomSheet.git", from: "3.1.0"),
.package(url: "https://github.com/mltbnz/MastodonKit.git", branch: "master")
],
targets: [
.target(
Expand Down Expand Up @@ -194,7 +196,7 @@ let package = Package(
dependencies: [
"ChatFeature",
"L10n",
"TwitterFeedFeature",
"MastodonFeedFeature",
"UserDefaultsClient",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
]
Expand All @@ -219,15 +221,16 @@ let package = Package(
]
),
.target(
name: "TwitterFeedFeature",
name: "MastodonFeedFeature",
dependencies: [
"ApiClient",
"Logger",
"SharedDependencies",
"SharedModels",
"Styleguide",
"UIApplicationClient",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "MastodonKit", package: "MastodonKit")
]
),
.target(
Expand Down Expand Up @@ -352,16 +355,17 @@ package.targets.append(contentsOf: [
exclude: ["__Snapshots__"]
),
.testTarget(
name: "TwitterFeedFeatureTests",
name: "MastodonFeedFeatureTests",
dependencies: [
"Helpers",
"SharedDependencies",
"TwitterFeedFeature",
"MastodonFeedFeature",
"TestHelper",
.product(
name: "ComposableArchitecture",
package: "swift-composable-architecture"
)
),
.product(name: "MastodonKit", package: "MastodonKit")
],
exclude: [
"__Snapshots__"
Expand Down
1 change: 0 additions & 1 deletion CriticalMapsKit/Sources/ApiClient/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public struct Endpoint {
public extension Endpoint {
static let locations = Self(baseUrl: cdnBaseUrl, pathComponents: ["locations"])
static let chatMessages = Self(baseUrl: apiGWBaseUrl, pathComponents: ["messages"])
static let twitter = Self(baseUrl: apiGWBaseUrl, pathComponents: ["twitter"])
static let criticalmass = Self(baseUrl: criticalmassInEndpoint, pathComponents: ["api", "ride"])
}

Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion CriticalMapsKit/Sources/AppFeature/AppFeatureCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import L10n
import Logger
import MapFeature
import MapKit
import MastodonFeedFeature
import NextRideFeature
import PathMonitorClient
import SettingsFeature
import SharedDependencies
import SharedModels
import SocialFeature
import TwitterFeedFeature
import UIApplicationClient
import UserDefaultsClient

Expand Down
2 changes: 1 addition & 1 deletion CriticalMapsKit/Sources/AppFeature/AppNavigationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import GuideFeature
import Helpers
import L10n
import MapFeature
import MastodonFeedFeature
import SettingsFeature
import SocialFeature
import Styleguide
import SwiftUI
import TwitterFeedFeature

public struct AppNavigationView: View {
public typealias State = AppFeature.State
Expand Down
15 changes: 15 additions & 0 deletions CriticalMapsKit/Sources/MastodonFeedFeature/Dependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ComposableArchitecture
import Foundation

private enum MastodonServiceKey: DependencyKey {
static let liveValue = TootService.live()
static let testValue = TootService.noop
}


public extension DependencyValues {
var tootService: TootService {
get { self[MastodonServiceKey.self] }
set { self[MastodonServiceKey.self] = newValue }
}
}
107 changes: 107 additions & 0 deletions CriticalMapsKit/Sources/MastodonFeedFeature/MastodonFeedView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import ComposableArchitecture
import Foundation
import MastodonKit
import SharedModels
import Styleguide
import SwiftUI

public struct MastodonFeedView: View {
public struct MastodonFeedViewState: Equatable {
public let displayPlaceholder: Bool

public init(_ state: TootFeedFeature.State) {
if state.isLoading && !state.isRefreshing {
displayPlaceholder = true
} else {
displayPlaceholder = false
}
}
}

let store: StoreOf<TootFeedFeature>
@ObservedObject var viewStore: ViewStore<MastodonFeedViewState, TootFeedFeature.Action>

public init(store: StoreOf<TootFeedFeature>) {
self.store = store
viewStore = ViewStore(store.scope(state: MastodonFeedViewState.init, action: { $0 }))
}

public var body: some View {
TootsListView(store: self.store)
.navigationBarTitleDisplayMode(.inline)
.onAppear { viewStore.send(.onAppear) }
}
}

// MARK: Preview

struct MastodonFeedView_Previews: PreviewProvider {
static var previews: some View {
MastodonFeedView(
store: Store<TootFeedFeature.State, TootFeedFeature.Action>(
initialState: .init(),
reducer: TootFeedFeature()._printChanges()
)
)
}
}

public extension Array where Element == MastodonKit.Status {
static let placeHolder: Self = [0, 1, 2, 3, 4].map {
Status(
id: String($0),
uri: "",
url: nil,
account: .init(
id: String($0),
username: "@criticalmaps",
acct: "@[email protected]",
displayName: "Critical Maps",
note: "",
url: "",
avatar: "",
avatarStatic: "",
header: "",
headerStatic: "",
locked: false,
createdAt: .init(timeIntervalSince1970: TimeInterval(1635521516)),
followersCount: 11,
followingCount: 8,
statusesCount: 0
),
inReplyToID: nil,
inReplyToAccountID: nil,
content: String("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed invidunt ut labore et dolore".dropLast($0)),
createdAt: .init(timeIntervalSince1970: TimeInterval(1635521516)),
emojis: [],
reblogsCount: 0,
favouritesCount: 0,
reblogged: nil,
favourited: nil,
bookmarked: nil,
sensitive: nil,
spoilerText: "",
visibility: .public,
mediaAttachments: [],
mentions: [],
tags: [],
application: nil,
language: nil,
reblog: nil,
pinned: nil,
card: nil,
repliesCount: 0
)
}
}

extension Store where State == TootFeedFeature.State, Action == TootFeedFeature.Action {
static let placeholder = Store(
initialState: .init(
toots: IdentifiedArray(
uniqueElements: [MastodonKit.Status].placeHolder
)
),
reducer: EmptyReducer()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@ import Foundation
import Helpers
import L10n
import Logger
import MastodonKit
import SharedDependencies
import SharedModels
import Styleguide
import SwiftUI
import UIApplicationClient

public struct TwitterFeedFeature: ReducerProtocol {
public struct TootFeedFeature: ReducerProtocol {
public init() {}

@Dependency(\.mainQueue) public var mainQueue
@Dependency(\.twitterService) public var twitterService
@Dependency(\.tootService) public var tootService
@Dependency(\.uiApplicationClient) public var uiApplicationClient

// MARK: State

public struct State: Equatable {
public var tweets: IdentifiedArrayOf<TweetFeature.State>
public var twitterFeedIsLoading = false
public var toots: IdentifiedArrayOf<MastodonKit.Status>
public var isLoading = false
public var isRefreshing = false
public var error: ErrorState?

public init(tweets: IdentifiedArrayOf<TweetFeature.State> = IdentifiedArray(uniqueElements: [Tweet].placeHolder, id: \.id)) {
self.tweets = tweets
public init(
toots: IdentifiedArrayOf<Status> = []
) {
self.toots = toots
}
}

Expand All @@ -35,8 +38,8 @@ public struct TwitterFeedFeature: ReducerProtocol {
case onAppear
case refresh
case fetchData
case fetchDataResponse(TaskResult<[Tweet]>)
case tweet(id: TweetFeature.State.ID, action: TweetFeature.Action)
case fetchDataResponse(TaskResult<[Status]>)
case toot(id: TootFeature.State.ID, action: TootFeature.Action)
}


Expand All @@ -52,39 +55,45 @@ public struct TwitterFeedFeature: ReducerProtocol {
return EffectTask(value: .fetchData)

case .fetchData:
state.twitterFeedIsLoading = true
state.isLoading = true
return .task {
await .fetchDataResponse(TaskResult { try await twitterService.getTweets() })
await .fetchDataResponse(TaskResult { try await tootService.getToots() })
}

case let .fetchDataResponse(.success(tweets)):
state.twitterFeedIsLoading = false
case let .fetchDataResponse(.success(toots)):
state.isLoading = false
state.isRefreshing = false

if tweets.isEmpty {
state.tweets = .init(uniqueElements: [])
if toots.isEmpty {
state.toots = .init(uniqueElements: [])
return .none
}

state.tweets = IdentifiedArray(uniqueElements: tweets)
state.toots = IdentifiedArray(uniqueElements: toots)
return .none
case let .fetchDataResponse(.failure(error)):
logger.debug("Failed to fetch tweets with error: \(error.localizedDescription)")
state.isRefreshing = false
state.twitterFeedIsLoading = false
state.isLoading = false
state.error = .init(
title: L10n.ErrorState.title,
body: L10n.ErrorState.message,
error: .init(error: error)
)
return .none

case .tweet:
case .toot:
return .none
}
}
.forEach(\.tweets, action: /TwitterFeedFeature.Action.tweet(id:action:)) {
TweetFeature()
.forEach(\.toots, action: /TootFeedFeature.Action.toot) {
TootFeature()
}
}
}

extension MastodonKit.Status: Identifiable {
public static func == (lhs: MastodonKit.Status, rhs: MastodonKit.Status) -> Bool {
lhs.id == rhs.id
}
}
Loading

0 comments on commit 61ad9f4

Please sign in to comment.