@@ -0,0 +1,2270 @@
1+//
2+// File.swift
3+// golos
4+//
5+// Created by Ilya Kos on 12/12/16.
6+// Copyright © 2016 Ilya Kos. All rights reserved.
7+//
8+
9+import UIKit
10+import RealmSwift
11+import Alamofire
12+import CoreLocation
13+import Nuke
14+import SwiftyJSON
15+//import SwiftyVK
16+
17+
18+typealias Token = String
19+typealias UserID = Int
20+typealias MessageID = Int
21+typealias MultichatID = Int
22+
23+class RealmPhoto: Object {
24+
25+ convenience init(surl: String, id: String) {
26+ self.init()
27+ self.imageSURL = surl
28+ self.id = id
29+ }
30+
31+ @objc dynamic var id: String = ""
32+ var imageURL: URL? {
33+ get {
34+ return imageSURL =>> {URL(string: $0)}
35+ }
36+ set {
37+ imageSURL = newValue?.absoluteString
38+ }
39+ }
40+ @objc dynamic var imageSURL: String? {
41+ didSet {
42+ if oldValue != imageSURL {
43+ localImageSURL = nil
44+ }
45+ }
46+ }
47+ @objc private dynamic var localImageSURL: String? {
48+ didSet {
49+ if oldValue != localImageSURL {
50+ imageCache = nil
51+ }
52+ }
53+ }
54+ private var imageCache: UIImage?
55+
56+ convenience init(id: String, surl: String?) {
57+ self.init()
58+ self.id = id
59+ self.imageSURL = surl
60+ }
61+
62+ class func by(_ id: String, surl: String?) -> RealmPhoto {
63+ do {
64+ let realm = try Realm()
65+ if let ob = realm.object(ofType: RealmPhoto.self, forPrimaryKey: id) {
66+ if realm.isInWriteTransaction {
67+ surl =>> {ob.imageSURL = $0}
68+ } else {
69+ try? realm.write {
70+ surl =>> {ob.imageSURL = $0}
71+ }
72+ }
73+ return ob
74+ } else {
75+ let ob = RealmPhoto(id: id, surl: surl)
76+ if realm.isInWriteTransaction {
77+ realm.add(ob)
78+ } else {
79+ try? realm.write {
80+ realm.add(ob)
81+ }
82+ }
83+ return ob
84+ }
85+ } catch {
86+ let ob = RealmPhoto(id: id, surl: surl)
87+ return ob
88+ }
89+ }
90+
91+ override static func primaryKey() -> String? {
92+ return "id"
93+ }
94+
95+ override static func ignoredProperties() -> [String] {
96+ return ["imageURL", "imageCache", "completions", "request"]
97+ }
98+
99+ var completions: [((() -> ()), RealmPhotoToken)] = []
100+ var views: [(UIImageView?, RealmPhotoToken)] = []
101+ var request: DataRequest?
102+
103+ @discardableResult func load(into view: UIImageView, completion: (() -> ())? = nil, error errorClosure: (() -> ())? = nil) -> RealmPhotoToken {
104+
105+ let token = RealmPhotoToken()
106+
107+ func getFromServer() {
108+ if request == nil {
109+ let surl = "\(id).png"
110+ if let url = imageURL {
111+ print("getting from network")
112+ request = Alamofire.request(url, method: .get).validate().responseData(queue: userQueue) { (responce) in
113+ self.request = nil
114+ switch responce.result {
115+ case .success(let data):
116+ UIImage(data: data) >>> {image in
117+ write(cache: UIImagePNGRepresentation(image), withName: surl) {
118+ realmWrite({ (realm) in
119+ realm.object(ofType: RealmPhoto.self, forPrimaryKey: self.id)?.localImageSURL = surl
120+ })
121+ }
122+ self.imageCache = image
123+ DispatchQueue.main.async {
124+ if token.isValid {
125+ self.views.map { if $0.1.isValid {$0.0?.image = image} }
126+ self.completions.map { if $0.1.isValid { $0.0() } }
127+ self.completions = []
128+ }
129+ }
130+ }
131+ case .failure(let error):
132+ errorClosure?()
133+ // TODO: Error handling
134+ break
135+ }
136+ }
137+ }
138+ }
139+ }
140+
141+ if let surl = localImageSURL, // TODO: Should probrably get thing from server ???
142+ let url = url(forName: surl) {
143+ Nuke.loadImage(with: url, into: view) { [weak view] (response, fromMemory) in
144+ switch response {
145+ case .fulfilled(let image):
146+ if token.isValid {
147+ view?.image = image
148+ completion?()
149+ }
150+ case .rejected(let error): // TODO: Handle error?
151+ completion >>> {self.completions.append(($0, token))}
152+ self.views.append((view, token))
153+ getFromServer()
154+ }
155+ }
156+ } else {
157+ completion >>> {self.completions.append(($0, token))}
158+ self.views.append((view, token))
159+ getFromServer()
160+ }
161+
162+ return token
163+ }
164+
165+ // func image(_ completion: @escaping ((UIImage?) -> ())) {
166+ // print(id)
167+ // let surl = "\(id).png"
168+ // if imageCache != nil {
169+ // print("we have caching!")
170+ // }
171+ // imageCache = imageCache ?? (localImageSURL =>> getLocalImage) // TODO: imageCache always seems to be nil... ie always loading from disk for some reason
172+ // if let sImage = imageCache {
173+ // DispatchQueue.main.async { completion(sImage) }
174+ // } else if let url = imageURL {
175+ // print("getting from network")
176+ // Alamofire.request(url, method: .get).validate().responseData(queue: userQueue) { (responce) in
177+ // switch responce.result {
178+ // case .success(let data):
179+ // UIImage(data: data) >>> {image in
180+ // write(cache: UIImagePNGRepresentation(image), withName: surl) {
181+ // DispatchQueue.main.async {
182+ // do {
183+ // let realm = try Realm()
184+ // if !realm.isInWriteTransaction {
185+ // try realm.write {
186+ // self.localImageSURL = surl
187+ // }
188+ // } else {
189+ // self.localImageSURL = surl
190+ // }
191+ // } catch { }
192+ // }
193+ // }
194+ // self.imageCache = image
195+ // DispatchQueue.main.async {completion(image)}
196+ // }
197+ // case .failure(let error):
198+ // // TODO: Error handling
199+ // break
200+ // }
201+ // }.resume()
202+ // }
203+ // }
204+}
205+
206+public class RealmPhotoToken {
207+ public func cancel() {
208+ isValid = false
209+ }
210+ fileprivate var isValid = true
211+}
212+
213+
214+class User: Object {
215+ @objc dynamic var id: UserID = 0
216+ @objc dynamic var firstName: String?
217+ @objc dynamic var lastName: String?
218+ var deactivated: Deactivated? {
219+ get {
220+ return deactivatedStr =>> {Deactivated.init(rawValue: $0)}
221+ }
222+ set {
223+ deactivatedStr = newValue >>> {$0.rawValue}
224+ }
225+ }
226+ @objc private dynamic var deactivatedStr: String?
227+
228+ @objc dynamic var about: String?
229+ @objc dynamic var birthday: Date?
230+ let blacklisted = RealmOptional<Bool>()
231+ let canSendFriendRequest = RealmOptional<Bool>()
232+ let canSendPrivateMessage = RealmOptional<Bool>()
233+ let mutualFriendCount = RealmOptional<Int>()
234+ // TODO: var connections:
235+ @objc dynamic var mobilePhone: String?
236+ @objc dynamic var homePhone: String?
237+ // TODO: var exports
238+ var friendStatus: FriendStatus? {
239+ get {
240+ return friendStatusInt.value =>> {FriendStatus(rawValue: $0)}
241+ }
242+ set {
243+ friendStatusInt.value = newValue >>> {$0.rawValue}
244+ }
245+ }
246+ private let friendStatusInt = RealmOptional<Int>()
247+ let online = RealmOptional<Bool>()
248+ @objc dynamic var avatar: RealmPhoto?
249+ var avatarID: String {
250+ return "user\(id)avatar"
251+ }
252+ @objc dynamic var photo: RealmPhoto?
253+ var photoID: String {
254+ return "user\(id)photo"
255+ }
256+ var site: URL? {
257+ get {
258+ return siteSURL =>> {URL.init(string: $0)}
259+ }
260+ set {
261+ siteSURL = newValue >>> {$0.absoluteString}
262+ }
263+ }
264+ @objc dynamic var siteSURL: String?
265+ @objc dynamic var status: String?
266+ @objc dynamic var lastSeen: Date?
267+ // dynamic var lastUpdated: Date = Date()
268+ let friendCount = RealmOptional<Int>()
269+
270+ convenience init(id: UserID, firstName: String, lastName: String) {
271+ self.init()
272+ self.id = id
273+ self.firstName = firstName
274+ self.lastName = lastName
275+ // self.lastUpdated = Date()
276+ }
277+ class func by(id: UserID) -> User {
278+ let realm = try! Realm()
279+ if let us = realm.object(ofType: User.self, forPrimaryKey: id) {
280+ return us
281+ } else {
282+ let us = User()
283+ us.id = id
284+ if realm.isInWriteTransaction {
285+ realm.add(us)
286+ } else {
287+ try? realm.write {
288+ realm.add(us)
289+
290+ }
291+ }
292+ return us
293+ }
294+ }
295+
296+// func get(completion: ((User?) -> ())? = nil) {
297+// VK.API.Users.get([VK.Arg.userIDs: "\(id)"]).send(onSuccess: { (response) in
298+// if let json = response.array?.first {
299+// let us = VKData.processUser(json: json)
300+// completion?(us)
301+// } else {
302+// completion?(nil)
303+// }
304+// }, onError: { (error) in
305+// completion?(nil)
306+// })
307+// }
308+ func get(completion: ((User?) -> ())? = nil) {
309+ VKServer.get(user: self) { (data) in
310+ if let json = JSON.init(data: data)["response"].array?.first {
311+ let user = VKData.processUser(json: json)
312+ completion?(user)
313+ }
314+ }
315+ }
316+
317+
318+ override static func primaryKey() -> String? {
319+ return "id"
320+ }
321+
322+ override static func ignoredProperties() -> [String] {
323+ return ["site", "photoID", "avatarID"]
324+ }
325+ // func update() { lastUpdated = Date() }
326+
327+ // MARK: Hashable override
328+
329+ override var hashValue: Int {
330+ return id
331+ }
332+}
333+
334+class Geolocation: Object {
335+ var geoCoordinates: CLLocation? {
336+ get {
337+ if let long = long.value, let latt = latt.value {
338+ return CLLocation(latitude: latt, longitude: long)
339+ }
340+ return nil
341+ }
342+ set {
343+ if let latt = newValue?.coordinate.latitude,
344+ let long = newValue?.coordinate.longitude {
345+ self.latt.value = latt
346+ self.long.value = long
347+ } else {
348+ self.long.value = nil
349+ self.latt.value = nil
350+ }
351+ }
352+ }
353+ private let latt = RealmOptional<Double>()
354+ private let long = RealmOptional<Double>()
355+ let id = RealmOptional<Int>()
356+ @objc dynamic var title: String?
357+ @objc dynamic var image: RealmPhoto?
358+ var imageID: String {
359+ return "geo_lat\(latt.value ?? 0)_long\(long.value ?? 0)image"
360+ }
361+ var city: String?
362+ var country: String?
363+
364+ override static func ignoredProperties() -> [String] {
365+ return ["geoCoordinates", "imageID"]
366+ }
367+ convenience init(long: Double, latt: Double) {
368+ self.init()
369+ self.long.value = long
370+ self.latt.value = latt
371+ }
372+
373+}
374+
375+class GenericMessage: Object {
376+ @objc dynamic var date = Date() //
377+ @objc dynamic var body = "" //
378+ @objc dynamic var user: User?
379+
380+ var outPhotos: [UIImage] = [] // TODO: ~
381+
382+ @objc dynamic var geolocation: Geolocation?
383+
384+ let attachments = List<RealmMedia>()
385+ let forwardedMessages = List<RealmForwardedMessage>()
386+ //let sentMessageId: MessageID? I DONT KNOW WHY I PUT THIS HERE
387+
388+ // convenience init(body: String) {
389+ // self.init()
390+ // self.body = body
391+ // self.out.value = true
392+ // }
393+ // convenience init(image: UIImage) {
394+ // self.init()
395+ // self.outPhotos.append(image)
396+ // }
397+ override static func ignoredProperties() -> [String] {
398+ return ["outPhotos"]
399+ }
400+}
401+
402+class Message: GenericMessage, Comparable, SortedArrayItem {
403+ @objc dynamic var id = 0
404+ let randomID = RealmOptional<MessageID>()
405+ @objc dynamic var read = false
406+ @objc dynamic var replied = false
407+ @objc dynamic var important = false
408+ @objc dynamic var deleted = false
409+
410+ func equal(to message: Message) -> Bool {
411+ // print("\(lhs.id.value) \(rhs.id.value) \(lhs.id.value == rhs.id.value)")
412+ if let a = self.randomID.value,
413+ let b = message.randomID.value {
414+ if a == b {
415+ return true
416+ }
417+ }
418+ return self.id == message.id
419+ }
420+ internal func greater(than message: Message) -> Bool {
421+ return self.id < message.id
422+ }
423+
424+ static func ==(lhs: Message, rhs: Message) -> Bool {
425+ print("\(lhs.id) \(rhs.id) \(lhs.id == rhs.id)")
426+ return lhs.id == rhs.id
427+ }
428+
429+ @objc dynamic var out = false
430+
431+ // Multichat stuff
432+
433+ @objc dynamic var photoSURL: String?
434+ let chatID = RealmOptional<MultichatID>()
435+ @objc private dynamic var serviceMessageString: String?
436+ private var serviceMessageUser: User?
437+ private let serviceMessage = RealmOptional<Int>()
438+
439+ var action: ServiceMessageAction? {
440+ get {
441+ func getUser() -> ServiceMessageUser? {
442+ if let user = self.serviceMessageUser {
443+ return .user(user)
444+ } else if let email = self.serviceMessageString {
445+ return .email(email)
446+ } else {
447+ return nil
448+ }
449+ }
450+
451+ return serviceMessage.value =>> { service in
452+ switch service {
453+ case 0:
454+ return .photoUpdate
455+ case 1:
456+ return .photoRemove
457+ case 2:
458+ return self.serviceMessageString >>> { .create($0) }
459+ case 3:
460+ return self.serviceMessageString >>> { .titleUpdate($0) }
461+ case 4:
462+ return getUser() >>> { .inviteUser($0) }
463+ case 5:
464+ return getUser() >>> { .kickUser($0) }
465+ default:
466+ return nil
467+ }
468+ }
469+ }
470+ set {
471+ func set(user: ServiceMessageUser) {
472+ switch user {
473+ case .user(let use):
474+ self.serviceMessageUser = use
475+ case .email(let str):
476+ self.serviceMessageString = str
477+ }
478+ }
479+
480+ serviceMessageString = nil
481+ serviceMessageUser = nil
482+ serviceMessage.value = newValue >>> { new in
483+ switch new {
484+ case .photoUpdate:
485+ return 0
486+ case .photoRemove:
487+ return 1
488+ case .create(let str):
489+ self.serviceMessageString = str
490+ return 2
491+ case .titleUpdate(let str):
492+ self.serviceMessageString = str
493+ return 3
494+ case .inviteUser(let user):
495+ set(user: user)
496+ return 4
497+ case .kickUser(let user):
498+ set(user: user)
499+ return 5
500+ }
501+ }
502+ }
503+ }
504+
505+// func get(completion: ((Message?) -> ())? = nil) {
506+// VK.API.Messages.getById([VK.Arg.messageIds: String(id)]).send(onSuccess: { (response) in
507+// if let json = response.array?.first {
508+// let mes = VKData.processMessage(response)
509+// completion?(mes)
510+// } else {
511+// completion?(nil)
512+// }
513+// }, onError: { (error) in
514+// completion?(nil)
515+// })
516+// // TODO: Implement propperly?
517+// }
518+
519+ class func by(id: MessageID) -> Message {
520+ let realm = try! Realm()
521+ if let us = realm.object(ofType: Message.self, forPrimaryKey: id) {
522+ return us
523+ } else {
524+ let us = Message()
525+ us.id = id
526+ realmWrite({ (_) in
527+ realm.add(us)
528+ })
529+ return us
530+ }
531+ }
532+
533+ override static func primaryKey() -> String? {
534+ return "id"
535+ }
536+}
537+//func ==(lhs: Message, rhs: Message) -> Bool {
538+// print("\(lhs.id.value) \(rhs.id.value) \(lhs.id.value == rhs.id.value)")
539+// return lhs.id.value == rhs.id.value
540+//}
541+func <(lhs: Message, rhs: Message) -> Bool {
542+ return lhs.id < rhs.id
543+}
544+
545+enum forwardedMessage {
546+ case genericMessage(GenericMessage)
547+ case message(Message)
548+}
549+
550+class RealmForwardedMessage: Object {
551+ var value: forwardedMessage? {
552+ get {
553+ if let gm = genericMessage {
554+ return .genericMessage(gm)
555+ } else if let m = message {
556+ return .message(m)
557+ } else {
558+ return nil
559+ }
560+ }
561+ set {
562+ realmWrite { _ in
563+ if let fm = newValue {
564+ switch fm {
565+ case .genericMessage(let gm):
566+ genericMessage = gm
567+ message = nil
568+ case .message(let m):
569+ genericMessage = nil
570+ message = m
571+ }
572+ } else {
573+ message = nil
574+ genericMessage = nil
575+ }
576+ }
577+ }
578+ }
579+ @objc private dynamic var genericMessage: GenericMessage?
580+ @objc private dynamic var message: Message?
581+
582+ var id: MessageID? {
583+ if let m = message {
584+ return m.id
585+ } else {
586+ return nil
587+ }
588+ }
589+
590+ convenience init(value: forwardedMessage) {
591+ self.init()
592+ self.value = value
593+ }
594+
595+ override static func ignoredProperties() -> [String] {
596+ return ["value"]
597+ }
598+}
599+
600+//struct Geolocation {
601+// var coordinates: CLLocation
602+// var title: String?
603+// var imageURL: URL? {
604+// get {
605+// return imageSURL =>> {URL.init(string: $0)}
606+// }
607+// set {
608+// imageSURL = newValue?.absoluteString
609+// }
610+// }
611+// var imageSURL: String?
612+// // func image(completion: ((UIImage) -> ())) {
613+// // imageFunc =>> {$0(completion)}
614+// // }
615+// var image: (@escaping ((UIImage) -> ()) -> ())?
616+// var city: String?
617+// var country: String?
618+// init(coordinates: CLLocation, title: String?, imageSURL: String?, image: (@escaping ((UIImage) -> ()) -> ()), city: String?, country: String?) {
619+// self.coordinates = coordinates
620+// self.title = title
621+// self.imageSURL = imageSURL
622+// self.image = image
623+// self.city = city
624+// self.country = country
625+// }
626+//}
627+typealias GeolocationID = Int
628+
629+enum Deactivated: String {
630+ case banned = "banned"
631+ case deleted = "deleted"
632+ case normal = "normal"
633+}
634+
635+enum FriendStatus: Int {
636+ case notFriends = 0
637+ case requestSent
638+ case requestRecieved
639+ case fiend
640+}
641+
642+enum MessageReadState: Int {
643+ case read = 0
644+ case notRead
645+}
646+enum MessageSendState: Int {
647+ case received = 0
648+ case sent
649+}
650+
651+enum MultichatNotificationSetting {
652+ case sound
653+ case disabled_until
654+}
655+
656+enum ServiceMessageAction {
657+ case photoUpdate // 0
658+ case photoRemove // 1
659+ case create(String) // 2
660+ case titleUpdate(String) // 3
661+ case inviteUser(ServiceMessageUser) // 4
662+ case kickUser(ServiceMessageUser) // 5
663+}
664+
665+enum ServiceMessageUser {
666+ case user(User)
667+ case email(String)
668+}
669+
670+typealias MessageChunkID = String
671+class MessageChunk: Object { // message ids go down
672+ subscript(index: Int) -> Message {
673+ return messages[index]
674+ }
675+
676+ @objc dynamic var id: MessageChunkID = ""
677+
678+ fileprivate let messages = List<Message>()
679+
680+ var firstMessage: Message? {
681+ return messages.first
682+ }
683+
684+ var lastMessage: Message? {
685+ return messages.last
686+ }
687+
688+ var firstID: MessageID? {
689+ return firstMessage?.id
690+ }
691+
692+ var lastID: MessageID? {
693+ return lastMessage?.id
694+ }
695+
696+ var count: Int {
697+ return messages.count
698+ }
699+
700+ fileprivate func append(message: Message?) {
701+ message >>> {messages.append($0)}
702+ }
703+
704+ override static func primaryKey() -> String? {
705+ return "id"
706+ }
707+
708+ override static func ignoredProperties() -> [String] {
709+ return ["firstMessage", "lastMessage", "firstID", "lastID", "count"]
710+ }
711+}
712+
713+func +=(lhs: MessageChunk, rhs: MessageChunk) {
714+ lhs.messages.append(objectsIn: rhs.messages)
715+}
716+
717+/// Returns: true if added. false if was already in list
718+func <~(lhs: MessageChunk?, rhs: Message?) -> Bool {
719+ var out: Bool = false
720+ realmWrite { (_) in
721+ out = lhs?.messages <~ rhs
722+ }
723+ return out
724+}
725+/// Returns: Indexes of added items
726+func <-(lhs: MessageChunk?, rhs: [Message?]?) -> [Int] {
727+ var out: [Int] = []
728+ realmWrite { (_) in
729+ out = lhs?.messages <- rhs
730+ }
731+ return out
732+}
733+/// Returns: Indexes of added items
734+func <-(lhs: MessageChunk?, rhs: MessageChunk?) -> [Int] {
735+ var out: [Int] = []
736+ realmWrite { (_) in
737+ out = lhs?.messages <- rhs?.messages
738+ }
739+ return out
740+}
741+
742+enum MessageEntry {
743+ case message(Message)
744+ case spacer
745+}
746+
747+typealias MessagesID = Int
748+class Messages: Object {
749+ subscript(ind: Int) -> MessageEntry? {
750+ assert(ind >= 0, "index out of bounds")
751+ if ind >= count || ind < 0 {
752+ return nil
753+ }
754+ var index = ind
755+ var i = 0
756+ while messageChunks[i].count < index {
757+ index -= messageChunks[i].count + 1
758+ i += 1
759+ }
760+ if index == messageChunks[i].count {
761+ return .spacer
762+ } else {
763+ return .message(messageChunks[i][index])
764+ }
765+ }
766+
767+ @objc dynamic var id: MessagesID = 0
768+
769+ @objc fileprivate dynamic var lastChunkID = 0
770+
771+ var endIndex: Int {
772+ return messageChunks.endIndex
773+ }
774+
775+ var first: Message? {
776+ return messageChunks.first?.firstMessage
777+ }
778+
779+ func count(upToChunk index: Int) -> Int {
780+ var n = 0
781+ for i in 0..<index {
782+ n += messageChunks[i].count + 1
783+ }
784+ return n
785+ }
786+
787+ public var count: Int {
788+ var n = messageChunks.count - 1
789+ for chunk in messageChunks {
790+ n += chunk.count
791+ }
792+ return n
793+ }
794+
795+ fileprivate let messageChunks = List<MessageChunk>()
796+
797+ public var chunks: List<MessageChunk> {
798+ return messageChunks
799+ }
800+
801+ fileprivate enum ChunkIndex {
802+ case inside(Int)
803+ case after(Int)
804+ case beforeFirst
805+ }
806+
807+ fileprivate func createChunk(at index: Int = 0) -> MessageChunk {
808+ let chunk = MessageChunk()
809+ realmWrite { (realm) in
810+ chunk.id = "\(id)_\(lastChunkID)"
811+ lastChunkID += 1
812+ realm.add(chunk)
813+ messageChunks.insert(chunk, at: index)
814+ }
815+ return chunk
816+ }
817+
818+ /// Return nil if there are no chunks
819+ fileprivate func chunkIndex(of message: Message) -> ChunkIndex? {
820+ if messageChunks.count == 0 {
821+ return nil
822+ }
823+ if let first = messageChunks.first?.firstMessage {
824+ if first.greater(than: message) {
825+ return .beforeFirst
826+ }
827+ }
828+ for (i, chunk) in messageChunks.enumerated() {
829+ if let lastMessage = chunk.lastMessage { // It should never be nil since a chunk is never empty
830+ if !message.greater(than: lastMessage) { // [ ... | ()] [...] ...
831+ if i == 0 {
832+ return .inside(i)
833+ } else {
834+ if let firstMessage = chunk.firstMessage { // It should never be nil since a chunk is never empty
835+ if message.greater(than: firstMessage) || message.equal(to: firstMessage) {
836+ return .inside(i)
837+ } else {
838+ return .after(i-1)
839+ }
840+ }
841+ }
842+ }
843+ }
844+ }
845+ return .after(endIndex-1)
846+ }
847+
848+
849+ override static func primaryKey() -> String? {
850+ return "id"
851+ }
852+
853+ override static func ignoredProperties() -> [String] {
854+ return ["endIndex"]
855+ }
856+
857+ class func by(id: MessagesID) -> Messages {
858+ let realm = try! Realm()
859+ if let us = realm.object(ofType: Messages.self, forPrimaryKey: id) {
860+ return us
861+ } else {
862+ let us = Messages()
863+ us.id = id
864+ realm.add(us)
865+ return us
866+ }
867+ }
868+
869+ // MARK: Messages obesrver
870+
871+ static let notificationName: Notification.Name = Notification.Name(rawValue: "MessagesWereModifiedNotificationName")
872+
873+ enum Changes {
874+ case added
875+ case modified
876+ case deleted
877+ }
878+}
879+
880+
881+
882+/// Returns: true if added. false if was already in list
883+func <~(lhs: Messages?, rhs: Message?) -> Bool {
884+ var out: Bool = false
885+ if let lhs = lhs,
886+ let rhs = rhs {
887+ realmWrite { (_) in
888+ if let index = lhs.chunkIndex(of: rhs) {
889+ switch index {
890+ case .after(let i):
891+ lhs.messageChunks[i].append(message: rhs)
892+ out = true
893+ case .inside(let i):
894+ out = lhs.messageChunks[i] <~ rhs
895+ case .beforeFirst:
896+ out = lhs.createChunk(at: 0) <~ rhs
897+ }
898+ } else {
899+ out = lhs.createChunk() <~ rhs
900+ }
901+ }
902+ }
903+ return out
904+}
905+/// Only accepts a list of SEQUENTIAL messages. Will silently break if not passed sequentially.
906+/// Returns: Indexes of added items
907+func <-(lhs: Messages?, rhs: [Message?]?) /*-> [Int]*/ {
908+ print(rhs)
909+ print(lhs)
910+ if let lhs = lhs,
911+ let rhs = rhs?.flatMap({$0}),
912+ let first = rhs.first,
913+ let last = rhs.last {
914+
915+ var deleted: [Int] = []
916+ var modified: [Int] = []
917+ var added: [Int] = []
918+ if let startI = lhs.chunkIndex(of: first),
919+ let endI = lhs.chunkIndex(of: last) {
920+ var start: Int!
921+ var end: Int!
922+ print("\t", startI, endI)
923+ switch startI {
924+ case .inside(let i):
925+ start = i
926+ case .after(let i):
927+ start = i + 1
928+ case .beforeFirst:
929+ start = -1
930+ }
931+ switch endI {
932+ case .after(let i):
933+ end = i
934+ case .inside(let i):
935+ end = i
936+ case .beforeFirst:
937+ end = -1
938+ }
939+
940+ if start < end {
941+ print(start, end)
942+ start = max(start, 0)
943+ var spacerIndexes = (start+1...end).map({lhs.count(upToChunk: $0) - 1})
944+ // deleted += spacerIndexes
945+ let count = lhs.count(upToChunk: start)
946+ var dIndex = 0
947+ for i in 0..<spacerIndexes.count {
948+ spacerIndexes[i] -= dIndex
949+ deleted.append(spacerIndexes[i])
950+ spacerIndexes[i] -= count
951+ dIndex += 1
952+ }
953+ var addedMs: [Int]!
954+ realmWriteWithCompletion { realm, completion in
955+ for x in start+1...end {
956+ print(lhs.chunks.count, x)
957+ lhs.chunks[start] += lhs.chunks[start+1]
958+ realm.delete(lhs.chunks[start+1])
959+ // lhs.chunks.remove(objectAtIndex: start+1)
960+ }
961+ let chunk = lhs.chunks[start]
962+ addedMs = chunk <- rhs
963+
964+ addedMs.map {$0 + count}
965+ added += addedMs
966+
967+ completion {
968+ print("after notification", lhs.chunks)
969+ NotificationCenter.default.post(name: Messages.notificationName,
970+ object: lhs.id,
971+ userInfo: [Messages.Changes.added: added,
972+ Messages.Changes.modified: modified,
973+ Messages.Changes.deleted: deleted])
974+ }
975+ }
976+ } else { // within one
977+ assert(start == end, "start index is larger than end index!")
978+ realmWriteWithCompletion { _, completion in
979+ if start == -1 {
980+ added += lhs.createChunk(at: 0) <- rhs
981+ added.append(0) // IDK o[]o[]o []o[]o
982+ } else {
983+ added += (lhs.messageChunks[start] <- rhs).map {$0 + lhs.count(upToChunk: start)}
984+ }
985+ completion {
986+ NotificationCenter.default.post(name: Messages.notificationName,
987+ object: lhs.id,
988+ userInfo: [Messages.Changes.added: added,
989+ Messages.Changes.modified: modified,
990+ Messages.Changes.deleted: deleted])
991+ }
992+ }
993+ }
994+ } else { // empty
995+ realmWriteWithCompletion { _, completion in
996+ added += lhs.createChunk() <- rhs
997+ completion {
998+ NotificationCenter.default.post(name: Messages.notificationName,
999+ object: lhs.id,
1000+ userInfo: [Messages.Changes.added: added,
1001+ Messages.Changes.modified: modified,
1002+ Messages.Changes.deleted: deleted])
1003+ }
1004+ }
1005+ }
1006+ print(lhs)
1007+ print(added)
1008+ print(deleted)
1009+ }
1010+}
1011+
1012+enum Media {
1013+ case photo(Photo)
1014+ case video(Video)
1015+ case audio(Audio)
1016+ case doc(Document)
1017+ case wall(WallPost)
1018+ case wallReply(WallPostReply)
1019+ case sticker(Sticker)
1020+ case link(Link)
1021+ case gift(Gift)
1022+ case market(Market)
1023+ case marketAlbum(MarketAlbum)
1024+ case graffiti(Document)
1025+ case note(Note)
1026+ case poll(Poll)
1027+ case album(PhotoAlbum)
1028+ var realm: RealmMedia {
1029+ return RealmMedia(value: self)
1030+ }
1031+}
1032+
1033+class RealmMedia: Object {
1034+ @objc private dynamic var photo: Photo?
1035+ @objc private dynamic var video: Video?
1036+ @objc private dynamic var audio: Audio?
1037+ @objc private dynamic var doc: Document?
1038+ @objc private dynamic var wall: WallPost?
1039+ @objc private dynamic var wallReply: WallPostReply?
1040+ @objc private dynamic var sticker: Sticker?
1041+ @objc private dynamic var link: Link?
1042+ @objc private dynamic var gift: Gift?
1043+ @objc private dynamic var market: Market?
1044+ @objc private dynamic var marketAlbum: MarketAlbum?
1045+ @objc private dynamic var graffiti: Document?
1046+ @objc private dynamic var note: Note?
1047+ @objc private dynamic var poll: Poll?
1048+ @objc private dynamic var album: PhotoAlbum?
1049+
1050+ var value: Media? {
1051+ get {
1052+ if let sureThing = self.photo { return .photo(sureThing) }
1053+ if let sureThing = self.video { return .video(sureThing) }
1054+ if let sureThing = self.audio { return .audio(sureThing) }
1055+ if let sureThing = self.doc { return .doc(sureThing) }
1056+ if let sureThing = self.wall { return .wall(sureThing) }
1057+ if let sureThing = self.wallReply { return .wallReply(sureThing) }
1058+ if let sureThing = self.sticker { return .sticker(sureThing) }
1059+ if let sureThing = self.link { return .link(sureThing) }
1060+ if let sureThing = self.gift { return .gift(sureThing) }
1061+ if let sureThing = self.market { return .market(sureThing) }
1062+ if let sureThing = self.marketAlbum { return .marketAlbum(sureThing) }
1063+ if let sureThing = self.graffiti { return .graffiti(sureThing) }
1064+ if let sureThing = self.note { return .note(sureThing) }
1065+ if let sureThing = self.poll { return .poll(sureThing) }
1066+ if let sureThing = self.album { return .album(sureThing) }
1067+ return nil
1068+ }
1069+ set {
1070+
1071+ photo = nil
1072+ video = nil
1073+ audio = nil
1074+ doc = nil
1075+ wall = nil
1076+ wallReply = nil
1077+ sticker = nil
1078+ link = nil
1079+ gift = nil
1080+ market = nil
1081+ marketAlbum = nil
1082+ graffiti = nil
1083+ note = nil
1084+ poll = nil
1085+ album = nil
1086+
1087+ if let val = newValue {
1088+ switch val {
1089+ case .photo(let foo): photo = foo
1090+ case .video(let foo): video = foo
1091+ case .audio(let foo): audio = foo
1092+ case .doc(let foo): doc = foo
1093+ case .wall(let foo): wall = foo
1094+ case .wallReply(let foo): wallReply = foo
1095+ case .sticker(let foo): sticker = foo
1096+ case .link(let foo): link = foo
1097+ case .gift(let foo): gift = foo
1098+ case .market(let foo): market = foo
1099+ case .marketAlbum(let foo): marketAlbum = foo
1100+ case .graffiti(let foo): graffiti = foo
1101+ case .note(let foo): note = foo
1102+ case .poll(let foo): poll = foo
1103+ case .album(let foo): album = foo
1104+ }
1105+ }
1106+ }
1107+ }
1108+ convenience init(value: Media) {
1109+ self.init()
1110+ self.value = value
1111+ }
1112+}
1113+
1114+class Poll: Object {
1115+ @objc dynamic var id: PollID = 0
1116+ @objc dynamic var owner: User?
1117+ @objc dynamic var date: Date?
1118+ @objc dynamic var question: String?
1119+ let votes = RealmOptional<Int>()
1120+ let answerID = RealmOptional<PollAnswerID>()
1121+ let answers = List<PollAnswer>()
1122+ convenience init(id: PollID, owner: User, date: Date, question: String, votes: Int) {
1123+ self.init()
1124+ self.id = id
1125+ self.owner = owner
1126+ self.date = date
1127+ self.question = question
1128+ self.votes.value = votes
1129+ // self.answerID.value = answerID
1130+ }
1131+ class func by(id: PollID) -> Poll {
1132+ let realm = try! Realm()
1133+ if let us = realm.object(ofType: Poll.self, forPrimaryKey: id) {
1134+ return us
1135+ } else {
1136+ let us = Poll()
1137+ us.id = id
1138+ realm.add(us)
1139+ return us
1140+ }
1141+ }
1142+
1143+ override class func primaryKey() -> String? {
1144+ return "id"
1145+ }
1146+}
1147+typealias PollID = Int
1148+
1149+class PollAnswer: Object {
1150+ @objc dynamic var id: PollAnswerID = 0
1151+ @objc dynamic var body: String = ""
1152+ @objc dynamic var votes: Int = 0
1153+ @objc dynamic var rating: Int = 0
1154+
1155+ convenience init(id: PollAnswerID, body: String, votes: Int, rating: Int) {
1156+ self.init()
1157+ self.id = id
1158+ self.body = body
1159+ self.votes = votes
1160+ self.rating = rating
1161+ }
1162+}
1163+typealias PollAnswerID = Int
1164+
1165+class Note: Object {
1166+ @objc dynamic var id: NoteID = 0
1167+ @objc dynamic var owner: User?
1168+ @objc dynamic var title: String?
1169+ @objc dynamic var body: String?
1170+ @objc dynamic var date: Date?
1171+ var url: URL? {
1172+ get {
1173+ return surl =>> {URL(string: $0)}
1174+ }
1175+ set {
1176+ surl = newValue >>> {$0.absoluteString}
1177+ }
1178+ }
1179+ @objc dynamic var surl: String?
1180+ let commentCount = RealmOptional<Int>()
1181+ convenience init(id: NoteID, owner: User, title: String, body: String, date: Date, url: URL, commentCount: Int) {
1182+ self.init()
1183+ self.id = id
1184+ self.owner = owner
1185+ self.title = title
1186+ self.body = body
1187+ self.date = date
1188+ self.url = url
1189+ self.commentCount.value = commentCount
1190+ }
1191+ class func by(id: NoteID) -> Note {
1192+ let realm = try! Realm()
1193+ if let us = realm.object(ofType: Note.self, forPrimaryKey: id) {
1194+ return us
1195+ } else {
1196+ let us = Note()
1197+ us.id = id
1198+ realm.add(us)
1199+ return us
1200+ }
1201+ }
1202+ override class func ignoredProperties() -> [String] {
1203+ return ["url"]
1204+ }
1205+
1206+ override class func primaryKey() -> String? {
1207+ return "id"
1208+ }
1209+}
1210+typealias NoteID = Int
1211+
1212+typealias GraffitiID = Int
1213+class Graffiti: Document {
1214+} // Use document.image ?
1215+
1216+class Photo: Object {
1217+ @objc dynamic var id: PhotoID = 0
1218+ @objc dynamic var owner: User?
1219+ @objc dynamic var date: Date?
1220+ @objc dynamic var thumbnail: RealmPhoto?
1221+ var thumbnailID: String {
1222+ return "photo\(id)thumbnail"
1223+ }
1224+ @objc dynamic var image: RealmPhoto?
1225+ var imageID: String {
1226+ return "photo\(id)image"
1227+ }
1228+ let width = RealmOptional<Int>()
1229+ let height = RealmOptional<Int>()
1230+ @objc dynamic var accessKey: Key?
1231+
1232+ convenience init(id: PhotoID, owner: User, date: Date){
1233+ self.init()
1234+ self.id = id
1235+ self.owner = owner
1236+ self.date = date
1237+ }
1238+
1239+ class func by(id: PhotoID) -> Photo {
1240+ let realm = try! Realm()
1241+ if let us = realm.object(ofType: Photo.self, forPrimaryKey: id) {
1242+ return us
1243+ } else {
1244+ let us = Photo()
1245+ us.id = id
1246+ realm.add(us)
1247+ return us
1248+ }
1249+ }
1250+
1251+// func get(completion: ((Photo?) -> ())? = nil) {
1252+// if let ownerID = owner?.id {
1253+// var ph = "\(ownerID)_\(id)"
1254+// if let key = accessKey {
1255+// ph.append("_\(key)")
1256+// }
1257+// VK.API.Photos.getById([VK.Arg.photos: ph]).send(onSuccess: { (response) in
1258+// if let json = response.array?.first {
1259+// let photo = VKData.processPhoto(json)
1260+// completion?(photo)
1261+// } else {
1262+// completion?(nil)
1263+// }
1264+// }, onError: { (error) in
1265+// completion?(nil)
1266+// })
1267+// } else {
1268+// completion?(nil)
1269+// }
1270+// }
1271+ func get(completion: ((Photo?) -> ())? = nil) {
1272+ VKServer.get(photo: self) { data in
1273+ if let json = JSON.init(data: data)["response"].array?.first {
1274+ let photo = VKData.processPhoto(json)
1275+ completion?(photo)
1276+ }
1277+ }
1278+ }
1279+
1280+ override class func primaryKey() -> String? {
1281+ return "id"
1282+ }
1283+
1284+ override static func ignoredProperties() -> [String] {
1285+ return ["thumbnailID", "imageID"]
1286+ }
1287+}
1288+
1289+class PhotoAlbum: Object {
1290+ @objc dynamic var id: PhotoAlbumID = 0
1291+ @objc dynamic var thumb: Photo?
1292+ @objc dynamic var owner: User?
1293+ @objc dynamic var title: String?
1294+ @objc dynamic var albumDescription: String?
1295+ @objc dynamic var dateCreated: Date?
1296+ @objc dynamic var dateUpdated: Date?
1297+ let count = RealmOptional<Int>()
1298+ let photos = List<Photo>()
1299+ convenience init(id: PhotoAlbumID) {
1300+ self.init()
1301+ self.id = id
1302+ }
1303+
1304+ override class func primaryKey() -> String? {
1305+ return "id"
1306+ }
1307+ class func by(id: PhotoAlbumID) -> PhotoAlbum {
1308+ let realm = try! Realm()
1309+ if let us = realm.object(ofType: PhotoAlbum.self, forPrimaryKey: id) {
1310+ return us
1311+ } else {
1312+ let us = PhotoAlbum()
1313+ us.id = id
1314+ realm.add(us)
1315+ return us
1316+ }
1317+ }
1318+
1319+
1320+ // init(id: PhotoAlbumID, thumbID: PhotoID, ownerID: UserID, title: String, dateCreated: Date, dateUpdated: Date, count: Int) {
1321+ // self.id = id
1322+ // self.thumbID = thumbID
1323+ // self.ownerID = ownerID
1324+ // self.title = title
1325+ // self.dateCreated = dateCreated
1326+ // self.dateUpdated = dateUpdated
1327+ // self.count = count
1328+ // }
1329+
1330+}
1331+typealias PhotoID = Int
1332+typealias PhotoAlbumID = Int
1333+typealias Key = String
1334+
1335+class WallPost: Object {
1336+ @objc dynamic var id: Int = 0
1337+ @objc dynamic var owner: User?
1338+ @objc dynamic var creator: User?
1339+ @objc dynamic var date: Date?
1340+ @objc dynamic var body: String?
1341+ let commentCount = RealmOptional<Int>()
1342+ let canPostComment = RealmOptional<Bool>()
1343+ let likeCount = RealmOptional<Int>()
1344+ @objc dynamic var liked: Bool = false
1345+ let canLike = RealmOptional<Bool>()
1346+ let repostCount = RealmOptional<Int>()
1347+ let canDelete = RealmOptional<Bool>()
1348+ let didRepost = RealmOptional<Bool>()
1349+ // Optional ⬇
1350+ let attachments = List<RealmMedia>()
1351+ @objc dynamic var geo: Geolocation?
1352+ // TODO: let postSource
1353+ @objc dynamic var signed: User?
1354+ @objc dynamic var ownerOfCopy: User?
1355+ @objc dynamic var original: WallPost?
1356+ let historyOfCopy = List<WallPost>()
1357+ let canPin = RealmOptional<Bool>()
1358+ let canEdit = RealmOptional<Bool>()
1359+ let isPinned = RealmOptional<Bool>()
1360+
1361+ // convenience init(id: WallPostID, ownerID: UserID, creatorID: UserID, date: Date, body: String, commentCount: Int, canPostComment: Bool, likeCount: Int, liked: Bool, canLike: Bool, repostCount: Int, didRepost: Bool, attachments: [Media], geo: Geolocation?, signed: UserID?, copyID: WallPostID?, canDelete: Bool, canPin: Bool?, canEdit: Bool?, isPinned: Bool?, copyHistory: [WallPostID], copyOwnerID: UserID?) {
1362+ // self.id = id
1363+ // self.ownerID = ownerID
1364+ // self.creatorID = creatorID
1365+ // self.date = date
1366+ // self.body = body
1367+ // self.commentCount = commentCount
1368+ // self.canPostComment = canPostComment
1369+ // self.likeCount = likeCount
1370+ // self.liked = liked
1371+ // self.canLike = canLike
1372+ // self.repostCount = repostCount
1373+ // self.didRepost = didRepost
1374+ // self.attachments = attachments
1375+ // self.geo = geo
1376+ // self.signed = signed
1377+ // self.copyID = copyID
1378+ // self.canDelete = canDelete
1379+ // self.canPin = canPin
1380+ // self.canEdit = canEdit
1381+ // self.isPinned = isPinned
1382+ // self.copyHistory = copyHistory
1383+ // self.copyOwnerID = copyOwnerID
1384+ // }
1385+
1386+// func get(completion: ((WallPost?) -> ())? = nil) {
1387+// if let ownerID = owner?.id {
1388+// var ph = "\(ownerID)_\(id)"
1389+// // if let key = accessKey {
1390+// // ph.append("_\(key)")
1391+// // }
1392+// VK.API.Wall.getById([VK.Arg.posts: ph]).send(onSuccess: { (response) in
1393+// if let json = response.array?.first {
1394+// let photo = VKData.processWallPost(json)
1395+// completion?(photo)
1396+// } else {
1397+// completion?(nil)
1398+// }
1399+// }, onError: { (error) in
1400+// completion?(nil)
1401+// })
1402+// } else {
1403+// completion?(nil)
1404+// }
1405+// }
1406+ func get(completion: ((WallPost?) -> ())? = nil) {
1407+ VKServer.get(wallPost: self) { (data) in
1408+ if let json = JSON.init(data: data)["response"].array?.first {
1409+ let post = VKData.processWallPost(json)
1410+ completion?(post)
1411+ }
1412+ }
1413+ }
1414+
1415+
1416+
1417+ override class func primaryKey() -> String? {
1418+ return "id"
1419+ }
1420+
1421+ class func by(id: WallPostID) -> WallPost {
1422+ let realm = try! Realm()
1423+ if let us = realm.object(ofType: WallPost.self, forPrimaryKey: id) {
1424+ return us
1425+ } else {
1426+ let us = WallPost()
1427+ us.id = id
1428+ realm.add(us)
1429+ return us
1430+ }
1431+ }
1432+
1433+}
1434+typealias WallPostID = Int
1435+
1436+class WallPostReply: Object {
1437+ @objc dynamic var id: WallPostReplyID = 0
1438+ @objc dynamic var author: User?
1439+ @objc dynamic var wallPost: WallPost?
1440+ @objc dynamic var wallOwner: User?
1441+ @objc dynamic var date: Date?
1442+ @objc dynamic var body: String?
1443+ let likeCount = RealmOptional<Int>()
1444+ let didLike = RealmOptional<Bool>()
1445+ let canLike = RealmOptional<Bool>()
1446+ @objc dynamic var inReplyToUser: User?
1447+ let attachments = List<RealmMedia>()
1448+ @objc dynamic var inReplyToComment: WallPostReply?
1449+ class func by(id: WallPostReplyID) -> WallPostReply {
1450+ let realm = try! Realm()
1451+ if let us = realm.object(ofType: WallPostReply.self, forPrimaryKey: id) {
1452+ return us
1453+ } else {
1454+ let us = WallPostReply()
1455+ us.id = id
1456+ realm.add(us)
1457+ return us
1458+ }
1459+ }
1460+
1461+ func get(completion: (() -> ())) {
1462+ // TODO: Get wallPostReply
1463+ }
1464+
1465+ override class func primaryKey() -> String? {
1466+ return "id"
1467+ }
1468+}
1469+typealias WallPostReplyID = Int
1470+
1471+class Sticker: Object {
1472+ @objc dynamic var id: StickerID = 0
1473+ @objc dynamic var packID: StickerPackID = 0
1474+ @objc dynamic var image: RealmPhoto?
1475+ var imageID: String {
1476+ return "sticker\(id)image"
1477+ }
1478+ let width = RealmOptional<Int>()
1479+ let height = RealmOptional<Int>()
1480+
1481+ convenience init(id: StickerID, packID: StickerPackID, width: Int, height: Int) {
1482+ self.init()
1483+ self.id = id
1484+ self.packID = packID
1485+ self.width.value = width
1486+ self.height.value = height
1487+ }
1488+ class func by(id: StickerID) -> Sticker {
1489+ let realm = try! Realm()
1490+ if let us = realm.object(ofType: Sticker.self, forPrimaryKey: id) {
1491+ return us
1492+ } else {
1493+ let us = Sticker()
1494+ us.id = id
1495+ realm.add(us)
1496+ return us
1497+ }
1498+ }
1499+
1500+ override class func primaryKey() -> String? {
1501+ return "id"
1502+ }
1503+ override static func ignoredProperties() -> [String] {
1504+ return ["imageID"]
1505+ }
1506+}
1507+typealias StickerID = Int
1508+typealias StickerPackID = Int
1509+
1510+class Gift: Object {
1511+ @objc dynamic var id: GiftID = 0
1512+ @objc dynamic var image: RealmPhoto?
1513+ var imageID: String {
1514+ return "gift\(id)image"
1515+ }
1516+ convenience init(id: GiftID, surl: String) {
1517+ self.init()
1518+ self.id = id
1519+ self.image = RealmPhoto.by(imageID, surl: surl)
1520+ }
1521+ class func by(id: GiftID) -> Gift {
1522+ let realm = try! Realm()
1523+ if let us = realm.object(ofType: Gift.self, forPrimaryKey: id) {
1524+ return us
1525+ } else {
1526+ let us = Gift()
1527+ us.id = id
1528+ realm.add(us)
1529+ return us
1530+ }
1531+ }
1532+
1533+ override class func primaryKey() -> String? {
1534+ return "id"
1535+ }
1536+ override static func ignoredProperties() -> [String] {
1537+ return ["imageID"]
1538+ }
1539+}
1540+typealias GiftID = Int
1541+
1542+class Link: Object {
1543+ var url: URL? {
1544+ get {
1545+ return surl =>> {URL(string: $0)}
1546+ }
1547+ }
1548+ @objc dynamic var surl: String = ""
1549+ @objc dynamic var title: String?
1550+ @objc dynamic var caption: String?
1551+ @objc dynamic var linkDescription: String?
1552+ @objc dynamic var photo: Photo?
1553+ let isExternal = RealmOptional<Bool>()
1554+ //var product: Market?
1555+ var rating: Rating? {
1556+ get {
1557+ if let stars = ratingStars.value,
1558+ let count = ratingReviewCount.value {
1559+ return Rating(stars: stars, reviewCount: count)
1560+ }
1561+ return nil
1562+ }
1563+ set {
1564+ ratingStars.value = newValue?.stars
1565+ ratingReviewCount.value = newValue?.reviewCount
1566+ }
1567+ }
1568+ private let ratingStars = RealmOptional<Int>()
1569+ private let ratingReviewCount = RealmOptional<Int>()
1570+ //let application: Application?
1571+ var button: LinkButton? {
1572+ get {
1573+ if let title = buttonTitle,
1574+ let url = buttonURL {
1575+ return LinkButton(title: title, url: url)
1576+ }
1577+ return nil
1578+ }
1579+ set {
1580+ buttonURL = newValue?.url
1581+ buttonTitle = newValue?.title
1582+ }
1583+ }
1584+ @objc private dynamic var buttonTitle: String?
1585+ private var buttonURL: URL? {
1586+ get {
1587+ return buttonSURL =>> {URL(string: $0)}
1588+ }
1589+ set {
1590+ buttonSURL = newValue?.absoluteString
1591+ }
1592+ }
1593+ private var buttonSURL: String?
1594+ //let previewURL: URL
1595+
1596+ class func by(surl: String) -> Link {
1597+ let realm = try! Realm()
1598+ if let us = realm.object(ofType: Link.self, forPrimaryKey: surl) {
1599+ return us
1600+ } else {
1601+ let us = Link()
1602+ us.surl = surl
1603+ realm.add(us)
1604+ return us
1605+ }
1606+ }
1607+ override class func primaryKey() -> String? {
1608+ return "surl"
1609+ }
1610+ override static func ignoredProperties() -> [String] {
1611+ return ["url", "rating", "button", "buttonURL"]
1612+ }
1613+}
1614+
1615+
1616+struct Rating {
1617+ let stars: Int
1618+ let reviewCount: Int
1619+}
1620+
1621+struct LinkButton {
1622+ let title: String
1623+ let url: URL
1624+}
1625+
1626+class Market: Object {
1627+ @objc dynamic var id: MarketID = 0
1628+ @objc dynamic var owner: User?
1629+ @objc dynamic var title: String?
1630+ @objc dynamic var marketDescription: String?
1631+ let amount = RealmOptional<Int>()
1632+ let price = RealmOptional<Int>()
1633+ let currencyID = RealmOptional<CurrencyID>()
1634+ @objc dynamic var currencyName: String?
1635+ @objc dynamic var priceString: String?
1636+ let categoryID = RealmOptional<MarketCategoryID>()
1637+ @objc dynamic var categoryName: String?
1638+ let sectionID = RealmOptional<MarketSectionID>()
1639+ @objc dynamic var sectionName: String?
1640+ @objc dynamic var image: RealmPhoto?
1641+ var imageID: String {
1642+ return "market\(id)image"
1643+ }
1644+ @objc dynamic var creationDate: Date?
1645+ var availability: MarketAvailability? {
1646+ set {
1647+ availabilityInt.value = newValue?.rawValue
1648+ }
1649+ get {
1650+ return availabilityInt.value =>> {MarketAvailability(rawValue: $0)}
1651+ }
1652+ }
1653+ private let availabilityInt = RealmOptional<Int>()
1654+ let photos = List<Photo>()
1655+ let canComment = RealmOptional<Bool>()
1656+ let canRepost = RealmOptional<Bool>()
1657+ let likesCount = RealmOptional<Int>()
1658+ let liked = RealmOptional<Bool>()
1659+
1660+ class func by(id: MarketID) -> Market {
1661+ let realm = try! Realm()
1662+ if let us = realm.object(ofType: Market.self, forPrimaryKey: id) {
1663+ return us
1664+ } else {
1665+ let us = Market()
1666+ us.id = id
1667+ realm.add(us)
1668+ return us
1669+ }
1670+ }
1671+
1672+ override class func primaryKey() -> String? {
1673+ return "id"
1674+ }
1675+ override static func ignoredProperties() -> [String] {
1676+ return ["imageID"]
1677+ }
1678+}
1679+typealias CurrencyID = Int
1680+typealias MarketCategoryID = Int
1681+typealias MarketSectionID = Int
1682+enum MarketAvailability: Int {
1683+ case availability = 0
1684+ case deleted
1685+ case notAvailability
1686+}
1687+typealias MarketID = Int
1688+
1689+class MarketAlbum: Object {
1690+ @objc dynamic var id: MarketAlbumID = 0
1691+ @objc dynamic var owner: User?
1692+ @objc dynamic var title: String?
1693+ @objc dynamic var photo: Photo?
1694+ let count = RealmOptional<Int>()
1695+ @objc dynamic var updateTime: Date?
1696+ override class func primaryKey() -> String? {
1697+ return "id"
1698+ }
1699+}
1700+typealias MarketAlbumID = Int
1701+
1702+class Video: Object {
1703+ @objc dynamic var id: VideoID = 0
1704+ @objc dynamic var owner: User?
1705+ @objc dynamic var title: String?
1706+ @objc dynamic var videoDescription: String?
1707+ let duration = RealmOptional<TimeInterval>()
1708+ @objc dynamic var thumbnail: RealmPhoto?
1709+ var thumbnailID: String {
1710+ return "video\(id)thumbnail"
1711+ }
1712+ var player: URL? {
1713+ get {
1714+ return playerSURL =>> {URL(string: $0)}
1715+ }
1716+ set {
1717+ playerSURL = newValue?.absoluteString
1718+ }
1719+ }
1720+ @objc dynamic var playerSURL: String?
1721+ @objc dynamic var accessKey: String?
1722+ @objc dynamic var isProcessing: Bool = false
1723+ @objc dynamic var isLive: Bool = false
1724+ convenience init(id: VideoID, owner: User, title: String, description: String, duration: TimeInterval) {
1725+ self.init()
1726+ self.id = id
1727+ self.owner = owner
1728+ self.title = title
1729+ self.videoDescription = description
1730+ self.duration.value = duration
1731+ }
1732+
1733+ class func by(id: VideoID) -> Video {
1734+ let realm = try! Realm()
1735+ if let us = realm.object(ofType: Video.self, forPrimaryKey: id) {
1736+ return us
1737+ } else {
1738+ let us = Video()
1739+ us.id = id
1740+ realm.add(us)
1741+ return us
1742+ }
1743+ }
1744+
1745+// func get(completion: ((Video?) -> ())? = nil) {
1746+// if let peerID = owner?.id {
1747+// var vi = "\(peerID)_\(id)"
1748+// if let key = accessKey {
1749+// vi.append("_\(key)")
1750+// }
1751+// VK.API.Video.get([VK.Arg.videos: vi]).send(onSuccess: { (response) in
1752+// if let json = response["items"].array?.first {
1753+// let video = VKData.processVideo(json)
1754+// completion?(video)
1755+// } else {
1756+// completion?(nil)
1757+// }
1758+// }, onError: { (error) in
1759+// completion?(nil)
1760+// })
1761+// } else {
1762+// completion?(nil)
1763+// }
1764+// }
1765+ func get(completion: ((Video?) -> ())? = nil) {
1766+ VKServer.get(video: self) { (data) in
1767+ if let json = JSON.init(data: data)["response"].array?.first {
1768+ let video = VKData.processVideo(json)
1769+ completion?(video)
1770+ }
1771+ }
1772+ }
1773+
1774+ override class func primaryKey() -> String? {
1775+ return "id"
1776+ }
1777+ override static func ignoredProperties() -> [String] {
1778+ return ["player", "thumbnailID"]
1779+ }
1780+}
1781+typealias VideoID = Int
1782+
1783+class Audio: Object {
1784+ @objc dynamic var id: AudioID = 0
1785+ @objc dynamic var owner: User?
1786+ @objc dynamic var artist: String?
1787+ @objc dynamic var title: String?
1788+ let duration = RealmOptional<TimeInterval>()
1789+ var url: URL? {
1790+ get {
1791+ return surl =>> {URL(string: $0)}
1792+ }
1793+ set {
1794+ surl = newValue?.absoluteString
1795+ }
1796+ }
1797+ @objc dynamic var surl: String?
1798+
1799+ convenience init(id: AudioID) {
1800+ self.init()
1801+ self.id = id
1802+ }
1803+ class func by(id: AudioID) -> Audio {
1804+ let realm = try! Realm()
1805+ if let us = realm.object(ofType: Audio.self, forPrimaryKey: id) {
1806+ return us
1807+ } else {
1808+ let us = Audio()
1809+ us.id = id
1810+ realm.add(us)
1811+ return us
1812+ }
1813+ }
1814+
1815+ override class func primaryKey() -> String? {
1816+ return "id"
1817+ }
1818+ override static func ignoredProperties() -> [String] {
1819+ return ["url"]
1820+ }
1821+}
1822+typealias AudioID = Int
1823+typealias LyricID = Int
1824+typealias AudioAlbumID = Int
1825+typealias GenreID = Int
1826+
1827+class Document: Object {
1828+ @objc dynamic var id: DocumentID = 0
1829+ @objc dynamic var owner: User?
1830+ @objc dynamic var title: String?
1831+ let size = RealmOptional<Int>()
1832+ @objc dynamic var fileExtension: String?
1833+ var url: URL? {
1834+ get {
1835+ return surl =>> {URL(string: $0)}
1836+ }
1837+ set {
1838+ surl = newValue?.absoluteString
1839+ }
1840+ }
1841+ @objc dynamic var surl: String?
1842+ @objc dynamic var image: RealmPhoto?
1843+ var imageID: String {
1844+ return "doc\(id)image"
1845+ }
1846+ var type: DocumentType? {
1847+ get {
1848+ return typeInt.value =>> {DocumentType(rawValue: $0)}
1849+ }
1850+ set {
1851+ typeInt.value = newValue?.rawValue
1852+ }
1853+ }
1854+ private let typeInt = RealmOptional<Int>()
1855+ convenience init(id: DocumentID, owner: User, title: String, size: Int, ext: String, url: URL, type: DocumentType) {
1856+ self.init()
1857+ self.id = id
1858+ self.owner = owner
1859+ self.title = title
1860+ self.size.value = size
1861+ self.fileExtension = ext
1862+ self.url = url
1863+ self.type = type
1864+ }
1865+ class func by(id: DocumentID) -> Document {
1866+ let realm = try! Realm()
1867+ if let us = realm.object(ofType: Document.self, forPrimaryKey: id) {
1868+ return us
1869+ } else {
1870+ let us = Document()
1871+ us.id = id
1872+ realm.add(us)
1873+ return us
1874+ }
1875+ }
1876+
1877+// func get(completion: ((Document?) -> ())? = nil) {
1878+// if let ownerID = owner?.id {
1879+// var doc = "\(ownerID)_\(id)"
1880+// // if let key = accessKey {
1881+// // doc.append("_\(key)")
1882+// // }
1883+// VK.API.Docs.getById([VK.Arg.docs: doc]).send(onSuccess: { response in
1884+// if let json = response.array?.first {
1885+// let document = VKData.processDocument(json)
1886+// completion?(document)
1887+// } else {
1888+// completion?(nil)
1889+// }
1890+// }, onError: { (error) in
1891+// completion?(nil)
1892+// })
1893+// } else {
1894+// completion?(nil)
1895+// }
1896+// }
1897+ func get(completion: ((Document?) -> ())? = nil) {
1898+ VKServer.get(doc: self) { (data) in
1899+ if let json = JSON.init(data: data)["response"].array?.first {
1900+ let doc = VKData.processDocument(json)
1901+ completion?(doc)
1902+ }
1903+ }
1904+ }
1905+
1906+ override class func primaryKey() -> String? {
1907+ return "id"
1908+ }
1909+ override static func ignoredProperties() -> [String] {
1910+ return ["url", "imageID"]
1911+ }
1912+}
1913+typealias DocumentID = Int
1914+enum DocumentType: Int {
1915+ case text = 1
1916+ case archive
1917+ case gif
1918+ case image
1919+ case audio
1920+ case video
1921+ case eBook
1922+ case unknown
1923+}
1924+
1925+protocol Dialog {
1926+ var firstMessage: Message? { get }
1927+ var important: Bool { get set }
1928+ var name: String? { get }
1929+ func loadImage(into view: UIImageView, completion: (() -> ())?) -> RealmPhotoToken?
1930+ var messages: Messages? { get }
1931+ var id: Int { get }
1932+ var lastReadMessageID: Int { get set }
1933+ var count: RealmOptional<Int> { get }
1934+ var notificationSettings: RealmNotificatonSettings? { get set }
1935+ var peerID: Int { get }
1936+}
1937+
1938+func dialog(for id: DialogID) -> Dialog {
1939+ if id > multichatchatId { // multichat
1940+ let chat = Multichat.by(id: id - multichatchatId)
1941+ return chat
1942+
1943+ //} else if id < 0 { // Pablic
1944+
1945+ } else { // Normal user
1946+ let chat = Chat.by(user: User.by(id: id))
1947+ return chat
1948+ }
1949+}
1950+
1951+// For name conflicts
1952+func dialogFor(_ id: DialogID) -> Dialog {
1953+ return dialog(for: id)
1954+}
1955+
1956+class RealmDialog: Object, Comparable {
1957+
1958+ // TODO: Remove these two
1959+ @objc dynamic var dateLastSent: Date?
1960+ /// Number of times a message has been sent in the dateLastSent second
1961+ @objc dynamic var dateSentNumber = 0
1962+
1963+ var value: Dialog? {
1964+ get {
1965+ return dialog(for: id)
1966+ }
1967+ }
1968+
1969+ var peerID: Int? {
1970+ return value?.peerID
1971+ }
1972+
1973+ @objc dynamic var id: DialogID = 0
1974+
1975+ @objc dynamic var date: Date?
1976+
1977+ class func by(dialog: Dialog) -> RealmDialog {
1978+ return RealmDialog.by(peerID: dialog.peerID)
1979+ }
1980+
1981+ class func by(peerID: Int) -> RealmDialog {
1982+ if let dialog = (try? Realm())?.object(ofType: RealmDialog.self, forPrimaryKey: peerID) {
1983+ return dialog
1984+ } else {
1985+ let dialog = RealmDialog()
1986+ dialog.id = peerID
1987+ realmWrite({ (realm) in
1988+ realm.add(dialog)
1989+ })
1990+ return dialog
1991+ }
1992+ }
1993+
1994+ override class func primaryKey() -> String? {
1995+ return "id"
1996+ }
1997+ override static func ignoredProperties() -> [String] {
1998+ return ["value", "peerID"]
1999+ }
2000+}
2001+func ==(lhs: RealmDialog, rhs: RealmDialog) -> Bool {
2002+ return lhs.date == rhs.date
2003+}
2004+func <(lhs: RealmDialog, rhs: RealmDialog) -> Bool {
2005+ if let l = lhs.date,
2006+ let r = rhs.date {
2007+ return l < r
2008+ } else {
2009+ return false
2010+ }
2011+}
2012+typealias DialogID = Int
2013+
2014+//func ==(lhs: Dialog, rhs: Dialog) -> Bool {
2015+// return lhs.date == rhs.date
2016+//}
2017+//func <(lhs: Dialog, rhs: Dialog) -> Bool {
2018+// return lhs.date < rhs.date
2019+//}
2020+
2021+class RealmNotificatonSettings: Object {
2022+ var value: ChatNotificationSettings? {
2023+ get {
2024+ if let sound = notiSound.value,
2025+ let never = notiDateNever.value {
2026+ if never {
2027+ return ChatNotificationSettings(sound: sound, disabledUntil: .never)
2028+ } else {
2029+ return notiDate >>> {ChatNotificationSettings(sound: sound, disabledUntil: .date($0))}
2030+ }
2031+ }
2032+ return nil
2033+ }
2034+ set {
2035+ notiSound.value = newValue?.sound
2036+ if let newV = newValue {
2037+ switch newV.disabledUntil {
2038+ case .date(let date):
2039+ notiDate = date
2040+ notiDateNever.value = false
2041+ case .never:
2042+ notiDateNever.value = true
2043+ notiDate = nil
2044+ }
2045+ }
2046+ }
2047+ }
2048+ private let notiSound = RealmOptional<Bool>()
2049+ private let notiDateNever = RealmOptional<Bool>()
2050+ @objc private dynamic var notiDate: Date?
2051+ convenience init(value: ChatNotificationSettings?) {
2052+ self.init()
2053+ self.value = value
2054+ }
2055+}
2056+
2057+class Multichat: Object {
2058+ @objc dynamic var id: MultichatID = 0
2059+ @objc dynamic var admin: User?
2060+ var timers: [UserID: Timer] = [:]
2061+ var typingUsers: Set<User> = []
2062+ @objc dynamic var title: String?
2063+ let users = List<MultiChatUser>()
2064+ @objc dynamic var messages: Messages?
2065+ // let messages = List<Message>()
2066+ @objc dynamic var lastReadMessageID = 0
2067+ @objc dynamic var important = false
2068+ @objc dynamic var answered = false
2069+ @objc dynamic var date = Date()
2070+ let count = RealmOptional<Int>()
2071+ @objc dynamic var image: RealmPhoto?
2072+ var imageID: String {
2073+ return "mchat\(id)image"
2074+ }
2075+ let attachments = List<RealmMedia>()
2076+
2077+ @objc dynamic var notificationSettings: RealmNotificatonSettings?
2078+
2079+ var participationStatus: MultichatStatus? {
2080+ get {
2081+ return participationStatusInt.value =>> {MultichatStatus(rawValue: $0)}
2082+ }
2083+ set {
2084+ participationStatusInt.value = newValue?.rawValue
2085+ }
2086+ }
2087+ private let participationStatusInt = RealmOptional<Int>()
2088+
2089+ // convenience init(id: MultichatID, adminID: UserID, title: String, userIDs: [(user: UserID, invitedBy: UserID)]?, notificationSettings: MultichatNotificationSettings?, participationStatus: MultichatStatus?) {
2090+ // self.id = id
2091+ // self.adminID = adminID
2092+ // self.title = title
2093+ // self.userIDs = userIDs
2094+ // self.notificationSetting = notificationSettings
2095+ // self.participationStatus = participationStatus
2096+ // }
2097+ class func by(id: MultichatID) -> Multichat {
2098+ let realm = try! Realm()
2099+ if let us = realm.object(ofType: Multichat.self, forPrimaryKey: id) {
2100+ return us
2101+ } else {
2102+// VkServices.getMultichat(for: id)
2103+ let us = Multichat()
2104+ realmWrite { realm in
2105+ us.id = id
2106+ us.messages = Messages.by(id: us.peerID)
2107+ realm.add(us)
2108+ }
2109+ return us
2110+ }
2111+ }
2112+
2113+ override class func primaryKey() -> String? {
2114+ return "id"
2115+ }
2116+ override static func ignoredProperties() -> [String] {
2117+ return ["imageID", "typingUsers", "timers"]
2118+ }
2119+}
2120+
2121+extension Multichat: Dialog {
2122+ var firstMessage: Message? {
2123+ return messages?.first
2124+ }
2125+
2126+ var name: String? {
2127+ return title
2128+ }
2129+
2130+ func loadImage(into view: UIImageView, completion: (() -> ())? = nil) -> RealmPhotoToken? {
2131+ return image?.load(into: view, completion: completion)
2132+ }
2133+ // func image(completion: @escaping ((UIImage?) -> ())) {
2134+ // if let im = image?.image {
2135+ // im(completion)
2136+ // } else {
2137+ // completion(nil)
2138+ // }
2139+ // }
2140+
2141+ var peerID: Int {
2142+ return id + multichatchatId
2143+ }
2144+}
2145+
2146+class MultiChatUser: Object {
2147+ @objc dynamic var user: User?
2148+ @objc dynamic var invitedBy: User?
2149+ convenience init(_ user: User, invitedBy: User) {
2150+ self.init()
2151+ self.user = user
2152+ self.invitedBy = invitedBy
2153+ }
2154+}
2155+
2156+enum MultichatStatus: Int {
2157+ case active
2158+ case left
2159+ case kicked
2160+}
2161+
2162+class Chat: Object {
2163+ @objc dynamic var user: User? {
2164+ didSet {
2165+ user >>> {userID = $0.id}
2166+ }
2167+ }
2168+ var timer: Timer?
2169+ @objc dynamic var date = Date()
2170+ @objc dynamic var userID = 0
2171+ @objc dynamic var isTyping = false
2172+ @objc dynamic var lastReadMessageID = 0
2173+ let count = RealmOptional<Int>()
2174+ @objc dynamic var messages: Messages?
2175+ // let messages = List<Message>()
2176+ @objc dynamic var important = false
2177+ @objc dynamic var answered = false
2178+ let attachments = List<RealmMedia>()
2179+ @objc dynamic var notificationSettings: RealmNotificatonSettings?
2180+ class func by(user: User) -> Chat {
2181+ let realm = try! Realm()
2182+ if let us = realm.object(ofType: Chat.self, forPrimaryKey: user.id) {
2183+ return us
2184+ } else {
2185+ let us = Chat()
2186+ realmWrite { realm in
2187+ us.user = user
2188+ us.messages = Messages.by(id: us.peerID)
2189+ realm.add(us)
2190+ }
2191+ return us
2192+ }
2193+ }
2194+
2195+ convenience init(user: User) {
2196+ self.init()
2197+ self.user = user
2198+ }
2199+ override static func ignoredProperties() -> [String] {
2200+ return ["timer"]
2201+ }
2202+ override class func primaryKey() -> String? {
2203+ return "userID"
2204+ }
2205+}
2206+
2207+extension Chat: Dialog {
2208+ var firstMessage: Message? {
2209+ return messages?.first
2210+ }
2211+
2212+ var name: String? {
2213+ if let firstName = user?.firstName,
2214+ let lastName = user?.lastName {
2215+ return "\(firstName) \(lastName)"
2216+ } else {
2217+ return nil
2218+ }
2219+ }
2220+
2221+ func loadImage(into view: UIImageView, completion: (() -> ())? = nil) -> RealmPhotoToken? {
2222+ return user?.avatar?.load(into: view, completion: completion)
2223+ }
2224+
2225+ // func image(completion: @escaping ((UIImage?) -> ())) {
2226+ // if let im = user?.avatar?.image {
2227+ // im(completion)
2228+ // } else {
2229+ // completion(nil)
2230+ // }
2231+ // }
2232+
2233+ var id: Int {
2234+ return userID
2235+ }
2236+
2237+ var peerID: Int {
2238+ return userID
2239+ }
2240+}
2241+
2242+struct ChatNotificationSettings {
2243+ var sound: Bool
2244+ var disabledUntil: disableDate
2245+}
2246+
2247+enum disableDate {
2248+ case never
2249+ case date(Date)
2250+ init?(value: Int) {
2251+ switch value {
2252+ case -1:
2253+ self = .never
2254+ case 0:
2255+ return nil
2256+ default:
2257+ self = .date(Date(timeIntervalSince1970: TimeInterval(value)))
2258+ }
2259+ }
2260+}
2261+
2262+func isPhoneNumber(_ phone: String) -> Bool {
2263+ let nums = "1234567890".characters
2264+ let f002 = phone.characters.map{nums.contains($0) ? 1 : 0}
2265+ let foo1 = Double(sum(acc: 0, arr: f002))
2266+ let foo = foo1/Double(phone.characters.count)
2267+ return foo >= 0.4 ? true : false
2268+}
2269+
2270+