Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Modules/Sources/APIClient/APIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public struct APIClient: Sendable {
public var editPost: @Sendable (_ request: PostEditRequest) async throws -> PostSendResponse
public var deletePosts: @Sendable (_ postIds: [Int]) async throws -> Bool
public var postKarma: @Sendable (_ postId: Int, _ isUp: Bool) async throws -> Bool
public var voteInTopicPoll: @Sendable (_ topicId: Int, _ selections: [[Int]]) async throws -> Bool

// Favorites
public var getFavorites: @Sendable (_ request: FavoritesRequest, _ policy: CachePolicy) async throws -> AsyncThrowingStream<Favorite, any Error>
Expand Down Expand Up @@ -357,6 +358,13 @@ extension APIClient: DependencyKey {
return status == 0
},

voteInTopicPoll: { topicId, selections in
let command = ForumCommand.Topic.Poll.vote(topicId: topicId, selections: selections)
let response = try await api.send(command)
let status = Int(response.getResponseStatus())!
return status == 0
},

// MARK: - Favorites

getFavorites: { request, policy in
Expand Down Expand Up @@ -570,6 +578,9 @@ extension APIClient: DependencyKey {
postKarma: { _, _ in
return true
},
voteInTopicPoll: { _, _ in
return true
},
getFavorites: { _, _ in
let (stream, continuation) = AsyncThrowingStream.makeStream(of: Favorite.self)
continuation.yield(with: .success(.mock))
Expand Down
1 change: 1 addition & 0 deletions Modules/Sources/AnalyticsClient/Events/TopicEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation
public enum TopicEvent: Event {
case onRefresh
case topicHatOpenButtonTapped
case topicPollOpenButtonTapped
case userTapped(Int)
case urlTapped(URL)
case imageTapped(URL)
Expand Down
51 changes: 34 additions & 17 deletions Modules/Sources/Models/Forum/Topic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public struct Topic: Codable, Sendable, Identifiable, Hashable {
self.options = options
}

public struct Choice: Sendable, Codable, Hashable {
public struct Choice: Sendable, Codable, Hashable, Identifiable {
public let id: Int
public let votes: Int
public let name: String
Expand All @@ -53,12 +53,14 @@ public struct Topic: Codable, Sendable, Identifiable, Hashable {
}
}

public struct Option: Sendable, Codable, Hashable {
public struct Option: Sendable, Codable, Hashable, Identifiable {
public let id: Int
public let name: String
public let several: Bool
public let choices: [Choice]

public init(name: String, several: Bool, choices: [Choice]) {
public init(id: Int, name: String, several: Bool, choices: [Choice]) {
self.id = id
self.name = name
self.several = several
self.choices = choices
Expand Down Expand Up @@ -110,20 +112,7 @@ public extension Topic {
authorName: "4spander",
curatorId: 6176341,
curatorName: "AirFlare",
poll: Poll(
name: "Some simple poll...",
voted: false,
totalVotes: 2134,
options: [
Poll.Option(
name: "Select this choise...",
several: false,
choices: [
Poll.Choice(id: 2, name: "First choice", votes: 2)
]
)
]
),
poll: .mock,
postsCount: 5005,
posts: [
.mock(id: 0), .mock(id: 1), .mock(id: 2)
Expand All @@ -133,3 +122,31 @@ public extension Topic {
]
)
}

public extension Topic.Poll {
static let mock = Topic.Poll(
name: "Some simple poll...",
voted: false,
totalVotes: 12,
options: [
.init(
id: 0,
name: "Select not several...",
several: false,
choices: [
.init(id: 2, name: "First choice", votes: 2),
.init(id: 3, name: "Second choice", votes: 4)
]
),
.init(
id: 1,
name: "Select several...",
several: true,
choices: [
.init(id: 4, name: "First choice", votes: 4),
.init(id: 5, name: "Second choice", votes: 2)
]
),
]
)
}
3 changes: 2 additions & 1 deletion Modules/Sources/ParsingClient/Parsers/TopicParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public struct TopicParser {

private static func parsePollOptions(_ optionsRaw: [[Any]]) throws(ParsingError) -> [Topic.Poll.Option] {
var options: [Topic.Poll.Option] = []
for option in optionsRaw {
for (idx, option) in optionsRaw.enumerated() {
guard let name = option[safe: 0] as? String,
let several = option[safe: 1] as? Int,
let names = option[safe: 2] as? [String],
Expand All @@ -151,6 +151,7 @@ public struct TopicParser {
}

let option = Topic.Poll.Option(
id: idx,
name: name,
several: several == 1 ? true : false,
choices: choices
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ extension TopicFeature {
.view(.onNextAppear),
.view(.finishedPostAnimation),
.view(.changeKarmaTapped),
.view(.topicPollVoteButtonTapped),
.internal(.loadTypes),
.internal(.goToPost),
.internal(.jumpRequestFailed),
.internal(.changeKarma),
.internal(.voteInPoll),
.internal(.load),
.internal(.refresh),
.pageNavigation,
Expand All @@ -41,6 +43,9 @@ extension TopicFeature {
case .view(.topicHatOpenButtonTapped):
analytics.log(TopicEvent.topicHatOpenButtonTapped)

case .view(.topicPollOpenButtonTapped):
analytics.log(TopicEvent.topicPollOpenButtonTapped)

case let .view(.userTapped(userId: userId)):
analytics.log(TopicEvent.userTapped(userId))

Expand Down
50 changes: 50 additions & 0 deletions Modules/Sources/TopicFeature/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
{
"sourceLanguage" : "en",
"strings" : {
"%lld people voted" : {
"localizations" : {
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Проголосовало %lld чел."
}
}
}
},
"Add to favorites" : {
"localizations" : {
"ru" : {
Expand Down Expand Up @@ -137,6 +147,16 @@
}
}
},
"Poll" : {
"localizations" : {
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Опрос"
}
}
}
},
"Post deleted" : {
"localizations" : {
"ru" : {
Expand Down Expand Up @@ -227,6 +247,16 @@
}
}
},
"Show results" : {
"localizations" : {
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Результаты"
}
}
}
},
"Today, %@" : {
"localizations" : {
"ru" : {
Expand Down Expand Up @@ -267,6 +297,26 @@
}
}
},
"Vote" : {
"localizations" : {
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Голосовать"
}
}
}
},
"Vote approved" : {
"localizations" : {
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Голос засчитан"
}
}
}
},
"Write Post" : {
"localizations" : {
"ru" : {
Expand Down
30 changes: 30 additions & 0 deletions Modules/Sources/TopicFeature/TopicFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public struct TopicFeature: Reducer, Sendable {
static let favoriteRemoved = LocalizedStringResource("Removed from favorites", bundle: .module)
static let postDeleted = LocalizedStringResource("Post deleted", bundle: .module)
static let postKarmaChanged = LocalizedStringResource("Post karma changed", bundle: .module)
static let topicVoteApproved = LocalizedStringResource("Vote approved", bundle: .module)
}

// MARK: - Destinations
Expand Down Expand Up @@ -81,6 +82,7 @@ public struct TopicFeature: Reducer, Sendable {
}

var shouldShowTopicHatButton = false
var shouldShowTopicPollButton = true

public init(
topicId: Int,
Expand Down Expand Up @@ -117,6 +119,8 @@ public struct TopicFeature: Reducer, Sendable {
case onRefresh
case finishedPostAnimation
case topicHatOpenButtonTapped
case topicPollOpenButtonTapped
case topicPollVoteButtonTapped([Int: Set<Int>])
case changeKarmaTapped(Int, Bool)
case userTapped(Int)
case urlTapped(URL)
Expand All @@ -132,6 +136,7 @@ public struct TopicFeature: Reducer, Sendable {
case refresh
case goToPost(postId: Int, offset: Int, forceRefresh: Bool)
case changeKarma(postId: Int, isUp: Bool)
case voteInPoll(selections: [[Int]])
case loadTopic(Int)
case loadTypes([[TopicTypeUI]])
case topicResponse(Result<Topic, any Error>)
Expand Down Expand Up @@ -228,6 +233,16 @@ public struct TopicFeature: Reducer, Sendable {
state.shouldShowTopicHatButton = false
return .none

case .view(.topicPollOpenButtonTapped):
state.shouldShowTopicPollButton = false
return .none

case .view(.topicPollVoteButtonTapped(let selections)):
let values = selections.sorted(by: { $0.key < $1.key }).map {
Array($0.value)
}
return .send(.internal(.voteInPoll(selections: values)))

case let .view(.userTapped(id)):
return .send(.delegate(.openUser(id: id)))

Expand Down Expand Up @@ -397,6 +412,20 @@ public struct TopicFeature: Reducer, Sendable {
jumpTo(.post(id: postId), true, &state)
)

case .internal(.voteInPoll(let selections)):
return .concatenate(
.run { [topicId = state.topicId] _ in
let status = try await apiClient.voteInTopicPoll(
topicId: topicId,
selections: selections
)
let voteApproved = ToastMessage(text: Localization.topicVoteApproved, haptic: .success)
await toastClient.showToast(status ? voteApproved : .whoopsSomethingWentWrong)
}.cancellable(id: CancelID.loading),

.send(.internal(.refresh))
)

case let .internal(.loadTopic(offset)):
if !state.isRefreshing {
state.isLoadingTopic = true
Expand Down Expand Up @@ -465,6 +494,7 @@ public struct TopicFeature: Reducer, Sendable {

state.isLoadingTopic = false
state.isRefreshing = false
state.shouldShowTopicPollButton = true
state.shouldShowTopicHatButton = !state.pageNavigation.isFirstPage

reportFullyDisplayed(&state)
Expand Down
Loading
Loading