Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import Foundation
// https://github.com/ekazaev/ChatLayout
enum ReactionTypes {
case delayedUpdate
}
enum InterfaceActions {
case changingKeyboardFrame
case changingContentInsets
case changingFrameSize
case sendingMessage
case scrollingToBottom
}
public final class SetActor<Option: SetAlgebra, ReactionType> {
public enum Action {
case onEmpty
case onChange
case onRemoval(_ option: Option)
case onInsertion(_ option: Option)
}
public enum ExecutionType {
case once
case eternal
}
public final class Reaction {
public let action: Action
public let type: ReactionType
public let actionBlock: () -> Void
public let executionType: ExecutionType
public init(
type: ReactionType,
action: Action,
executionType: ExecutionType = .once,
actionBlock: @escaping () -> Void
) {
self.type = type
self.action = action
self.executionType = executionType
self.actionBlock = actionBlock
}
}
public var options: Option {
didSet { optionsChanged(oldOptions: oldValue) }
}
public private(set) var reactions: [Reaction]
public init(options: Option = [], reactions: [Reaction] = []) {
self.options = options
self.reactions = reactions
optionsChanged(oldOptions: [])
}
public func add(reaction: Reaction) {
reactions.append(reaction)
}
public func remove(reaction: Reaction) {
reactions.removeAll(where: { $0 === reaction })
}
public func removeAllReactions(where shouldBeRemoved: (Reaction) throws -> Bool) throws {
try reactions.removeAll(where: shouldBeRemoved)
}
public func removeAllReactions() {
reactions.removeAll()
}
private func optionsChanged(oldOptions: Option) {
let reactions = self.reactions
let onChangeReactions = reactions.filter {
guard case .onChange = $0.action else {
return false
}
return true
}
onChangeReactions.forEach { reaction in
reaction.actionBlock()
if reaction.executionType == .once {
self.reactions.removeAll(where: { $0 === reaction })
}
}
if options.isEmpty {
let onEmptyReactions = reactions.filter {
guard case .onEmpty = $0.action else {
return false
}
return true
}
onEmptyReactions.forEach { reaction in
reaction.actionBlock()
if reaction.executionType == .once {
self.reactions.removeAll(where: { $0 === reaction })
}
}
}
let insertedOptions = options.subtracting(oldOptions)
for option in [insertedOptions] {
let onEmptyReactions = reactions.filter {
guard case let .onInsertion(newOption) = $0.action,
newOption == option else {
return false
}
return true
}
onEmptyReactions.forEach { reaction in
reaction.actionBlock()
if reaction.executionType == .once {
self.reactions.removeAll(where: { $0 === reaction })
}
}
}
let removedOptions = oldOptions.subtracting(options)
for option in [removedOptions] {
let onEmptyReactions = reactions.filter {
guard case let .onRemoval(newOption) = $0.action,
newOption == option else {
return false
}
return true
}
onEmptyReactions.forEach { reaction in
reaction.actionBlock()
if reaction.executionType == .once {
self.reactions.removeAll(where: { $0 === reaction })
}
}
}
}
}
extension SetActor where ReactionType: Equatable {
public func removeAllReactions(_ type: ReactionType) {
reactions.removeAll(where: { $0.type == type })
}
}