@@ -1,6 +1,7 @@ | |||||
version: 2 | version: 2 | ||||
model: | model: | ||||
finishLoading: [Bool, false] | |||||
isLoading: [Bool, false] | isLoading: [Bool, false] | ||||
join: [Bool, false] | join: [Bool, false] | ||||
textUI: [String, ""] | textUI: [String, ""] | ||||
@@ -10,6 +11,7 @@ service: | |||||
busModel | busModel | ||||
🚀shouldResetLoading: Bus.send(K.isLoading, v) | 🚀shouldResetLoading: Bus.send(K.isLoading, v) | ||||
pipes: | pipes: | ||||
finishLoading: [toggle, K.finishLoading] | |||||
isLoading: [recent, K.isLoading] | isLoading: [recent, K.isLoading] | ||||
join: [toggle, K.join] | join: [toggle, K.join] | ||||
textUI: [recent, K.textUI] | textUI: [recent, K.textUI] | ||||
@@ -2,25 +2,27 @@ import BusX | |||||
import Combine | import Combine | ||||
extension MeetupId { | extension MeetupId { | ||||
final class Formatter: ObservableObject { | |||||
let text = PassthroughSubject<String, Never>() | |||||
final class Debounce<Src, Dst> { | |||||
let v = PassthroughSubject<Src, Never>() | |||||
var subscriptions = [AnyCancellable]() | var subscriptions = [AnyCancellable]() | ||||
init( | init( | ||||
_ sec: Double, | |||||
_ handler: @escaping ((Src) -> Dst?), | |||||
_ src: String, | _ src: String, | ||||
_ dst: String | _ dst: String | ||||
) { | ) { | ||||
Bus.receive( | Bus.receive( | ||||
[src], | [src], | ||||
{ [weak self] _, v in self?.text.send(v) }, | |||||
{ [weak self] _, v in self?.v.send(v) }, | |||||
sub: &subscriptions | sub: &subscriptions | ||||
) | ) | ||||
Bus.send( | Bus.send( | ||||
dst, | dst, | ||||
text | |||||
.debounce(for: .seconds(0.2), scheduler: DispatchQueue.main) | |||||
.compactMap(formatId) | |||||
v | |||||
.debounce(for: .seconds(sec), scheduler: DispatchQueue.main) | |||||
.compactMap { (v: Src) in handler(v) } | |||||
.eraseToAnyPublisher(), | .eraseToAnyPublisher(), | ||||
sub: &subscriptions | sub: &subscriptions | ||||
) | ) |
@@ -1,5 +1,6 @@ | |||||
public extension MeetupId { | public extension MeetupId { | ||||
enum K { | enum K { | ||||
static let finishLoading = "MeetupId.finishLoading" | |||||
static let isJoinAvailable = "MeetupId.isJoinAvailable" | static let isJoinAvailable = "MeetupId.isJoinAvailable" | ||||
static let isLoading = "MeetupId.isLoading" | static let isLoading = "MeetupId.isLoading" | ||||
static let join = "MeetupId.join" | static let join = "MeetupId.join" | ||||
@@ -2,6 +2,8 @@ import Foundation | |||||
public extension MeetupId { | public extension MeetupId { | ||||
static func shouldEnableJoin(_ c: MeetupIdContext) -> Bool? { | static func shouldEnableJoin(_ c: MeetupIdContext) -> Bool? { | ||||
guard !c.isLoading.value else { return nil } | |||||
if | if | ||||
c.textUI.isRecent, | c.textUI.isRecent, | ||||
let sid = formatId(c.textUI.value) | let sid = formatId(c.textUI.value) | ||||
@@ -12,6 +14,13 @@ public extension MeetupId { | |||||
if c.join { | if c.join { | ||||
return false | return false | ||||
} | } | ||||
if | |||||
c.isLoading.isRecent, | |||||
!c.isLoading.value | |||||
{ | |||||
return true | |||||
} | |||||
return nil | return nil | ||||
} | } | ||||
@@ -4,12 +4,14 @@ import SwiftUI | |||||
extension MeetupId { | extension MeetupId { | ||||
public struct V: View { | public struct V: View { | ||||
@StateObject var fmt = MeetupId.Formatter(K.textUI, K.textApp) | |||||
let fmt = MeetupId.Debounce(5, formatId, K.textUI, K.textApp) | |||||
@StateObject var isJoinAvailable = Cord.Receive(K.isJoinAvailable, false) | @StateObject var isJoinAvailable = Cord.Receive(K.isJoinAvailable, false) | ||||
@StateObject var isLoading = Cord.Receive(K.isLoading, false) | @StateObject var isLoading = Cord.Receive(K.isLoading, false) | ||||
@StateObject var join = Cord.Button(K.join) | @StateObject var join = Cord.Button(K.join) | ||||
@StateObject var textField = Cord.TextField(K.textApp, K.textUI) | @StateObject var textField = Cord.TextField(K.textApp, K.textUI) | ||||
let pSEJ = Bus.Processor(shouldEnableJoin, K.M, K.isJoinAvailable) | |||||
let procs: [Any] = [ | |||||
Bus.Processor(shouldEnableJoin, K.M, K.isJoinAvailable), | |||||
] | |||||
public init() { } | public init() { } | ||||
@@ -17,8 +19,12 @@ extension MeetupId { | |||||
VStack(spacing: 8) { | VStack(spacing: 8) { | ||||
HStack { | HStack { | ||||
TextField("Binding-3", value: $textField.v, formatter: Cord.TextFieldValueOwner()) | TextField("Binding-3", value: $textField.v, formatter: Cord.TextFieldValueOwner()) | ||||
.disabled(isLoading.v) | |||||
.padding(8) | .padding(8) | ||||
.border(Color.blue, width: 2) | |||||
.border( | |||||
!isLoading.v ? Color.black : Color.gray, | |||||
width: !isLoading.v ? 2 : 1 | |||||
) | |||||
if isLoading.v { | if isLoading.v { | ||||
ProgressView() | ProgressView() | ||||
.padding(8) | .padding(8) | ||||
@@ -30,7 +36,7 @@ extension MeetupId { | |||||
.padding(8) | .padding(8) | ||||
.border( | .border( | ||||
isJoinAvailable.v ? Color.green : Color.gray, | isJoinAvailable.v ? Color.green : Color.gray, | ||||
width: isJoinAvailable.v ? 4 : 1 | |||||
width: isJoinAvailable.v ? 2 : 1 | |||||
) | ) | ||||
} | } | ||||
.disabled(!isJoinAvailable.v) | .disabled(!isJoinAvailable.v) | ||||
@@ -1,7 +1,7 @@ | |||||
PODS: | PODS: | ||||
- AELog (0.6.3) | - AELog (0.6.3) | ||||
- BusX (2023.12.28) | - BusX (2023.12.28) | ||||
- CordX (2023.12.28): | |||||
- CordX (2023.12.29): | |||||
- BusX | - BusX | ||||
- MeetupIdX (2023.12.28): | - MeetupIdX (2023.12.28): | ||||
- AELog | - AELog | ||||
@@ -34,7 +34,7 @@ EXTERNAL SOURCES: | |||||
SPEC CHECKSUMS: | SPEC CHECKSUMS: | ||||
AELog: f732b70f7a9d1b4c6a3676304192b3908f362133 | AELog: f732b70f7a9d1b4c6a3676304192b3908f362133 | ||||
BusX: fd22c04ad544d131e66315c1a33d87d85b19712e | BusX: fd22c04ad544d131e66315c1a33d87d85b19712e | ||||
CordX: 63515d366b217366b9562edcfef34630a7be1171 | |||||
CordX: f8ba6a8db42f9f27142948b536b9b8272f9b977c | |||||
MeetupIdX: b214163e32acffd0a5283062e9d69aaf5a5b66e6 | MeetupIdX: b214163e32acffd0a5283062e9d69aaf5a5b66e6 | ||||
MPAKX: dc592434f55edf34709f6e4f37c9ec90dcd95185 | MPAKX: dc592434f55edf34709f6e4f37c9ec90dcd95185 | ||||