mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Refactor TelegramNotices, TelegramPermissions, PhotoResources, ImageTransparency, TinyThumbnail
This commit is contained in:
283
submodules/TelegramCallsUI/Sources/CallFeedbackController.swift
Normal file
283
submodules/TelegramCallsUI/Sources/CallFeedbackController.swift
Normal file
@@ -0,0 +1,283 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import OverlayStatusController
|
||||
import AccountContext
|
||||
|
||||
private enum CallFeedbackReason: Int32, CaseIterable {
|
||||
case echo
|
||||
case noise
|
||||
case interruption
|
||||
case distortedSpeech
|
||||
case silentLocal
|
||||
case silentRemote
|
||||
case dropped
|
||||
|
||||
var hashtag: String {
|
||||
switch self {
|
||||
case .echo:
|
||||
return "echo"
|
||||
case .noise:
|
||||
return "noise"
|
||||
case .interruption:
|
||||
return "interruptions"
|
||||
case .distortedSpeech:
|
||||
return "distorted_speech"
|
||||
case .silentLocal:
|
||||
return "silent_local"
|
||||
case .silentRemote:
|
||||
return "silent_remote"
|
||||
case .dropped:
|
||||
return "dropped"
|
||||
}
|
||||
}
|
||||
|
||||
static func localizedString(for reason: CallFeedbackReason, strings: PresentationStrings) -> String {
|
||||
switch reason {
|
||||
case .echo:
|
||||
return strings.CallFeedback_ReasonEcho
|
||||
case .noise:
|
||||
return strings.CallFeedback_ReasonNoise
|
||||
case .interruption:
|
||||
return strings.CallFeedback_ReasonInterruption
|
||||
case .distortedSpeech:
|
||||
return strings.CallFeedback_ReasonDistortedSpeech
|
||||
case .silentLocal:
|
||||
return strings.CallFeedback_ReasonSilentLocal
|
||||
case .silentRemote:
|
||||
return strings.CallFeedback_ReasonSilentRemote
|
||||
case .dropped:
|
||||
return strings.CallFeedback_ReasonDropped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CallFeedbackControllerArguments {
|
||||
let updateComment: (String) -> Void
|
||||
let toggleReason: (CallFeedbackReason, Bool) -> Void
|
||||
let toggleIncludeLogs: (Bool) -> Void
|
||||
|
||||
init(updateComment: @escaping (String) -> Void, toggleReason: @escaping (CallFeedbackReason, Bool) -> Void, toggleIncludeLogs: @escaping (Bool) -> Void) {
|
||||
self.updateComment = updateComment
|
||||
self.toggleReason = toggleReason
|
||||
self.toggleIncludeLogs = toggleIncludeLogs
|
||||
}
|
||||
}
|
||||
|
||||
private enum CallFeedbackControllerSection: Int32 {
|
||||
case reasons
|
||||
case comment
|
||||
case logs
|
||||
}
|
||||
|
||||
private enum CallFeedbackControllerEntry: ItemListNodeEntry {
|
||||
case reasonsHeader(PresentationTheme, String)
|
||||
case reason(PresentationTheme, CallFeedbackReason, String, Bool)
|
||||
case comment(PresentationTheme, String, String)
|
||||
case includeLogs(PresentationTheme, String, Bool)
|
||||
case includeLogsInfo(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .reasonsHeader, .reason:
|
||||
return CallFeedbackControllerSection.reasons.rawValue
|
||||
case .comment:
|
||||
return CallFeedbackControllerSection.comment.rawValue
|
||||
case .includeLogs, .includeLogsInfo:
|
||||
return CallFeedbackControllerSection.logs.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .reasonsHeader:
|
||||
return 0
|
||||
case let .reason(_, reason, _, _):
|
||||
return 1 + reason.rawValue
|
||||
case .comment:
|
||||
return 100
|
||||
case .includeLogs:
|
||||
return 101
|
||||
case .includeLogsInfo:
|
||||
return 102
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: CallFeedbackControllerEntry, rhs: CallFeedbackControllerEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .reasonsHeader(lhsTheme, lhsText):
|
||||
if case let .reasonsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .reason(lhsTheme, lhsReason, lhsText, lhsValue):
|
||||
if case let .reason(rhsTheme, rhsReason, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsReason == rhsReason, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .comment(lhsTheme, lhsText, lhsValue):
|
||||
if case let .comment(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .includeLogs(lhsTheme, lhsText, lhsValue):
|
||||
if case let .includeLogs(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .includeLogsInfo(lhsTheme, lhsText):
|
||||
if case let .includeLogsInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: CallFeedbackControllerEntry, rhs: CallFeedbackControllerEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(_ arguments: CallFeedbackControllerArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .reasonsHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .reason(theme, reason, title, value):
|
||||
return ItemListSwitchItem(theme: theme, title: title, value: value, maximumNumberOfLines: 2, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleReason(reason, value)
|
||||
})
|
||||
case let .comment(theme, text, placeholder):
|
||||
return ItemListMultilineInputItem(theme: theme, text: text, placeholder: placeholder, maxLength: nil, sectionId: self.section, style: .blocks, textUpdated: { updatedText in
|
||||
arguments.updateComment(updatedText)
|
||||
}, action: {})
|
||||
case let .includeLogs(theme, title, value):
|
||||
return ItemListSwitchItem(theme: theme, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleIncludeLogs(value)
|
||||
})
|
||||
case let .includeLogsInfo(theme, text):
|
||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct CallFeedbackState: Equatable {
|
||||
let reasons: Set<CallFeedbackReason>
|
||||
let comment: String
|
||||
let includeLogs: Bool
|
||||
|
||||
init(reasons: Set<CallFeedbackReason> = Set(), comment: String = "", includeLogs: Bool = true) {
|
||||
self.reasons = reasons
|
||||
self.comment = comment
|
||||
self.includeLogs = includeLogs
|
||||
}
|
||||
|
||||
func withUpdatedReasons(_ reasons: Set<CallFeedbackReason>) -> CallFeedbackState {
|
||||
return CallFeedbackState(reasons: reasons, comment: self.comment, includeLogs: self.includeLogs)
|
||||
}
|
||||
|
||||
func withUpdatedComment(_ comment: String) -> CallFeedbackState {
|
||||
return CallFeedbackState(reasons: self.reasons, comment: comment, includeLogs: self.includeLogs)
|
||||
}
|
||||
|
||||
func withUpdatedIncludeLogs(_ includeLogs: Bool) -> CallFeedbackState {
|
||||
return CallFeedbackState(reasons: self.reasons, comment: self.comment, includeLogs: includeLogs)
|
||||
}
|
||||
}
|
||||
|
||||
private func callFeedbackControllerEntries(theme: PresentationTheme, strings: PresentationStrings, state: CallFeedbackState) -> [CallFeedbackControllerEntry] {
|
||||
var entries: [CallFeedbackControllerEntry] = []
|
||||
|
||||
entries.append(.reasonsHeader(theme, strings.CallFeedback_WhatWentWrong))
|
||||
for reason in CallFeedbackReason.allCases {
|
||||
entries.append(.reason(theme, reason, CallFeedbackReason.localizedString(for: reason, strings: strings), state.reasons.contains(reason)))
|
||||
}
|
||||
|
||||
entries.append(.comment(theme, state.comment, strings.CallFeedback_AddComment))
|
||||
|
||||
entries.append(.includeLogs(theme, strings.CallFeedback_IncludeLogs, state.includeLogs))
|
||||
entries.append(.includeLogsInfo(theme, strings.CallFeedback_IncludeLogsInfo))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
public func callFeedbackController(sharedContext: SharedAccountContext, account: Account, callId: CallId, rating: Int, userInitiated: Bool) -> ViewController {
|
||||
let initialState = CallFeedbackState()
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((CallFeedbackState) -> CallFeedbackState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var presentControllerImpl: ((ViewController) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
|
||||
let arguments = CallFeedbackControllerArguments(updateComment: { value in
|
||||
updateState { $0.withUpdatedComment(value) }
|
||||
}, toggleReason: { reason, value in
|
||||
updateState { current in
|
||||
var reasons = current.reasons
|
||||
if value {
|
||||
reasons.insert(reason)
|
||||
} else {
|
||||
reasons.remove(reason)
|
||||
}
|
||||
return current.withUpdatedReasons(reasons)
|
||||
}
|
||||
}, toggleIncludeLogs: { value in
|
||||
updateState { $0.withUpdatedIncludeLogs(value) }
|
||||
})
|
||||
|
||||
let signal = combineLatest(sharedContext.presentationData, statePromise.get())
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<CallFeedbackControllerEntry>, CallFeedbackControllerEntry.ItemGenerationArguments)) in
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||
dismissImpl?()
|
||||
})
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.CallFeedback_Send), style: .bold, enabled: true, action: {
|
||||
var comment = state.comment
|
||||
var hashtags = ""
|
||||
for reason in CallFeedbackReason.allCases {
|
||||
if state.reasons.contains(reason) {
|
||||
if !hashtags.isEmpty {
|
||||
hashtags.append(" ")
|
||||
}
|
||||
hashtags.append("#\(reason.hashtag)")
|
||||
}
|
||||
}
|
||||
if !comment.isEmpty && !state.reasons.isEmpty {
|
||||
comment.append("\n")
|
||||
}
|
||||
comment.append(hashtags)
|
||||
|
||||
let _ = rateCallAndSendLogs(account: account, callId: callId, starsCount: rating, comment: comment, userInitiated: userInitiated, includeLogs: state.includeLogs).start()
|
||||
dismissImpl?()
|
||||
|
||||
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .starSuccess(presentationData.strings.CallFeedback_Success)))
|
||||
})
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.CallFeedback_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: callFeedbackControllerEntries(theme: presentationData.theme, strings: presentationData.strings, state: state), style: .blocks, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
||||
|
||||
let controller = ItemListController(sharedContext: sharedContext, state: signal)
|
||||
presentControllerImpl = { [weak controller] c in
|
||||
controller?.present(c, in: .window(.root))
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
controller?.dismiss()
|
||||
}
|
||||
return controller
|
||||
}
|
||||
Reference in New Issue
Block a user