|
| 1 | +// |
| 2 | +// MeTopic.swift |
| 3 | +// TinodeSDK |
| 4 | +// |
| 5 | +// Copyright © 2020 Tinode. All rights reserved. |
| 6 | +// |
| 7 | + |
| 8 | +import Foundation |
| 9 | + |
| 10 | + |
| 11 | +open class MeTopic<DP: Codable & Mergeable>: Topic<DP, PrivateType, DP, PrivateType> { |
| 12 | + public init(tinode: Tinode?, l: MeTopic<DP>.Listener?) { |
| 13 | + super.init(tinode: tinode, name: Tinode.kTopicMe, l: l) |
| 14 | + } |
| 15 | + public init(tinode: Tinode?, desc: Description<DP, PrivateType>) { |
| 16 | + super.init(tinode: tinode, name: Tinode.kTopicMe, desc: desc) |
| 17 | + } |
| 18 | + |
| 19 | + override public var subsUpdated: Date? { |
| 20 | + get { return tinode?.topicsUpdated } |
| 21 | + } |
| 22 | + |
| 23 | + override func loadSubs() -> Int { |
| 24 | + // Don't attempt to load subscriptions: 'me' subscriptions are stored as topics. |
| 25 | + return 0 |
| 26 | + } |
| 27 | + |
| 28 | + override public func topicLeft(unsub: Bool?, code: Int?, reason: String?) { |
| 29 | + super.topicLeft(unsub: unsub, code: code, reason: reason) |
| 30 | + if let topics = tinode?.getTopics() { |
| 31 | + for t in topics { |
| 32 | + t.online = false |
| 33 | + } |
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + override public func updateMode(update: String) -> PromisedReply<ServerMessage>? { |
| 38 | + var acs = accessMode |
| 39 | + if acs == nil { |
| 40 | + acs = Acs() |
| 41 | + } |
| 42 | + |
| 43 | + let mode = AcsHelper(ah: acs!.want) |
| 44 | + if mode.update(from: update) { |
| 45 | + return setSubscription(sub: MetaSetSub(user: nil, mode: mode.description)) |
| 46 | + } |
| 47 | + |
| 48 | + // The state is unchanged, return resolved promise. |
| 49 | + return PromisedReply<ServerMessage>(value: ServerMessage()) |
| 50 | + } |
| 51 | + |
| 52 | + override internal func update(acsMap: [String:String]?, sub: MetaSetSub) { |
| 53 | + var newAcs: Acs |
| 54 | + if let acsMap = acsMap { |
| 55 | + newAcs = Acs(from: acsMap) |
| 56 | + } else { |
| 57 | + newAcs = Acs() |
| 58 | + newAcs.want = AcsHelper(str: sub.mode) |
| 59 | + } |
| 60 | + |
| 61 | + var changed = false |
| 62 | + var acs = accessMode |
| 63 | + if acs == nil { |
| 64 | + acs = newAcs |
| 65 | + changed = true |
| 66 | + } else { |
| 67 | + changed = acs!.merge(from: newAcs) |
| 68 | + } |
| 69 | + |
| 70 | + if changed { |
| 71 | + accessMode = acs |
| 72 | + self.store?.topicUpdate(topic: self) |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + override public func routePres(pres: MsgServerPres) { |
| 77 | + let what = MsgServerPres.parseWhat(what: pres.what) |
| 78 | + if what == .kTerm { |
| 79 | + // The 'me' topic itself is detached. Mark as unsubscribed. |
| 80 | + super.routePres(pres: pres) |
| 81 | + return |
| 82 | + } |
| 83 | + |
| 84 | + if what == .kUpd && Tinode.kTopicMe == pres.src { |
| 85 | + // Me's desc was updated, fetch the updated version. |
| 86 | + getMeta(query: getMetaGetBuilder().withDesc().build()) |
| 87 | + return |
| 88 | + } |
| 89 | + |
| 90 | + // "what":"tags" has src == nil |
| 91 | + if let topic = pres.src != nil ? tinode!.getTopic(topicName: pres.src!) : nil { |
| 92 | + switch what { |
| 93 | + case .kOn: // topic came online |
| 94 | + topic.online = true |
| 95 | + case .kOff: // topic went offline |
| 96 | + topic.online = false |
| 97 | + topic.lastSeen = LastSeen(when: Date(), ua: nil) |
| 98 | + case .kMsg: // new message received |
| 99 | + topic.seq = pres.seq |
| 100 | + if pres.act == nil || tinode!.isMe(uid: pres.act!) { |
| 101 | + assignRead(to: topic, read: pres.seq) |
| 102 | + } |
| 103 | + topic.touched = Date() |
| 104 | + case .kUpd: // pub/priv updated |
| 105 | + getMeta(query: getMetaGetBuilder().withSub(user: pres.src).build()) |
| 106 | + case .kAcs: // access mode changed |
| 107 | + if topic.updateAccessMode(ac: pres.dacs) { |
| 108 | + self.store?.topicUpdate(topic: topic) |
| 109 | + } |
| 110 | + case .kUa: // user agent changed |
| 111 | + topic.lastSeen = LastSeen(when: Date(), ua: pres.ua) |
| 112 | + case .kRecv: // user's other session marked some messages as received |
| 113 | + assignRecv(to: topic, recv: pres.seq) |
| 114 | + case .kRead: // user's other session marked some messages as read |
| 115 | + assignRead(to: topic, read: pres.seq) |
| 116 | + case .kGone: |
| 117 | + // If topic is unknown (==nil), then we don't care to unregister it. |
| 118 | + topic.persist(false) |
| 119 | + tinode!.stopTrackingTopic(topicName: pres.src!) |
| 120 | + case .kDel: // messages deleted |
| 121 | + // Explicitly ignored: 'me' topic has no messages. |
| 122 | + break |
| 123 | + default: |
| 124 | + Tinode.log.error("ME.pres message - unknown what: %@", String(describing: pres.what)) |
| 125 | + } |
| 126 | + } else { |
| 127 | + // New topic |
| 128 | + switch what { |
| 129 | + case .kAcs: |
| 130 | + let acs = Acs() |
| 131 | + acs.update(from: pres.dacs) |
| 132 | + if acs.isModeDefined { |
| 133 | + getMeta(query: getMetaGetBuilder().withSub(user: pres.src).build()) |
| 134 | + } else { |
| 135 | + Tinode.log.error("ME.acs - unexpected access mode: %@", String(describing: pres.dacs)) |
| 136 | + } |
| 137 | + case .kTags: |
| 138 | + // Account tags updated |
| 139 | + getMeta(query: getMetaGetBuilder().withTags().build()) |
| 140 | + default: |
| 141 | + Tinode.log.error("ME.pres - topic not found: what = %@, src = %@", |
| 142 | + String(describing: pres.what), String(describing: pres.src)) |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + if (what == MsgServerPres.What.kGone) { |
| 147 | + listener?.onSubsUpdated() |
| 148 | + } |
| 149 | + listener?.onPres(pres: pres) |
| 150 | + } |
| 151 | + |
| 152 | + fileprivate func assignRecv(to topic: TopicProto, recv seq: Int?) { |
| 153 | + if (topic.recv ?? -1) < (seq ?? -1) { |
| 154 | + topic.recv = seq |
| 155 | + self.store?.setRecv(topic: topic, recv: seq!) |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + fileprivate func assignRead(to topic: TopicProto, read seq: Int?) { |
| 160 | + if (topic.read ?? -1) < (seq ?? -1) { |
| 161 | + topic.read = seq |
| 162 | + self.store?.setRead(topic: topic, read: topic.read!) |
| 163 | + assignRecv(to: topic, recv: topic.read) |
| 164 | + } |
| 165 | + } |
| 166 | + |
| 167 | + override internal func routeMetaSub(meta: MsgServerMeta) { |
| 168 | + if let metaSubs = meta.sub as? Array<Subscription<DP, PrivateType>> { |
| 169 | + for sub in metaSubs { |
| 170 | + if let topic = tinode!.getTopic(topicName: sub.topic!) { |
| 171 | + if sub.deleted != nil { |
| 172 | + topic.persist(false) |
| 173 | + tinode!.stopTrackingTopic(topicName: sub.topic!) |
| 174 | + } else { |
| 175 | + if let t = topic as? DefaultTopic { |
| 176 | + t.update(sub: sub as! Subscription<VCard, PrivateType>) |
| 177 | + } else if let t = topic as? DefaultMeTopic { |
| 178 | + t.update(sub: sub as! Subscription<VCard, PrivateType>) |
| 179 | + } /*else if let t = topic as? DefaultFndTopic { |
| 180 | + t.update(sub: sub) |
| 181 | + } */ |
| 182 | + else { |
| 183 | + Tinode.log.fault("ME.routeMetaSub - failed to update topic %@", String(describing: topic)) |
| 184 | + assert(false) |
| 185 | + } |
| 186 | + } |
| 187 | + } else if sub.deleted == nil { |
| 188 | + let topic = tinode!.newTopic(sub: sub) |
| 189 | + topic.persist(true) |
| 190 | + } |
| 191 | + listener?.onMetaSub(sub: sub) |
| 192 | + } |
| 193 | + } |
| 194 | + listener?.onSubsUpdated() |
| 195 | + } |
| 196 | +} |
0 commit comments