├── .gitmodules ├── .DS_Store ├── Eloquence ├── .DS_Store ├── Resources │ ├── README.me │ ├── iOS │ │ ├── menu.png │ │ ├── menu@2x.png │ │ ├── menu@3x.png │ │ ├── chevron-left.png │ │ ├── chevron-left@2x.png │ │ └── chevron-left@3x.png │ └── ICON-LICENSE ├── Eloquence │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── MacIcon.appiconset │ │ │ ├── eloquence_128.png │ │ │ ├── eloquence_16.png │ │ │ ├── eloquence_256.png │ │ │ ├── eloquence_32.png │ │ │ ├── eloquence_512.png │ │ │ ├── eloquence_128_@2.png │ │ │ ├── eloquence_16_@2.png │ │ │ ├── eloquence_256_@2.png │ │ │ ├── eloquence_32_@2.png │ │ │ ├── eloquence_512_@2.png │ │ │ └── Contents.json │ ├── Preferences │ │ ├── AccountTableCellView.swift │ │ ├── PreferencesGeneralViewController.swift │ │ ├── PreferencesGeneralViewController.xib │ │ ├── PreferencesAccountViewController.swift │ │ └── PreferencesAccountDialogWindowController.swift │ ├── Eloquence-Bridging-Header.h │ ├── EXMessageViewCellIncoming.swift │ ├── Cells │ │ ├── EXMessageViewCellOutgoing.swift │ │ ├── EXRosterContactCell.swift │ │ ├── EXMessageViewCell.swift │ │ └── EXRosterContactCell.xib │ ├── MainWindowController.swift │ ├── Dialogs │ │ └── ErrorDialog.swift │ ├── Info.plist │ ├── PXListView │ │ └── LICENSE-PXListView.md │ ├── AppDelegate.swift │ ├── MainSplitViewController.swift │ ├── RosterViewController.swift │ ├── MessageViewController.swift │ ├── EXMessageViewCellOutgoing.xib │ └── EXMessageViewCellIncoming.xib ├── EloquenceIOS │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── iOSIcon.appiconset │ │ │ ├── 29.png │ │ │ ├── 40.png │ │ │ ├── 50.png │ │ │ ├── 57.png │ │ │ ├── 72.png │ │ │ ├── 76.png │ │ │ ├── 29-1.png │ │ │ ├── 29x2-1.png │ │ │ ├── 29x2.png │ │ │ ├── 29x3.png │ │ │ ├── 40-1.png │ │ │ ├── 40x2-1.png │ │ │ ├── 40x2.png │ │ │ ├── 50x2.png │ │ │ ├── 57x2.png │ │ │ ├── 60x2-1.png │ │ │ ├── 60x2.png │ │ │ ├── 60x3-1.png │ │ │ ├── 60x3.png │ │ │ ├── 72x2.png │ │ │ ├── 76x2.png │ │ │ ├── 83.5x2.png │ │ │ └── Contents.json │ ├── GlobalMenuCell.swift │ ├── EMPreferencesButtonCell.swift │ ├── EMNotification.swift │ ├── EMPreferencesSwitchCell.swift │ ├── PreferencesAccountViewController.swift │ ├── EloquenceIOS-Bridging-Header.h │ ├── RosterCell.swift │ ├── ViewController.swift │ ├── EMAccountCell.swift │ ├── PadViewController.swift │ ├── Info.plist │ ├── AppDelegate.swift │ ├── RosterViewController.swift │ ├── EMAccountsViewController.swift │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── GlobalMenuViewController.swift │ ├── EMAddAccountViewController.swift │ ├── MessageViewController.swift │ └── PhoneMainViewController.swift ├── Core │ ├── XMPP │ │ ├── EloXMPPMessageArchiveManagementWithContactCoreDataStorage.h │ │ ├── XEP-0313 │ │ │ ├── XMPPMessage+Elo_XEP_0313.h │ │ │ ├── README.md │ │ │ ├── EloXMPPMessageArchiveManagement.xcdatamodeld │ │ │ │ ├── .xccurrentversion │ │ │ │ └── EloXMPPMessageArchiveManagement.xcdatamodel │ │ │ │ │ └── contents │ │ │ ├── XMPPMessage+Elo_XEP_0313.m │ │ │ ├── EloXMPPMessageArchiveManagement_Contact_CoreDataObject.h │ │ │ ├── EloXMPPMessageArchiveManagement_Message_CoreDataObject.h │ │ │ ├── EloXMPPMessageArchiveManagement_Contact_CoreDataObject.m │ │ │ ├── EloXMPPMessageArchiveManagementCoreDataStorage.h │ │ │ ├── EloXMPPMessageArchiveManagement.h │ │ │ └── EloXMPPMessageArchiveManagement_Message_CoreDataObject.m │ │ ├── EloContactList_Item_CoreDataObject.h │ │ ├── EloContactListCoreDataStorage.h │ │ ├── EloXMPPMessageArchiveManagementWithContactCoreDataStorage.m │ │ ├── EloContactList.xcdatamodeld │ │ │ └── EloContactList.xcdatamodel │ │ │ │ └── contents │ │ ├── EloContactList_Item_CoreDataObject.m │ │ ├── EloXMPPRoster.xcdatamodeld │ │ │ └── EloXMPPRoster.xcdatamodel │ │ │ │ └── contents │ │ ├── EloContactListCoreDataStorage.m │ │ └── EloContactListAndArchive.xcdatamodeld │ │ │ └── EloContactListAndArchive.xcdatamodel │ │ │ └── contents │ ├── EloAccountJid.swift │ ├── EloContactJid.swift │ ├── EloChats.swift │ ├── EloConstants.swift │ ├── EloXMPPRosterCoreDataStorage.h │ ├── EloFetchedResultsController.swift │ ├── EloMessage.swift │ ├── EloUtils.swift │ ├── EloPseudoStructs.swift │ ├── MulticastDelegateContainer.swift │ ├── DataModel.xcdatamodeld │ │ └── DataModel.xcdatamodel │ │ │ └── contents │ ├── EloConnections.swift │ ├── EloAccount.swift │ ├── DataController.swift │ ├── EloCapabilities.swift │ ├── EloXMPPRoster.xcdatamodeld │ │ └── EloXMPPRoster.xcdatamodel │ │ │ └── contents │ ├── EloXMPPRosterCoreDataStorage.m │ └── EloRoster.swift ├── EloquenceIOSTests │ ├── Info.plist │ └── EloquenceIOSTests.swift ├── EloquenceTests │ ├── Info.plist │ └── EloquenceTests.m ├── EloquenceUITests │ ├── Info.plist │ └── EloquenceUITests.m ├── EloquenceIOSUITests │ ├── Info.plist │ └── EloquenceIOSUITests.swift └── Eloquence.xcodeproj │ ├── xcuserdata │ └── farion.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ └── xcshareddata │ └── xcschemes │ ├── EloquenceIOS.xcscheme │ └── Eloquence.xcscheme ├── Eloquence.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── WorkspaceSettings.xcsettings ├── .travis.yml ├── .gitignore ├── Podfile └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/.DS_Store -------------------------------------------------------------------------------- /Eloquence/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/.DS_Store -------------------------------------------------------------------------------- /Eloquence/Resources/README.me: -------------------------------------------------------------------------------- 1 | Icons from http://useiconic.com/open 2 | For license (MIT) see ICON-LICENSE 3 | -------------------------------------------------------------------------------- /Eloquence/Resources/iOS/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Resources/iOS/menu.png -------------------------------------------------------------------------------- /Eloquence/Resources/iOS/menu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Resources/iOS/menu@2x.png -------------------------------------------------------------------------------- /Eloquence/Resources/iOS/menu@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Resources/iOS/menu@3x.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Eloquence/Resources/iOS/chevron-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Resources/iOS/chevron-left.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Eloquence/Resources/iOS/chevron-left@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Resources/iOS/chevron-left@2x.png -------------------------------------------------------------------------------- /Eloquence/Resources/iOS/chevron-left@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Resources/iOS/chevron-left@3x.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Preferences/AccountTableCellView.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class AccountTableCellView: NSTableCellView { 4 | 5 | } -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/GlobalMenuCell.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | class GlobalMenuCell: UITableViewCell { 5 | 6 | } -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/50.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/57.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/72.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/76.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29-1.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29x2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29x2-1.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29x2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29x3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/29x3.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40-1.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40x2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40x2-1.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/40x2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/50x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/50x2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/57x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/57x2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x2-1.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x3-1.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/60x3.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/72x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/72x2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/76x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/76x2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/83.5x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/83.5x2.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_128.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_16.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_256.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_32.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_512.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_128_@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_128_@2.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_16_@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_16_@2.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_256_@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_256_@2.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_32_@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_32_@2.png -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_512_@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farion/eloquence/HEAD/Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/eloquence_512_@2.png -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/EMPreferencesButtonCell.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | class EMPreferencesButtonCell:UITableViewCell { 5 | 6 | @IBOutlet weak var nameText: UILabel! 7 | @IBOutlet weak var descriptionText: UILabel! 8 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloXMPPMessageArchiveManagementWithContactCoreDataStorage.h: -------------------------------------------------------------------------------- 1 | #import "EloXMPPMessageArchiveManagementCoreDataStorage.h" 2 | 3 | @interface EloXMPPMessageArchiveManagementWithContactCoreDataStorage : EloXMPPMessageArchiveManagementCoreDataStorage 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/EMNotification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct EMNotification { 4 | private static let PREFIX = "com.trigonmedia.eloquence.mobile." 5 | static let SHOW_ACCOUNTS = PREFIX + "showAccounts" 6 | static let SHOW_PREFERENCES = PREFIX + "showPreferences" 7 | } -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/EMPreferencesSwitchCell.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | class EMPreferencesSwitchCell:UITableViewCell { 5 | 6 | @IBOutlet weak var nameText: UILabel! 7 | @IBOutlet weak var descriptionText: UILabel! 8 | @IBOutlet weak var valueSwitch: UISwitch! 9 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/XMPPMessage+Elo_XEP_0313.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface XMPPMessage (Elo_XEP_0313) 5 | 6 | - (NSString *)result; 7 | - (XMPPMessage *) forwardedMessage; 8 | - (NSString *)messageId; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /Eloquence.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/PreferencesAccountViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | class PreferencesAccountViewController: UIViewController { 5 | 6 | 7 | 8 | override func preferredStatusBarStyle() -> UIStatusBarStyle { 9 | return UIStatusBarStyle.LightContent; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/Eloquence-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "EloXMPPRosterCoreDataStorage.h" 2 | #import "EloXMPPMessageArchiveManagement.h" 3 | #import "EloContactListCoreDataStorage.h" 4 | #import "EloContactList_Item_CoreDataObject.h" 5 | #import "EloXMPPMessageArchiveManagementWithContactCoreDataStorage.h" 6 | #import "XMPPMessage+Elo_XEP_0313.h" -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/EloquenceIOS-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "EloXMPPRosterCoreDataStorage.h" 2 | #import "EloXMPPMessageArchiveManagement.h" 3 | #import "EloXMPPMessageArchiveManagementWithContactCoreDataStorage.h" 4 | #import "EloContactListCoreDataStorage.h" 5 | #import "EloContactList_Item_CoreDataObject.h" 6 | #import "XMPPMessage+Elo_XEP_0313.h" -------------------------------------------------------------------------------- /Eloquence.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/README.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | * Support urn:xmpp:mam:1 and urn:xmpp:mam:tmp. Currently only urn:xmpp:mam:0 works. Should be no big deal. 5 | * Do fuzzy message merging. 6 | * Use XEP-0359 for message merging if available. Do this in separate folder, to keep XEPs separated. 7 | * Pull this back to XMPPFramework as soon as possible. -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/RosterCell.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | class RosterCell:UITableViewCell { 5 | 6 | @IBOutlet weak var name: UILabel! 7 | 8 | @IBOutlet weak var viaLabel: UILabel! 9 | 10 | @IBOutlet weak var lastMessageLabel: UILabel! 11 | 12 | @IBOutlet weak var avatar: UIImageView! 13 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagement.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | EloXMPPMessageArchiveManagement.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | before_install: 4 | - pod repo update 5 | - pod install 6 | script: 7 | - xctool -workspace Eloquence.xcworkspace -scheme Eloquence -sdk macosx10.11 build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO 8 | - xctool -workspace Eloquence.xcworkspace -scheme EloquenceIOS -sdk iphoneos9.3 build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO 9 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/EXMessageViewCellIncoming.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EXMessageViewIncoming.swift 3 | // Eloquence 4 | // 5 | // Created by Frieder Reinhold on 14.04.16. 6 | // Copyright © 2016 TRIGONmedia. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class EXMessageViewCellIncoming: EXMessageViewCell { 12 | 13 | 14 | override func getNibName() -> String { 15 | return "EXMessageViewCellIncoming"; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /Eloquence/Eloquence/Cells/EXMessageViewCellOutgoing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExMessageViewCellOutgoing.swift 3 | // Eloquence 4 | // 5 | // Created by Frieder Reinhold on 14.04.16. 6 | // Copyright © 2016 TRIGONmedia. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class EXMessageViewCellOutgoing: EXMessageViewCell { 12 | 13 | override func getNibName() -> String { 14 | return "EXMessageViewCellOutgoing"; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /Eloquence/Eloquence/MainWindowController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import XMPPFramework 3 | 4 | class MainWindowController: NSWindowController { 5 | 6 | override func windowDidLoad() { 7 | super.windowDidLoad() 8 | 9 | self.window!.titleVisibility = NSWindowTitleVisibility.Hidden; 10 | self.window!.titlebarAppearsTransparent = true; 11 | self.window!.styleMask |= NSFullSizeContentViewWindowMask; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class ViewController: UIViewController { 4 | 5 | override func viewDidLoad() { 6 | super.viewDidLoad() 7 | // Do any additional setup after loading the view, typically from a nib. 8 | } 9 | 10 | override func didReceiveMemoryWarning() { 11 | super.didReceiveMemoryWarning() 12 | // Dispose of any resources that can be recreated. 13 | } 14 | 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Eloquence/Core/EloAccountJid.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XMPPFramework 3 | 4 | struct EloAccountJid:Hashable { 5 | let jid:String 6 | 7 | var hashValue: Int { 8 | return jid.hashValue 9 | } 10 | 11 | init(_ jid: String) { 12 | self.jid = jid 13 | } 14 | 15 | var xmppJid: XMPPJID { 16 | return XMPPJID.jidWithString(jid) 17 | } 18 | } 19 | 20 | func == (jid1: EloAccountJid, jid2: EloAccountJid) -> Bool { 21 | return jid1.jid == jid2.jid 22 | } -------------------------------------------------------------------------------- /Eloquence/Core/EloContactJid.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XMPPFramework 3 | 4 | struct EloContactJid:Hashable { 5 | let jid:String 6 | 7 | var hashValue: Int { 8 | return jid.hashValue 9 | } 10 | 11 | init(_ jid: String) { 12 | self.jid = jid 13 | } 14 | 15 | var xmppJid: XMPPJID { 16 | return XMPPJID.jidWithString(jid) 17 | } 18 | } 19 | 20 | func == (jid1: EloContactJid, jid2: EloContactJid) -> Bool { 21 | return jid1.jid == jid2.jid 22 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloContactList_Item_CoreDataObject.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #if TARGET_OS_IPHONE 5 | #import 6 | #else 7 | #import 8 | #endif 9 | 10 | #import 11 | 12 | @interface EloContactList_Item_CoreDataObject : NSManagedObject 13 | 14 | @property (nonatomic, strong) XMPPJID * bareJid; 15 | @property (nonatomic, strong) NSString * bareJidStr; 16 | 17 | @property (nonatomic, strong) NSString * streamBareJidStr; 18 | 19 | @end -------------------------------------------------------------------------------- /Eloquence/Core/EloChats.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XMPPFramework 3 | 4 | class EloChats { 5 | 6 | static let sharedInstance = EloChats(); 7 | 8 | var chats = [EloChatId:EloChat]() 9 | 10 | func getChat(from: EloAccountJid, to:EloContactJid) -> EloChat { 11 | 12 | let chatId = EloChatId(from: from, to: to) 13 | 14 | if(chats[chatId] == nil){ 15 | chats[chatId] = EloChat(from:from, to:to); 16 | } 17 | 18 | return chats[chatId]!; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Eloquence/Core/EloConstants.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct EloConstants { 4 | 5 | private static let PREFIX = "com.trigonmedia.eloquence." 6 | 7 | static let CONNECTION_ONLINE = PREFIX + "connectionOnline" 8 | static let CONNECTION_OFFLINE = PREFIX + "connectionOffline" 9 | static let ROSTER_CHANGED = PREFIX + "rosterChanged" 10 | static let ACTIVATE_CONTACT = PREFIX + "activateContact" 11 | static let SHOW_ACCOUNTS = PREFIX + "showAccounts" 12 | static let SHOW_PREFERENCES = PREFIX + "showPreferences" 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/xcode 2 | 3 | ### Xcode ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | ## Pods ## 29 | Pods/ 30 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/EMAccountCell.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | 5 | class EMAccountCell:UITableViewCell { 6 | 7 | 8 | @IBOutlet weak var avatarImage: UIImageView! 9 | @IBOutlet weak var disableSwitch: UISwitch! 10 | @IBOutlet weak var jidLabel: UILabel! 11 | @IBOutlet weak var statusLabel: UILabel! 12 | @IBOutlet weak var connectionLabel: UILabel! 13 | 14 | var account:EloAccount!; 15 | 16 | @IBAction func toggleAvailability(sender: UISwitch) { 17 | account.setAutoConnect(sender.on); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloContactListCoreDataStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import "EloXMPPMessageArchiveManagement_Contact_CoreDataObject.h" 5 | 6 | @interface EloContactListCoreDataStorage : XMPPCoreDataStorage 7 | 8 | + (instancetype)sharedInstance; 9 | 10 | - (void)didUpdateOrInsertMamContact:(EloXMPPMessageArchiveManagement_Contact_CoreDataObject*) mamContact; 11 | - (void)didUpdateOrInsertUser:(XMPPUserCoreDataStorageObject*) user; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Eloquence/Core/EloXMPPRosterCoreDataStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | #import 7 | #import 8 | 9 | /** 10 | * This class is an example implementation of XMPPRosterStorage using core data. 11 | * You are free to substitute your own roster storage class. 12 | **/ 13 | 14 | @interface EloXMPPRosterCoreDataStorage : XMPPRosterCoreDataStorage 15 | 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Eloquence/Core/EloFetchedResultsController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if os(iOS) 4 | typealias EloFetchedResultsController = NSFetchedResultsController 5 | typealias EloFetchedResultsControllerDelegate = NSFetchedResultsControllerDelegate 6 | typealias EloFetchedResultsChangeType = NSFetchedResultsChangeType 7 | #else 8 | import KSPFetchedResultsController 9 | typealias EloFetchedResultsController = KSPFetchedResultsController 10 | typealias EloFetchedResultsControllerDelegate = KSPFetchedResultsControllerDelegate 11 | typealias EloFetchedResultsChangeType = KSPFetchedResultsChangeType 12 | #endif -------------------------------------------------------------------------------- /Eloquence/Core/EloMessage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class EloMessage:NSObject { 4 | var author: String? 5 | var text: String? 6 | var isOutgoing = false 7 | let timestamp: NSDate 8 | 9 | override init() { 10 | timestamp = NSDate() 11 | } 12 | 13 | init(_ object:EloXMPPMessageArchiveManagement_Message_CoreDataObject){ 14 | isOutgoing = object.isOutgoing 15 | timestamp = object.timestamp 16 | text = object.body 17 | if(text == nil){ 18 | text = "Composing ..." 19 | } 20 | 21 | author = "foo" 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /Eloquence/Eloquence/Dialogs/ErrorDialog.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | class ErrorDialog { 5 | 6 | func showError(msg:String){ 7 | let alert = NSAlert(); 8 | alert.messageText = "Error"; 9 | alert.addButtonWithTitle("OK"); 10 | alert.informativeText = msg; 11 | 12 | let windowWithKeyboardAccess = NSApplication.sharedApplication().keyWindow; 13 | 14 | if(windowWithKeyboardAccess != nil) { 15 | alert.beginSheetModalForWindow(windowWithKeyboardAccess!, completionHandler: nil ); 16 | } 17 | 18 | //TODO also do a real window in case of no window active 19 | } 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /Eloquence/Core/EloUtils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // Basically taken from: https://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1 4 | 5 | var GlobalMainQueue: dispatch_queue_t { 6 | return dispatch_get_main_queue() 7 | } 8 | 9 | var GlobalUserInteractiveQueue: dispatch_queue_t { 10 | return dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE,0) 11 | } 12 | 13 | var GlobalUserInitiatedQueue: dispatch_queue_t { 14 | return dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0) 15 | } 16 | 17 | var GlobalUtilityQueue: dispatch_queue_t { 18 | return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0) 19 | } 20 | 21 | var GlobalBackgroundQueue: dispatch_queue_t { 22 | return dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0) 23 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloXMPPMessageArchiveManagementWithContactCoreDataStorage.m: -------------------------------------------------------------------------------- 1 | #import "EloXMPPMessageArchiveManagementWithContactCoreDataStorage.h" 2 | #import "EloContactListCoreDataStorage.h" 3 | 4 | @implementation EloXMPPMessageArchiveManagementWithContactCoreDataStorage 5 | 6 | 7 | - (NSString *)managedObjectModelName 8 | { 9 | return @"XMPPMessageArchiveManagement"; 10 | } 11 | 12 | 13 | - (void)willInsertContact:(EloXMPPMessageArchiveManagement_Contact_CoreDataObject *)contact 14 | { 15 | [EloContactListCoreDataStorage.sharedInstance didUpdateOrInsertMamContact:contact]; 16 | } 17 | 18 | - (void)didUpdateContact:(EloXMPPMessageArchiveManagement_Contact_CoreDataObject *)contact 19 | { 20 | [EloContactListCoreDataStorage.sharedInstance didUpdateOrInsertMamContact:contact]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Eloquence/EloquenceTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Eloquence/EloquenceUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Eloquence/Core/EloPseudoStructs.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct EloPseudoStructs { 4 | 5 | } 6 | 7 | public class EloChatId:Hashable { 8 | let from: EloAccountJid 9 | let to:EloContactJid 10 | public var hashValue: Int { 11 | return from.hashValue ^ to.hashValue 12 | } 13 | public var toString: String { 14 | return from.jid + "|" + to.jid 15 | } 16 | init(identifier:String){ 17 | let chatParts = identifier.characters.split{$0 == "|"}.map(String.init) 18 | self.from = EloAccountJid(chatParts[0]) 19 | self.to = EloContactJid(chatParts[1]) 20 | } 21 | init(from:EloAccountJid,to:EloContactJid){ 22 | self.from = from 23 | self.to = to 24 | } 25 | } 26 | 27 | public func ==(a: EloChatId, b: EloChatId) -> Bool { 28 | return a.from == b.from && a.to == b.to 29 | } -------------------------------------------------------------------------------- /Eloquence/EloquenceIOSUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloContactList.xcdatamodeld/EloContactList.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Eloquence/Core/MulticastDelegateContainer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // Basically taken from 4 | // http://stackoverflow.com/a/33300974 5 | 6 | protocol MulticastDelegateContainer { 7 | 8 | typealias DelegateType : NSObjectProtocol 9 | var multicastDelegate : [DelegateType] {set get} 10 | } 11 | 12 | extension MulticastDelegateContainer { 13 | 14 | mutating func addDelegate(delegate : DelegateType) { 15 | multicastDelegate.append(delegate) 16 | } 17 | 18 | mutating func removeDelegate(delegate : DelegateType) { 19 | guard let indexToRemove = self.multicastDelegate.indexOf({(item : DelegateType) -> Bool in 20 | return item === delegate 21 | }) else {return} 22 | 23 | multicastDelegate.removeAtIndex(indexToRemove) 24 | } 25 | 26 | func invokeDelegate(invocation: (DelegateType) -> ()) { 27 | for delegate in multicastDelegate { 28 | invocation(delegate) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Eloquence/Eloquence/Preferences/PreferencesGeneralViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import CCNPreferencesWindowController 3 | 4 | class PreferencesGeneralViewController: NSViewController, CCNPreferencesWindowControllerProtocol { 5 | 6 | var preferenceWindow: NSWindow; 7 | 8 | init? (window: NSWindow){ 9 | preferenceWindow = window; 10 | super.init(nibName: nil, bundle: nil); 11 | } 12 | 13 | required init?(coder: NSCoder) { 14 | preferenceWindow = NSWindow(); 15 | super.init(coder: coder); 16 | } 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | // Do view setup here. 21 | } 22 | 23 | func preferenceIdentifier() -> String! { 24 | return "GeneralPreferencesIdentifier"; 25 | } 26 | 27 | func preferenceTitle() -> String! { 28 | return "General"; 29 | } 30 | 31 | func preferenceIcon() -> NSImage! { 32 | return NSImage(named: NSImageNamePreferencesGeneral); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /Eloquence/EloquenceTests/EloquenceTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // EloquenceTests.m 3 | // EloquenceTests 4 | // 5 | // Created by Frieder Reinhold on 16.02.16. 6 | // Copyright © 2016 TRIGONmedia. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EloquenceTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation EloquenceTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Eloquence/Core/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Eloquence/Resources/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/PadViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | class PadViewController: UIViewController { 5 | 6 | @IBOutlet weak var rosterView: UIView! 7 | @IBOutlet weak var messageView: UIView! 8 | 9 | override func viewDidLoad() { 10 | 11 | let roster = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("Roster") as! RosterViewController 12 | 13 | 14 | roster.view.frame = rosterView.bounds 15 | rosterView.addSubview(roster.view); 16 | addChildViewController(roster); 17 | roster.didMoveToParentViewController(self); 18 | 19 | let message = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("Message") as! MessageViewController 20 | 21 | message.view.frame = messageView.bounds 22 | messageView.addSubview(message.view); 23 | addChildViewController(message); 24 | message.didMoveToParentViewController(self); 25 | } 26 | 27 | override func preferredStatusBarStyle() -> UIStatusBarStyle { 28 | return UIStatusBarStyle.LightContent; 29 | } 30 | } -------------------------------------------------------------------------------- /Eloquence/EloquenceIOSTests/EloquenceIOSTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EloquenceIOSTests.swift 3 | // EloquenceIOSTests 4 | // 5 | // Created by Frieder Reinhold on 28.02.16. 6 | // Copyright © 2016 TRIGONmedia. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import EloquenceIOS 11 | 12 | class EloquenceIOSTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Eloquence/Core/EloConnections.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreData 3 | import XMPPFramework 4 | 5 | @objc 6 | protocol EloConnectionsDelegate: NSObjectProtocol { 7 | 8 | } 9 | 10 | 11 | class EloConnections: MulticastDelegateContainer { 12 | 13 | static let sharedInstance = EloConnections(); 14 | 15 | typealias DelegateType = EloConnectionsDelegate; 16 | var multicastDelegate = [EloConnectionsDelegate]() 17 | 18 | var connections = [EloAccountJid:EloConnection](); 19 | 20 | //TODO threadsafe 21 | 22 | func connectAllAccounts(){ 23 | 24 | let accounts = DataController.sharedInstance.getAccounts(); 25 | NSLog("%@","connectall"); 26 | 27 | 28 | NSLog("accountcount: %d",accounts.count); 29 | for account in accounts { 30 | 31 | let accountJid = account.getJid() 32 | connections[accountJid] = EloConnection(account: account); 33 | if(account.isAutoConnect()){ 34 | connections[accountJid]!.connect(); 35 | } 36 | } 37 | } 38 | 39 | func getConnection(accountJid:EloAccountJid) -> EloConnection { 40 | return connections[accountJid]! ; //otherwise it is a bad bug 41 | } 42 | 43 | } 44 | // invokeDelegate { $0.method() } -------------------------------------------------------------------------------- /Eloquence/Eloquence/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Eloquence 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.social-networking 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | NSHumanReadableCopyright 30 | Copyright © 2016 TRIGONmedia. All rights reserved. 31 | NSMainStoryboardFile 32 | Main 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /Eloquence/Eloquence.xcodeproj/xcuserdata/farion.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Eloquence.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | EloquenceIOS.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 8 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | BFB6307E1C73CE980008179D 21 | 22 | primary 23 | 24 | 25 | BFB630941C73CE990008179D 26 | 27 | primary 28 | 29 | 30 | BFB6309F1C73CE990008179D 31 | 32 | primary 33 | 34 | 35 | BFEC352B1C839DA1005CB593 36 | 37 | primary 38 | 39 | 40 | BFEC353E1C839DA2005CB593 41 | 42 | primary 43 | 44 | 45 | BFEC35491C839DA2005CB593 46 | 47 | primary 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/Cells/EXRosterContactCell.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JNWCollectionView 3 | import XMPPFramework 4 | 5 | class EXRosterContactCell:JNWCollectionViewCell { 6 | 7 | @IBOutlet var cellView: NSView! 8 | @IBOutlet var avatarImage: NSImageView! 9 | @IBOutlet var nameLabel: NSTextField! 10 | @IBOutlet var viaLabel: NSTextField! 11 | 12 | override init(frame frameRect: NSRect) { 13 | super.init(frame: frameRect) 14 | commonInit(frame) 15 | } 16 | 17 | required init?(coder: NSCoder) { 18 | super.init(coder: coder) 19 | commonInit(self.frame) 20 | } 21 | 22 | private func commonInit(frame:NSRect){ 23 | NSBundle.mainBundle().loadNibNamed("EXRosterContactCell", owner: self, topLevelObjects: nil) 24 | 25 | let contentFrame = NSMakeRect(0,0,frame.size.width,frame.size.height) 26 | self.cellView.frame = contentFrame 27 | contentView = cellView 28 | 29 | /* nameLabel = NSTextField() 30 | contentView.addSubview(nameLabel) 31 | nameLabel.stringValue = "LABEL" 32 | nameLabel.sizeToFit()*/ 33 | } 34 | 35 | 36 | func setItem(user: EloContactList_Item_CoreDataObject) { 37 | nameLabel.stringValue = user.bareJidStr; 38 | viaLabel.stringValue = "via " + user.streamBareJidStr; 39 | } 40 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/XMPPMessage+Elo_XEP_0313.m: -------------------------------------------------------------------------------- 1 | #import "XMPPMessage+Elo_XEP_0313.h" 2 | 3 | @implementation XMPPMessage (Elo_XEP_0313) 4 | 5 | - (NSString *)result 6 | { 7 | return [[self elementForName:@"result" xmlns:@"urn:xmpp:mam:0"] stringValue]; 8 | } 9 | 10 | - (NSString *)messageId 11 | { 12 | return [[self attributeForName:@"id"] stringValue]; 13 | } 14 | 15 | - (XMPPMessage *) forwardedMessage 16 | { 17 | //TODO support mam:tmp and mam:1 18 | NSXMLElement* forwardedElement = [[self elementForName:@"result" xmlns:@"urn:xmpp:mam:0"] elementForName:@"forwarded" xmlns:@"urn:xmpp:forward:0"]; 19 | 20 | NSXMLElement* messageElement = [forwardedElement elementForName:@"message" xmlns: @"jabber:client"]; 21 | 22 | //XEP-0313 adds a delay element outside message instead inside. 23 | //So we "fix" the element to work as expected internally. 24 | 25 | NSXMLElement* delayElement = [forwardedElement elementForName:@"delay" xmlns: @"urn:xmpp:delay"]; 26 | 27 | //sometimes server sends additional delay. Noticed on ejabberd. 28 | if([messageElement elementForName:@"delay" xmlns: @"urn:xmpp:delay"] != nil){ 29 | [messageElement removeElementForName:@"delay"]; 30 | } 31 | 32 | [messageElement addChild: [delayElement copy]]; 33 | 34 | return [XMPPMessage messageFromElement: messageElement]; 35 | } 36 | 37 | @end -------------------------------------------------------------------------------- /Eloquence/EloquenceUITests/EloquenceUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // EloquenceUITests.m 3 | // EloquenceUITests 4 | // 5 | // Created by Frieder Reinhold on 16.02.16. 6 | // Copyright © 2016 TRIGONmedia. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EloquenceUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation EloquenceUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOSUITests/EloquenceIOSUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EloquenceIOSUITests.swift 3 | // EloquenceIOSUITests 4 | // 5 | // Created by Frieder Reinhold on 28.02.16. 6 | // Copyright © 2016 TRIGONmedia. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class EloquenceIOSUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagement_Contact_CoreDataObject.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | 6 | 7 | @interface EloXMPPMessageArchiveManagement_Contact_CoreDataObject : NSManagedObject 8 | 9 | @property (nonatomic, strong) XMPPJID * bareJid; // Transient (proper type, not on disk) 10 | @property (nonatomic, strong) NSString * bareJidStr; // Shadow (binary data, written to disk) 11 | 12 | @property (nonatomic, strong) NSDate * mostRecentMessageTimestamp; 13 | @property (nonatomic, strong) NSString * mostRecentMessageBody; 14 | @property (nonatomic, strong) NSNumber * mostRecentMessageOutgoing; 15 | 16 | @property (nonatomic, strong) NSString * streamBareJidStr; 17 | 18 | /** 19 | * This method is called immediately before the object is inserted into the managedObjectContext. 20 | * At this point, all normal properties have been set. 21 | * 22 | * If you extend EloXMPPMessageArchiveManagement_Contact_CoreDataObject, 23 | * you can use this method as a hook to set your custom properties. 24 | **/ 25 | - (void)willInsertObject; 26 | 27 | /** 28 | * This method is called after any properties on the object have been updated, 29 | * due to a message being added to the conversation. 30 | * At this point, any changed properties have been updated. 31 | * 32 | * If you extend EloXMPPMessageArchiveManagement_Contact_CoreDataObject, 33 | * you can use this method as a hook to update your custom properties. 34 | **/ 35 | - (void)didUpdateObject; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | target 'Eloquence' do 2 | platform :osx, '10.11' 3 | pod "CocoaLumberjack", '~> 2.0' 4 | pod 'CocoaAsyncSocket', '~> 7.4' 5 | pod 'CCNPreferencesWindowController', '~> 1.2.2' 6 | pod 'KeychainSwift', '~> 3.0' 7 | pod 'KissXML', :git => 'https://github.com/robbiehanson/KissXML.git', :commit => 'ca4aaeaa38bedaf0f42b450e7c75170dc3a788da' 8 | pod 'XMPPFramework', :git => 'https://github.com/robbiehanson/XMPPFramework.git', :commit => 'c5ab86ffd9c74b41626910813bc5955eb900c07f' 9 | pod 'SwiftyUserDefaults','~> 2.1.3' 10 | pod 'KSPFetchedResultsController', '~> 1.1.1' 11 | pod 'JNWCollectionView', :git => 'https://github.com/jwilling/JNWCollectionView.git' 12 | pod "HockeySDK-Mac", '~> 4.1.0-beta.4' 13 | end 14 | 15 | target 'EloquenceIOS' do 16 | platform :ios, '9.0' 17 | pod "CocoaLumberjack", '~> 2.0' 18 | pod 'CocoaAsyncSocket', '~> 7.4' 19 | pod 'KeychainSwift', '~> 3.0' 20 | pod 'KissXML', :git => 'https://github.com/robbiehanson/KissXML.git', :commit => 'ca4aaeaa38bedaf0f42b450e7c75170dc3a788da' 21 | pod 'XMPPFramework', :git => 'https://github.com/robbiehanson/XMPPFramework.git', :commit => 'c5ab86ffd9c74b41626910813bc5955eb900c07f' 22 | pod 'ODRefreshControl','~> 1.2' 23 | pod 'Touchpose','~> 1.2.0' 24 | pod 'WYPopoverController','~> 0.3.9' 25 | pod 'SwiftyUserDefaults','~> 2.1.3' 26 | pod 'SwiftySettings','~> 0.0.3' 27 | pod 'JSQMessagesViewController','~> 7.3.4' 28 | pod 'MBProgressHUD', '~> 0.9.2' 29 | pod 'HockeySDK', '~> 4.1.0-beta.2' 30 | end 31 | 32 | workspace 'Eloquence' 33 | xcodeproj 'Eloquence/Eloquence' 34 | use_frameworks! 35 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/Assets.xcassets/MacIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "eloquence_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "eloquence_16_@2.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "eloquence_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "eloquence_32_@2.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "eloquence_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "eloquence_128_@2.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "eloquence_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "eloquence_256_@2.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "eloquence_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "eloquence_512_@2.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Eloquence 2 | 3 | [![Build Status](https://travis-ci.org/farion/eloquence.svg?branch=master)](https://travis-ci.org/farion/eloquence) 4 | 5 | This is an alternative XMPP/Jabber instant messenger for Mac OSX and iOS written in Swift. It was created as an alternative to Adium, which is not actively developed anymore and also is based on libpurple, which does not contain the newest XMPP features. Also it adresses the lack of a good client for iOS ... with mod_push, which is available for ejabberd it makes sense to do that now. 6 | 7 | OSX and iOS version is developed in parallel. 8 | 9 | Thanks to the [XMPPFramework](https://github.com/robbiehanson/XMPPFramework "XMPPFramework") which is the base of Eloquence. 10 | 11 | It is not planned to develop clients for other platforms. 12 | As for Android there is the outstanding [Conversations](https://conversations.im "Conversations") and for Windows and Linux there is the at least standing [Gajim](https://gajim.org/ "Gajim"). 13 | 14 | It is also not planned to add other protocols beneath XMPP. 15 | 16 | So come on. Join and fork us. 17 | 18 | Visit also our website: [www.eloquence-im.org](http://www.eloquence-im.org "Eloquence Website") 19 | 20 | ## Help 21 | 22 | If you like what we are doing, please consider donating: 23 | 24 | [![Donate with PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RBBFDAWHZT7FG) 25 | 26 | ## Follow 27 | 28 | [Follow us on Twitter](https://twitter.com/EloquenceIM) 29 | 30 | [Like us on Facebook](https://facebook.com/eloquenceim) 31 | 32 | ## Milestones 33 | 34 | * 03.03.2016 First timt it is chatting (MacOSX) 35 | 36 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/PXListView/LICENSE-PXListView.md: -------------------------------------------------------------------------------- 1 | ##License 2 | PXListView is licensed under the New BSD License, as detailed below (adapted from OSI http://www.opensource.org/licenses/bsd-license.php): 3 | 4 | 5 | Copyright © 2011, Alex Rozanski. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 9 | 10 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | - Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // This file is part of Eloquence IM. 3 | // 4 | // Eloquence is licensed under the Apache License 2.0. 5 | // See LICENSE file for more information. 6 | // 7 | 8 | import Cocoa 9 | import CocoaLumberjack 10 | import CCNPreferencesWindowController 11 | import HockeySDK 12 | 13 | @NSApplicationMain 14 | class AppDelegate: NSObject, NSApplicationDelegate { 15 | 16 | private let preferences = CCNPreferencesWindowController(); 17 | 18 | func applicationDidFinishLaunching(aNotification: NSNotification) { 19 | // Insert code here to initialize your application 20 | 21 | BITHockeyManager.sharedHockeyManager().configureWithIdentifier("0fd88936cfd143e381f53122a3ace4a7") 22 | // Do some additional configuration if needed here 23 | BITHockeyManager.sharedHockeyManager().startManager() 24 | 25 | let ddLogLevel:DDLogLevel = DDLogLevel.All; 26 | 27 | DDLog.addLogger(DDTTYLogger.sharedInstance(), withLevel: ddLogLevel); 28 | 29 | //TODO move to subclass? 30 | preferences.centerToolbarItems = true; 31 | preferences.setPreferencesViewControllers([ 32 | PreferencesGeneralViewController(window: preferences.window!)!, 33 | PreferencesAccountViewController(window: preferences.window!)! 34 | ]) 35 | 36 | EloConnections.sharedInstance.connectAllAccounts(); 37 | 38 | } 39 | 40 | func applicationWillTerminate(aNotification: NSNotification) { 41 | // Insert code here to tear down your application 42 | // TODO disconnect 43 | } 44 | 45 | @IBAction func reloadRosterMenuItemClicked(sender: AnyObject) { 46 | 47 | } 48 | 49 | 50 | @IBAction func preferenceMenuItemClicked(sender: AnyObject) { 51 | preferences.showPreferencesWindow(); 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Eloquence 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Phone 29 | UIMainStoryboardFile~ipad 30 | Pad 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UIStatusBarStyle 36 | UIStatusBarStyleLightContent 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | UIInterfaceOrientationPortraitUpsideDown 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/Cells/EXMessageViewCell.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import JNWCollectionView 3 | import XMPPFramework 4 | 5 | class EXMessageViewCell:JNWCollectionViewCell { 6 | 7 | @IBOutlet var cellView: NSView! 8 | @IBOutlet var cellBottomLabel: NSTextField! 9 | @IBOutlet var avatarImage: NSImageView! 10 | @IBOutlet var messageContentView: NSView! 11 | @IBOutlet var textLabel: NSTextField! 12 | 13 | 14 | override init(frame frameRect: NSRect) { 15 | super.init(frame: frameRect) 16 | commonInit(frame) 17 | } 18 | 19 | required init?(coder: NSCoder) { 20 | super.init(coder: coder) 21 | commonInit(self.frame) 22 | } 23 | 24 | private func commonInit(frame:NSRect){ 25 | 26 | NSBundle.mainBundle().loadNibNamed(getNibName(), owner: self, topLevelObjects: nil) 27 | 28 | 29 | let contentFrame = NSMakeRect(0,0,frame.size.width,frame.size.height) 30 | self.cellView.frame = contentFrame 31 | contentView = cellView 32 | 33 | /* nameLabel = NSTextField() 34 | contentView.addSubview(nameLabel) 35 | nameLabel.stringValue = "LABEL" 36 | nameLabel.sizeToFit()*/ 37 | 38 | } 39 | 40 | /* 41 | func getHeight() -> Int { 42 | return 100; 43 | }*/ 44 | 45 | 46 | func setMessage(message: EloMessage) { 47 | textLabel.stringValue = message.text! 48 | 49 | let formatter = NSDateFormatter() 50 | formatter.dateStyle = .MediumStyle 51 | formatter.timeStyle = .ShortStyle 52 | cellBottomLabel.stringValue = formatter.stringFromDate(message.timestamp) 53 | // nameLabel.stringValue = user.jidStr; 54 | // viaLabel.stringValue = "via " + user.streamBareJidStr; 55 | } 56 | 57 | 58 | 59 | func getNibName() -> String { 60 | fatalError("Subclasses need to implement the `getNibName()` method.") 61 | } 62 | } -------------------------------------------------------------------------------- /Eloquence/Core/EloAccount.swift: -------------------------------------------------------------------------------- 1 | import CoreData 2 | import Foundation 3 | import KeychainSwift 4 | 5 | 6 | class EloAccount: NSManagedObject { 7 | 8 | @NSManaged var jid:NSString 9 | @NSManaged var resource: NSString? 10 | @NSManaged var port: NSNumber? 11 | @NSManaged var server: NSString? 12 | @NSManaged var priority: NSNumber 13 | @NSManaged var autoconnect: NSNumber 14 | 15 | let keychainPasswordPrefix = "Eloquence password for " 16 | 17 | func isAutoConnect() -> Bool { 18 | return autoconnect.boolValue 19 | } 20 | 21 | func setAutoConnect(autoconnect:Bool){ 22 | if(autoconnect){ 23 | self.autoconnect = 1 24 | } else { 25 | self.autoconnect = 0 26 | } 27 | DataController.sharedInstance.save() 28 | } 29 | 30 | 31 | func getJid() -> EloAccountJid { 32 | return EloAccountJid(jid as String) 33 | } 34 | 35 | func getResource() -> String? { 36 | return resource as? String 37 | } 38 | 39 | 40 | func getServer() -> String? { 41 | return server as? String 42 | } 43 | 44 | func getPort() -> Int? { 45 | return port as? Int 46 | } 47 | 48 | func getPriority() -> Int { 49 | return priority as Int 50 | } 51 | 52 | func getPassword() -> String { 53 | 54 | let jid = getJid() 55 | 56 | let keychain = KeychainSwift() 57 | let password = keychain.get(keychainPasswordPrefix + jid.jid) 58 | 59 | if(password != nil){ 60 | return password! 61 | } 62 | return "" 63 | 64 | } 65 | 66 | func setPassword(password: String) { 67 | 68 | let jid = getJid(); 69 | 70 | let keychain = KeychainSwift(); 71 | keychain.set(password, forKey: keychainPasswordPrefix + jid.jid); 72 | 73 | //directly get again, to trigger permission dialog 74 | keychain.get(keychainPasswordPrefix + jid.jid); 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloContactList_Item_CoreDataObject.m: -------------------------------------------------------------------------------- 1 | #import "EloContactList_Item_CoreDataObject.h" 2 | 3 | @interface EloContactList_Item_CoreDataObject () 4 | 5 | @property (nonatomic, strong) XMPPJID * primitiveBareJid; 6 | @property (nonatomic, strong) NSString * primitiveBareJidStr; 7 | 8 | @end 9 | 10 | 11 | @implementation EloContactList_Item_CoreDataObject 12 | 13 | 14 | @dynamic bareJid, primitiveBareJid; 15 | @dynamic bareJidStr, primitiveBareJidStr; 16 | @dynamic streamBareJidStr; 17 | 18 | #pragma mark Transient bareJid 19 | 20 | - (XMPPJID *)bareJid 21 | { 22 | // Create and cache on demand 23 | 24 | [self willAccessValueForKey:@"bareJid"]; 25 | XMPPJID *tmp = self.primitiveBareJid; 26 | [self didAccessValueForKey:@"bareJid"]; 27 | 28 | if (tmp == nil) 29 | { 30 | NSString *bareJidStr = self.bareJidStr; 31 | if (bareJidStr) 32 | { 33 | tmp = [XMPPJID jidWithString:bareJidStr]; 34 | self.primitiveBareJid = tmp; 35 | } 36 | } 37 | 38 | return tmp; 39 | } 40 | 41 | - (void)setBareJid:(XMPPJID *)bareJid 42 | { 43 | if ([self.bareJid isEqualToJID:bareJid options:XMPPJIDCompareBare]) 44 | { 45 | return; // No change 46 | } 47 | 48 | [self willChangeValueForKey:@"bareJid"]; 49 | [self willChangeValueForKey:@"bareJidStr"]; 50 | 51 | self.primitiveBareJid = [bareJid bareJID]; 52 | self.primitiveBareJidStr = [bareJid bare]; 53 | 54 | [self didChangeValueForKey:@"bareJid"]; 55 | [self didChangeValueForKey:@"bareJidStr"]; 56 | } 57 | 58 | - (void)setBareJidStr:(NSString *)bareJidStr 59 | { 60 | if ([self.bareJidStr isEqualToString:bareJidStr]) 61 | { 62 | return; // No change 63 | } 64 | 65 | [self willChangeValueForKey:@"bareJid"]; 66 | [self willChangeValueForKey:@"bareJidStr"]; 67 | 68 | XMPPJID *bareJid = [[XMPPJID jidWithString:bareJidStr] bareJID]; 69 | 70 | self.primitiveBareJid = bareJid; 71 | self.primitiveBareJidStr = [bareJid bare]; 72 | 73 | [self didChangeValueForKey:@"bareJid"]; 74 | [self didChangeValueForKey:@"bareJidStr"]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagement_Message_CoreDataObject.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | 6 | 7 | @interface EloXMPPMessageArchiveManagement_Message_CoreDataObject : NSManagedObject 8 | 9 | @property (nonatomic, strong) XMPPMessage * message; // Transient (proper type, not on disk) 10 | @property (nonatomic, strong) NSString * messageStr; // Shadow (binary data, written to disk) 11 | 12 | /** 13 | * This is the bare jid of the person you're having the conversation with. 14 | * For example: robbiehanson@deusty.com 15 | * 16 | * Regardless of whether the message was incoming or outgoing, 17 | * this will represent the "other" participant in the conversation. 18 | **/ 19 | @property (nonatomic, strong) XMPPJID * bareJid; // Transient (proper type, not on disk) 20 | @property (nonatomic, strong) NSString * bareJidStr; // Shadow (binary data, written to disk) 21 | 22 | @property (nonatomic, strong) NSString * messageId; // The id given by MOD_MAM 23 | 24 | @property (nonatomic, strong) NSString * body; 25 | @property (nonatomic, strong) NSString * thread; 26 | 27 | @property (nonatomic, strong) NSNumber * outgoing; // Use isOutgoing 28 | @property (nonatomic, assign) BOOL isOutgoing; // Convenience property 29 | 30 | @property (nonatomic, strong) NSNumber * composing; // Use isComposing 31 | @property (nonatomic, assign) BOOL isComposing; // Convenience property 32 | 33 | @property (nonatomic, strong) NSDate * timestamp; 34 | 35 | @property (nonatomic, strong) NSString * streamBareJidStr; 36 | 37 | /** 38 | * This method is called immediately before the object is inserted into the managedObjectContext. 39 | * At this point, all normal properties have been set. 40 | * 41 | * If you extend EloXMPPMessageArchiveManagement_Message_CoreDataObject, 42 | * you can use this method as a hook to set your custom properties. 43 | **/ 44 | - (void)willInsertObject; 45 | 46 | /** 47 | * This method is called immediately after the message has been changed. 48 | * At this point, all normal properties have been updated. 49 | * 50 | * If you extend EloXMPPMessageArchiveManagement_Message_CoreDataObject, 51 | * you can use this method as a hook to set your custom properties. 52 | **/ 53 | - (void)didUpdateObject; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/MainSplitViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class MainSplitViewController:NSSplitViewController, NSPageControllerDelegate, RosterViewControllerDelegate { 4 | 5 | @IBOutlet var sidebarItem: NSSplitViewItem! 6 | @IBOutlet var mainItem: NSSplitViewItem! 7 | 8 | var messageController = [EloChatId:MessageViewController]() 9 | var rosterViewController:RosterViewController? 10 | var messagePageController:NSPageController? 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | /* 16 | rosterViewController = RosterViewController(nibName:"RosterViewController",bundle:nil) 17 | rosterViewController!.delegate = self 18 | rosterItem = NSSplitViewItem(viewController: rosterViewController!) 19 | insertSplitViewItem(rosterItem!, atIndex: 0) 20 | 21 | messagePageController = NSPageController( 22 | messageItem = NSSplitViewItem(viewController:messagePageController!) 23 | insertSplitViewItem(messageItem!, atIndex: 1)*/ 24 | rosterViewController = sidebarItem!.viewController as? RosterViewController 25 | rosterViewController!.delegate = self 26 | 27 | messagePageController = mainItem!.viewController as? NSPageController 28 | messagePageController!.delegate = self 29 | 30 | } 31 | 32 | private func getMessageController(chatId: EloChatId) -> MessageViewController{ 33 | if(messageController[chatId] == nil){ 34 | 35 | messageController[chatId] = MessageViewController(nibName:"MessageViewController", chatId:chatId) 36 | } 37 | return messageController[chatId]! 38 | } 39 | 40 | func pageController(pageController: NSPageController, identifierForObject object: AnyObject) -> String { 41 | 42 | let chatId = object as! EloChatId 43 | return chatId.toString 44 | 45 | } 46 | 47 | func pageController(pageController: NSPageController, viewControllerForIdentifier identifier: String) -> NSViewController { 48 | return getMessageController(EloChatId(identifier:identifier)) 49 | } 50 | 51 | func didClickContact(chatId: EloChatId){ 52 | NSLog("click" + chatId.to.jid ); 53 | 54 | //TODO if I do not call it twice, the first clik nothing happens. 55 | messagePageController!.navigateForwardToObject(chatId) 56 | messagePageController!.navigateForwardToObject(chatId) 57 | } 58 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagement.xcdatamodeld/EloXMPPMessageArchiveManagement.xcdatamodel/contents: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagement_Contact_CoreDataObject.m: -------------------------------------------------------------------------------- 1 | #import "EloXMPPMessageArchiveManagement_Contact_CoreDataObject.h" 2 | 3 | 4 | @interface EloXMPPMessageArchiveManagement_Contact_CoreDataObject () 5 | 6 | @property (nonatomic, strong) XMPPJID * primitiveBareJid; 7 | @property (nonatomic, strong) NSString * primitiveBareJidStr; 8 | 9 | @end 10 | 11 | 12 | @implementation EloXMPPMessageArchiveManagement_Contact_CoreDataObject 13 | 14 | @dynamic bareJid, primitiveBareJid; 15 | @dynamic bareJidStr, primitiveBareJidStr; 16 | @dynamic mostRecentMessageTimestamp; 17 | @dynamic mostRecentMessageBody; 18 | @dynamic mostRecentMessageOutgoing; 19 | @dynamic streamBareJidStr; 20 | 21 | #pragma mark Transient bareJid 22 | 23 | - (XMPPJID *)bareJid 24 | { 25 | // Create and cache on demand 26 | 27 | [self willAccessValueForKey:@"bareJid"]; 28 | XMPPJID *tmp = self.primitiveBareJid; 29 | [self didAccessValueForKey:@"bareJid"]; 30 | 31 | if (tmp == nil) 32 | { 33 | NSString *bareJidStr = self.bareJidStr; 34 | if (bareJidStr) 35 | { 36 | tmp = [XMPPJID jidWithString:bareJidStr]; 37 | self.primitiveBareJid = tmp; 38 | } 39 | } 40 | 41 | return tmp; 42 | } 43 | 44 | - (void)setBareJid:(XMPPJID *)bareJid 45 | { 46 | if ([self.bareJid isEqualToJID:bareJid options:XMPPJIDCompareBare]) 47 | { 48 | return; // No change 49 | } 50 | 51 | [self willChangeValueForKey:@"bareJid"]; 52 | [self willChangeValueForKey:@"bareJidStr"]; 53 | 54 | self.primitiveBareJid = [bareJid bareJID]; 55 | self.primitiveBareJidStr = [bareJid bare]; 56 | 57 | [self didChangeValueForKey:@"bareJid"]; 58 | [self didChangeValueForKey:@"bareJidStr"]; 59 | } 60 | 61 | - (void)setBareJidStr:(NSString *)bareJidStr 62 | { 63 | if ([self.bareJidStr isEqualToString:bareJidStr]) 64 | { 65 | return; // No change 66 | } 67 | 68 | [self willChangeValueForKey:@"bareJid"]; 69 | [self willChangeValueForKey:@"bareJidStr"]; 70 | 71 | XMPPJID *bareJid = [[XMPPJID jidWithString:bareJidStr] bareJID]; 72 | 73 | self.primitiveBareJid = bareJid; 74 | self.primitiveBareJidStr = [bareJid bare]; 75 | 76 | [self didChangeValueForKey:@"bareJid"]; 77 | [self didChangeValueForKey:@"bareJidStr"]; 78 | } 79 | 80 | #pragma mark Hooks 81 | 82 | - (void)willInsertObject 83 | { 84 | // If you extend EloXMPPMessageArchiveManagement_Contact_CoreDataObject, 85 | // you can override this method to use as a hook to set your own custom properties. 86 | } 87 | 88 | - (void)didUpdateObject 89 | { 90 | // If you extend EloXMPPMessageArchiveManagement_Contact_CoreDataObject, 91 | // you can override this method to use as a hook to update your own custom properties. 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import CocoaLumberjack 3 | import HockeySDK 4 | 5 | @UIApplicationMain 6 | class AppDelegate: UIResponder, UIApplicationDelegate { 7 | 8 | var window: UIWindow? 9 | 10 | 11 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 12 | // Override point for customization after application launch. 13 | 14 | BITHockeyManager.sharedHockeyManager().configureWithIdentifier("20ded3dc57fd4eb19592045b5abfd6b8") 15 | BITHockeyManager.sharedHockeyManager().startManager() 16 | BITHockeyManager.sharedHockeyManager().authenticator.authenticateInstallation() // This line is obsolete in the crash only builds 17 | 18 | 19 | let ddLogLevel:DDLogLevel = DDLogLevel.All; 20 | 21 | DDLog.addLogger(DDTTYLogger.sharedInstance(), withLevel: ddLogLevel); 22 | 23 | EloConnections.sharedInstance.connectAllAccounts(); 24 | 25 | return true 26 | } 27 | 28 | func applicationWillResignActive(application: UIApplication) { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 31 | } 32 | 33 | func applicationDidEnterBackground(application: UIApplication) { 34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | func applicationWillEnterForeground(application: UIApplication) { 39 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 40 | } 41 | 42 | func applicationDidBecomeActive(application: UIApplication) { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | func applicationWillTerminate(application: UIApplication) { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/RosterViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XMPPFramework 3 | import JNWCollectionView 4 | 5 | protocol RosterViewControllerDelegate { 6 | func didClickContact(chatId:EloChatId); 7 | } 8 | 9 | class RosterViewController: NSViewController , EloRosterDelegate, JNWCollectionViewDataSource, JNWCollectionViewDelegate, JNWCollectionViewListLayoutDelegate { 10 | 11 | @IBOutlet var rosterScrollView: JNWCollectionView! 12 | 13 | var roster = EloRoster() 14 | var delegate:RosterViewControllerDelegate? 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | // rosterTable.setDelegate(self) 20 | // rosterTable.setDataSource(self) 21 | // rosterScrollView!.delegate 22 | 23 | roster.delegate = self 24 | roster.initializeData() 25 | 26 | 27 | let listLayout = JNWCollectionViewListLayout(collectionView: rosterScrollView) 28 | listLayout.rowHeight = 60 29 | listLayout.delegate = self 30 | 31 | rosterScrollView .collectionViewLayout = listLayout 32 | 33 | rosterScrollView.registerClass(EXRosterContactCell.self, forCellWithReuseIdentifier: "contactCell") 34 | 35 | rosterScrollView.delegate = self 36 | rosterScrollView.dataSource = self 37 | rosterScrollView.reloadData() 38 | } 39 | 40 | //Mark: EloRosterDelegate 41 | func rosterWillChangeContent() { 42 | 43 | } 44 | 45 | func roster(didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: EloFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 46 | 47 | } 48 | func rosterDidChangeContent() { 49 | rosterScrollView.reloadData() 50 | 51 | } 52 | 53 | //Mark: JNWCollectionViewDelegate 54 | func collectionView(collectionView: JNWCollectionView!, didSelectItemAtIndexPath indexPath: NSIndexPath!) { 55 | if(indexPath.item >= 0 && delegate != nil){ 56 | delegate!.didClickContact(roster.getChatId(indexPath.item)) 57 | // roster.chatWishedByUserAction(indexPath.item); 58 | } 59 | } 60 | 61 | //Mark: JNWCollectionViewDataSource 62 | 63 | func collectionView(collectionView: JNWCollectionView!, numberOfItemsInSection section: Int) -> UInt { 64 | return UInt(roster.numberOfRows()); 65 | } 66 | 67 | /// Asks the data source for the view that should be used for the cell at the specified index path. The returned 68 | func collectionView(collectionView: JNWCollectionView!, cellForItemAtIndexPath indexPath: NSIndexPath!) -> JNWCollectionViewCell! { 69 | let cell = rosterScrollView.dequeueReusableCellWithIdentifier("contactCell") as! EXRosterContactCell; 70 | 71 | cell.setItem(roster.getContactListItem(indexPath.item)); 72 | 73 | return cell; 74 | } 75 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagementCoreDataStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import 4 | 5 | #import "EloXMPPMessageArchiveManagement.h" 6 | #import "EloXMPPMessageArchiveManagement_Message_CoreDataObject.h" 7 | #import "EloXMPPMessageArchiveManagement_Contact_CoreDataObject.h" 8 | 9 | 10 | @interface EloXMPPMessageArchiveManagementCoreDataStorage : XMPPCoreDataStorage 11 | { 12 | /* Inherited protected variables from XMPPCoreDataStorage 13 | 14 | NSString *databaseFileName; 15 | NSUInteger saveThreshold; 16 | 17 | dispatch_queue_t storageQueue; 18 | 19 | */ 20 | } 21 | 22 | /** 23 | * Convenience method to get an instance with the default database name. 24 | * 25 | * IMPORTANT: 26 | * You are NOT required to use the sharedInstance. 27 | * 28 | * If your application uses multiple xmppStreams, and you use a sharedInstance of this class, 29 | * then all of your streams share the same database store. You might get better performance if you create 30 | * multiple instances of this class instead (using different database filenames), as this way you can have 31 | * concurrent writes to multiple databases. 32 | **/ 33 | + (instancetype)sharedInstance; 34 | 35 | 36 | @property (strong) NSString *messageEntityName; 37 | @property (strong) NSString *contactEntityName; 38 | 39 | - (NSEntityDescription *)messageEntity:(NSManagedObjectContext *)moc; 40 | - (NSEntityDescription *)contactEntity:(NSManagedObjectContext *)moc; 41 | 42 | - (EloXMPPMessageArchiveManagement_Contact_CoreDataObject *)contactForMessage:(EloXMPPMessageArchiveManagement_Message_CoreDataObject *)msg; 43 | 44 | - (EloXMPPMessageArchiveManagement_Contact_CoreDataObject *)contactWithJid:(XMPPJID *)contactJid 45 | streamJid:(XMPPJID *)streamJid 46 | managedObjectContext:(NSManagedObjectContext *)moc; 47 | 48 | - (EloXMPPMessageArchiveManagement_Contact_CoreDataObject *)contactWithBareJidStr:(NSString *)contactBareJidStr 49 | streamBareJidStr:(NSString *)streamBareJidStr 50 | managedObjectContext:(NSManagedObjectContext *)moc; 51 | 52 | 53 | /* Inherited from XMPPCoreDataStorage 54 | * Please see the XMPPCoreDataStorage header file for extensive documentation. 55 | 56 | - (id)initWithDatabaseFilename:(NSString *)databaseFileName storeOptions:(NSDictionary *)storeOptions; 57 | - (id)initWithInMemoryStore; 58 | 59 | @property (readonly) NSString *databaseFileName; 60 | 61 | @property (readwrite) NSUInteger saveThreshold; 62 | 63 | @property (readonly) NSManagedObjectModel *managedObjectModel; 64 | @property (readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator; 65 | 66 | @property (readonly) NSManagedObjectContext *mainThreadManagedObjectContext; 67 | 68 | */ 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/RosterViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | import XMPPFramework 4 | 5 | class RosterViewController:UIViewController, UITableViewDelegate, UITableViewDataSource, EloRosterDelegate { 6 | 7 | @IBOutlet weak var tableView: UITableView! 8 | 9 | var roster = EloRoster() 10 | 11 | override func viewDidLoad() { 12 | super.viewDidLoad() 13 | 14 | self.tableView.delegate = self 15 | self.tableView.dataSource = self 16 | 17 | roster.delegate = self 18 | roster.initializeData() 19 | } 20 | 21 | /* private */ 22 | 23 | func configureCell(cell:RosterCell, atIndexPath: NSIndexPath){ 24 | NSLog("IP %d",atIndexPath.row) 25 | let contactListItem = roster.getContactListItem(atIndexPath.row); 26 | cell.name.text = contactListItem.bareJidStr 27 | cell.viaLabel.text = "via " + contactListItem.streamBareJidStr 28 | // cell.imageView!.image = user.photo; 29 | } 30 | 31 | /* UITableViewDataSource */ 32 | 33 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 34 | return roster.numberOfRowsInSection(section); 35 | } 36 | 37 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 38 | 39 | let cell = tableView.dequeueReusableCellWithIdentifier("RosterCell") as! RosterCell 40 | configureCell(cell,atIndexPath:indexPath) 41 | return cell 42 | } 43 | 44 | func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 45 | roster.chatWishedByUserAction(indexPath.row) 46 | } 47 | 48 | /* NSFetchedResultsControllerDelegate */ 49 | 50 | func rosterWillChangeContent() { 51 | NSLog("rosterwillchange") 52 | tableView.beginUpdates() 53 | } 54 | 55 | func roster(didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 56 | 57 | switch(type){ 58 | case .Insert: 59 | tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade) 60 | break 61 | case .Delete: 62 | tableView.deleteRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade) 63 | break 64 | case .Update: 65 | configureCell(tableView.cellForRowAtIndexPath(indexPath!) as! RosterCell ,atIndexPath: indexPath!) 66 | break 67 | case .Move: 68 | tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade) 69 | tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade) 70 | break 71 | } 72 | } 73 | 74 | func rosterDidChangeContent() { 75 | tableView.endUpdates() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/Preferences/PreferencesGeneralViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 33 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/Preferences/PreferencesAccountViewController.swift: -------------------------------------------------------------------------------- 1 | 2 | import Cocoa 3 | import CCNPreferencesWindowController 4 | 5 | class PreferencesAccountViewController: NSViewController, CCNPreferencesWindowControllerProtocol { 6 | 7 | var data = [EloAccount](); 8 | let accountDialog = PreferencesAccountDialogWindowController.init(windowNibName:"PreferencesAccountDialogWindowController"); 9 | 10 | @IBOutlet weak var tableView: NSTableView! 11 | 12 | var preferenceWindow: NSWindow; 13 | 14 | init? (window: NSWindow){ 15 | preferenceWindow = window; 16 | super.init(nibName: nil, bundle: nil); 17 | } 18 | 19 | required init?(coder: NSCoder) { 20 | preferenceWindow = NSWindow(); 21 | super.init(coder: coder); 22 | } 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | tableView.setDelegate(self); 28 | tableView.setDataSource(self); 29 | self.prepareData(); 30 | 31 | } 32 | 33 | func prepareData(){ 34 | 35 | self.data = DataController.sharedInstance.getAccounts(); 36 | tableView.reloadData(); 37 | 38 | } 39 | 40 | func preferenceIdentifier() -> String! { 41 | return "AccountPreferencesIdentifier"; 42 | } 43 | 44 | func preferenceTitle() -> String! { 45 | return "Accounts"; 46 | } 47 | 48 | func preferenceIcon() -> NSImage! { 49 | return NSImage(named: NSImageNameUserAccounts); 50 | } 51 | 52 | @IBAction func addClicked(sender: AnyObject) { 53 | accountDialog.setAccountToEdit(nil); 54 | preferenceWindow.beginSheet(accountDialog.window! , completionHandler: { (returnCode: NSModalResponse) in 55 | if(returnCode == NSModalResponseOK){ 56 | self.prepareData(); 57 | } 58 | }); 59 | } 60 | 61 | @IBAction func removeClicked(sender: AnyObject) { 62 | 63 | for (_,index) in tableView.selectedRowIndexes.enumerate() { 64 | DataController.sharedInstance.deleteAccount(self.data[index]); 65 | } 66 | self.prepareData(); 67 | } 68 | 69 | @IBAction func editClicked(sender: AnyObject) { 70 | for (_,index) in tableView.selectedRowIndexes.enumerate() { 71 | accountDialog.setAccountToEdit(self.data[index]); 72 | preferenceWindow.beginSheet(accountDialog.window! , completionHandler: { (returnCode: NSModalResponse) in 73 | if(returnCode == NSModalResponseOK){ 74 | self.prepareData(); 75 | } 76 | }); 77 | } 78 | } 79 | } 80 | 81 | extension PreferencesAccountViewController : NSTableViewDataSource { 82 | func numberOfRowsInTableView(tableView: NSTableView) -> Int { 83 | return self.data.count; 84 | } 85 | } 86 | 87 | 88 | extension PreferencesAccountViewController : NSTableViewDelegate { 89 | func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? { 90 | 91 | if let cell = tableView.makeViewWithIdentifier("account", owner: nil) as? NSTableCellView { 92 | cell.textField?.stringValue = (self.data[row].getJid().jid as String); 93 | return cell 94 | } 95 | return nil 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Assets.xcassets/iOSIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "29.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "29x2.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "29x3.png", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "40-1.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "40x2.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "57x57", 35 | "idiom" : "iphone", 36 | "filename" : "57.png", 37 | "scale" : "1x" 38 | }, 39 | { 40 | "size" : "57x57", 41 | "idiom" : "iphone", 42 | "filename" : "57x2.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "60x2.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "60x3.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "29x29", 59 | "idiom" : "ipad", 60 | "filename" : "29-1.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "29x2-1.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "40x40", 71 | "idiom" : "ipad", 72 | "filename" : "40.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "40x2-1.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "50x50", 83 | "idiom" : "ipad", 84 | "filename" : "50.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "50x50", 89 | "idiom" : "ipad", 90 | "filename" : "50x2.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "72x72", 95 | "idiom" : "ipad", 96 | "filename" : "72.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "72x72", 101 | "idiom" : "ipad", 102 | "filename" : "72x2.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "76x76", 107 | "idiom" : "ipad", 108 | "filename" : "76.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "76x76", 113 | "idiom" : "ipad", 114 | "filename" : "76x2.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "83.5x83.5", 119 | "idiom" : "ipad", 120 | "filename" : "83.5x2.png", 121 | "scale" : "2x" 122 | }, 123 | { 124 | "size" : "60x60", 125 | "idiom" : "car", 126 | "filename" : "60x2-1.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "60x60", 131 | "idiom" : "car", 132 | "filename" : "60x3-1.png", 133 | "scale" : "3x" 134 | } 135 | ], 136 | "info" : { 137 | "version" : 1, 138 | "author" : "xcode" 139 | } 140 | } -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/EMAccountsViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | import XMPPFramework 4 | 5 | protocol EMAccountsViewControllerDelegate:NSObjectProtocol { 6 | func didClickDoneInAccountsViewController(); 7 | } 8 | 9 | class EMAccountsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, EMAddAccountViewControllerDelegate { 10 | 11 | var delegate:EMAccountsViewControllerDelegate?; 12 | 13 | var addController: EMAddAccountViewController?; 14 | 15 | var data = [EloAccount](); 16 | 17 | @IBOutlet weak var tableView: UITableView! 18 | 19 | override func viewDidLoad() { 20 | 21 | tableView.delegate = self; 22 | tableView.dataSource = self; 23 | 24 | self.prepareData(); 25 | 26 | } 27 | 28 | 29 | override func preferredStatusBarStyle() -> UIStatusBarStyle { 30 | return UIStatusBarStyle.LightContent; 31 | } 32 | 33 | func prepareData(){ 34 | self.data = DataController.sharedInstance.getAccounts(); 35 | tableView.reloadData(); 36 | } 37 | 38 | 39 | @IBAction func doneClicked(sender: AnyObject) { 40 | if(delegate != nil){ 41 | delegate!.didClickDoneInAccountsViewController() 42 | } 43 | } 44 | 45 | @IBAction func addClicked(sender: AnyObject) { 46 | 47 | addController = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("AddAccount") as? EMAddAccountViewController; 48 | addController!.delegate = self; 49 | self.presentViewController(addController!, animated: true, completion: {}) 50 | } 51 | 52 | func doCloseAddAccountView(){ 53 | if(addController != nil){ 54 | addController!.dismissViewControllerAnimated(true, completion: nil) 55 | addController!.delegate = nil; 56 | addController = nil; 57 | } 58 | } 59 | 60 | 61 | 62 | //pragma mark - UITableViewDataSource 63 | 64 | 65 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 66 | var cell = tableView.dequeueReusableCellWithIdentifier("AccountCell", forIndexPath: indexPath) as? EMAccountCell; 67 | if(cell == nil) { 68 | cell = EMAccountCell(style: UITableViewCellStyle.Default, reuseIdentifier: "AccountCell"); 69 | } 70 | cell!.jidLabel.text = data[indexPath.row].getJid().jid; 71 | cell!.disableSwitch.on = data[indexPath.row].isAutoConnect() 72 | cell!.account = data[indexPath.row]; 73 | return cell!; 74 | } 75 | 76 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 77 | return data.count 78 | } 79 | 80 | 81 | //pragma mark - UITableViewDelegate 82 | 83 | func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 84 | addController = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("AddAccount") as? EMAddAccountViewController; 85 | addController!.delegate = self; 86 | addController!.account = data[indexPath.row]; 87 | self.presentViewController(addController!, animated: true, completion: {}) 88 | 89 | } 90 | 91 | 92 | } -------------------------------------------------------------------------------- /Eloquence/Eloquence/Preferences/PreferencesAccountDialogWindowController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class PreferencesAccountDialogWindowController: NSWindowController { 4 | 5 | @IBOutlet weak var jidText: NSTextField! 6 | @IBOutlet weak var passwordText: NSTextField! 7 | @IBOutlet weak var priorityText: NSTextField! 8 | @IBOutlet weak var resourceText: NSTextField! 9 | @IBOutlet weak var serverText: NSTextField! 10 | @IBOutlet weak var portText: NSTextField! 11 | @IBOutlet weak var autoConnect: NSButton! 12 | 13 | var account:EloAccount?; 14 | 15 | override func windowDidLoad() { 16 | super.windowDidLoad() 17 | fill(); 18 | } 19 | 20 | func setAccountToEdit(account:EloAccount?){ 21 | self.account = account; 22 | fill(); 23 | } 24 | 25 | 26 | private func fill(){ 27 | 28 | if(jidText == nil){ 29 | return; 30 | } 31 | 32 | if(account == nil){ 33 | jidText.stringValue = ""; 34 | passwordText.stringValue = ""; 35 | priorityText.stringValue = ""; 36 | resourceText.stringValue = ""; 37 | serverText.stringValue = ""; 38 | portText.stringValue = ""; 39 | autoConnect.state = 0; 40 | }else{ 41 | let safeAccount = account!; 42 | jidText.stringValue = safeAccount.getJid().jid 43 | passwordText.stringValue = safeAccount.getPassword(); 44 | 45 | let priority = safeAccount.getPriority(); 46 | 47 | 48 | if(priority > 0){ 49 | priorityText.stringValue = String(priority); 50 | }else{ 51 | priorityText.stringValue = ""; 52 | } 53 | 54 | resourceText.stringValue = safeAccount.getResource()!; 55 | serverText.stringValue = safeAccount.getServer()!; 56 | 57 | let port = safeAccount.getPort(); 58 | if(port != nil && port > 0){ 59 | portText.stringValue = String(port!) 60 | }else{ 61 | portText.stringValue = ""; 62 | } 63 | 64 | if(safeAccount.isAutoConnect()){ 65 | autoConnect.state = 1; 66 | }else{ 67 | autoConnect.state = 0; 68 | } 69 | } 70 | 71 | } 72 | 73 | 74 | @IBAction func clickOk(sender: AnyObject) { 75 | 76 | if(account == nil){ 77 | account = DataController.sharedInstance.insertAccount(); 78 | } 79 | 80 | let safeAccount = account!; 81 | safeAccount.jid = jidText.stringValue; 82 | safeAccount.priority = priorityText.integerValue; 83 | safeAccount.resource = resourceText.stringValue; 84 | safeAccount.server = serverText.stringValue; 85 | safeAccount.port = portText.integerValue; 86 | safeAccount.autoconnect = autoConnect.state; 87 | safeAccount.setPassword(passwordText.stringValue); 88 | DataController.sharedInstance.save(); 89 | 90 | self.window?.sheetParent?.endSheet(self.window!, returnCode:NSModalResponseOK); 91 | } 92 | 93 | @IBAction func clickCancel(sender: AnyObject) { 94 | self.window?.sheetParent?.endSheet(self.window!, returnCode:NSModalResponseCancel); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/Core/DataController.swift: -------------------------------------------------------------------------------- 1 | import CoreData 2 | 3 | class DataController: NSObject { 4 | 5 | static let sharedInstance = DataController() 6 | 7 | var managedObjectContext: NSManagedObjectContext; 8 | 9 | override init() { 10 | // This resource is the same name as your xcdatamodeld contained in your project. 11 | guard let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension:"momd") else { 12 | fatalError("Error loading model from bundle") 13 | } 14 | // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model. 15 | guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else { 16 | fatalError("Error initializing mom from: \(modelURL)") 17 | } 18 | let psc = NSPersistentStoreCoordinator(managedObjectModel: mom) 19 | self.managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) 20 | self.managedObjectContext.persistentStoreCoordinator = psc 21 | dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { 22 | let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) 23 | let docURL = urls[urls.endIndex-1] 24 | /* The directory the application uses to store the Core Data store file. 25 | This code uses a file named "DataModel.sqlite" in the application's documents directory. 26 | */ 27 | let storeURL = docURL.URLByAppendingPathComponent("DataModel.sqlite") 28 | do { 29 | try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil) 30 | } catch { 31 | fatalError("Error migrating store: \(error)") 32 | } 33 | } 34 | } 35 | 36 | func getAccounts() -> [EloAccount] { 37 | let accountsFetch = NSFetchRequest(entityName: "Account"); 38 | do { 39 | let fetchedAccounts = try managedObjectContext.executeFetchRequest(accountsFetch) as! [EloAccount]; 40 | NSLog("count: %d",fetchedAccounts.count); 41 | return fetchedAccounts; 42 | } catch { 43 | fatalError("Failed to fetch accounts: \(error)"); 44 | } 45 | } 46 | 47 | func insertAccount() -> EloAccount { 48 | return NSEntityDescription.insertNewObjectForEntityForName("Account", inManagedObjectContext: self.managedObjectContext) as! EloAccount; 49 | } 50 | 51 | func save() { 52 | do { 53 | try self.managedObjectContext.save() 54 | } catch { 55 | fatalError("Failure to save context: \(error)") 56 | } 57 | } 58 | 59 | func deleteAccount(account: EloAccount?) { 60 | if(account != nil){ 61 | self.managedObjectContext.deleteObject(account!); 62 | } 63 | } 64 | 65 | func getAccount(accountId:NSNumber?) -> EloAccount?{ 66 | 67 | if(accountId == nil){ 68 | return nil; 69 | } 70 | 71 | let accountsFetch = NSFetchRequest(entityName: "Account"); 72 | accountsFetch.predicate = NSPredicate(format: "objectID == %d", accountId!); 73 | 74 | do { 75 | let fetchedAccounts = try managedObjectContext.executeFetchRequest(accountsFetch) as! [EloAccount]; 76 | if(fetchedAccounts.isEmpty){ 77 | return nil; 78 | } 79 | return fetchedAccounts[0]; 80 | } catch { 81 | fatalError("Failed to fetch accounts: \(error)"); 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /Eloquence/Eloquence.xcodeproj/xcshareddata/xcschemes/EloquenceIOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Eloquence/Core/EloCapabilities.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XMPPFramework 3 | 4 | class EloCapabilities { 5 | 6 | struct EloCapability { 7 | let xep:String 8 | let name:String 9 | let supportedByServer:Bool 10 | } 11 | 12 | func getCapabilities(jid: EloAccountJid) -> [EloCapability] { 13 | 14 | let xmppStream = EloConnections.sharedInstance.getConnection(jid).getXMPPStream(); 15 | 16 | if(!xmppStream.isConnected()){ 17 | return [] 18 | } 19 | 20 | let query = XMPPCapabilitiesCoreDataStorage.sharedInstance().capabilitiesForJID(XMPPJID.jidWithUser(nil, domain: xmppStream.myJID.domain, resource: nil) , xmppStream: xmppStream) 21 | 22 | var serverCapabilities = [String]() 23 | 24 | for feature in query.elementsForName("feature") { 25 | let capability = feature.attributeStringValueForName("var") 26 | serverCapabilities.append(capability) 27 | NSLog("%@",capability) 28 | } 29 | 30 | return [ 31 | 32 | EloCapability( 33 | xep: "0191", 34 | name: "Blocking Command 1.2+", 35 | supportedByServer: (["urn:xmpp:blocking"].filter { serverCapabilities.contains($0) }.count > 0) 36 | ), 37 | EloCapability( 38 | xep: "0198", 39 | name: "Stream Management 1.3+", 40 | supportedByServer: (["urn:xmpp:sm:3"].filter { serverCapabilities.contains($0) }.count > 0) 41 | ), 42 | EloCapability( 43 | xep: "0199", 44 | name: "XMPP Ping 2.0+", 45 | supportedByServer: (["urn:xmpp:ping"].filter { serverCapabilities.contains($0) }.count > 0) 46 | ), 47 | EloCapability( 48 | xep: "0203", 49 | name: "Delayed Delivery 2.0+", 50 | supportedByServer: (["urn:xmpp:delay"].filter { serverCapabilities.contains($0) }.count > 0) 51 | ), 52 | EloCapability( 53 | xep: "0237", 54 | name: "Roster Versioning 0.5+", 55 | supportedByServer: (["urn:xmpp:features:rosterver"].filter { serverCapabilities.contains($0) }.count > 0) 56 | ), 57 | EloCapability( 58 | xep: "0280", 59 | name: "Message Carbons 0.7+", 60 | supportedByServer: (["urn:xmpp:carbons:2"].filter { serverCapabilities.contains($0) }.count > 0) 61 | ), 62 | EloCapability( 63 | xep: "0313", 64 | name: "Message Archive Management 0.3+", 65 | supportedByServer: (["urn:xmpp:mam:0","urn:xmpp:mam:1"].filter { serverCapabilities.contains($0) }.count > 0) 66 | ), 67 | EloCapability( 68 | xep: "0333", 69 | name: "Chat Markers 0.2.1", 70 | supportedByServer: (["urn:xmpp:chat-markers:0"].filter { serverCapabilities.contains($0) }.count > 0) 71 | ), 72 | EloCapability( 73 | xep: "0352", 74 | name: "Client State Indication 0.2+", 75 | supportedByServer: (["urn:xmpp:csi:0"].filter { serverCapabilities.contains($0) }.count > 0) 76 | ), 77 | EloCapability( 78 | xep: "0357", 79 | name: "Push Notifications 0.1+", 80 | supportedByServer: (["urn:xmpp:push:0"].filter { serverCapabilities.contains($0) }.count > 0) 81 | ), 82 | EloCapability( 83 | xep: "0363", 84 | name: "HTTP File Upload 0.2+", 85 | supportedByServer: (["urn:xmpp:http:upload"].filter { serverCapabilities.contains($0) }.count > 0) 86 | ) 87 | ] 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagement.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | #import "XMPPMessage+Elo_XEP_0313.h" 7 | 8 | #define _XMPP_MESSAGE_ARCHIVE_MANAGEMENT_H 9 | 10 | @class XMPPIDTracker; 11 | 12 | @protocol EloXMPPMessageArchiveManagementStorage; 13 | 14 | /** 15 | * This class provides support for storing message history. 16 | * It implements XEP-0313 and a client side history storage. 17 | * The code is heavly based (was a copy initially) on the XEP-0136 implementation and is still using classes from there. 18 | * Alltough the XEP-0136 related code was removed. 19 | */ 20 | 21 | @interface EloXMPPMessageArchiveManagement : XMPPModule { 22 | 23 | @protected 24 | 25 | __strong id EloXMPPMessageArchiveManagementStorage; 26 | XMPPIDTracker *responseTracker; 27 | 28 | } 29 | 30 | - (void)mamQueryWith: (XMPPJID*) jid andStart: (NSDate*) start andEnd: (NSDate*) end andResultSet: (XMPPResultSet*) resultSet; 31 | - (id)initWithMessageArchiveManagementStorage:(id )storage; 32 | - (id)initWithMessageArchiveManagementStorage:(id )storage dispatchQueue:(dispatch_queue_t)queue; 33 | 34 | @property (readonly, strong) id EloXMPPMessageArchiveManagementStorage; 35 | 36 | /** 37 | * 38 | **/ 39 | @property (readwrite, copy) NSXMLElement *preferences; 40 | 41 | @end 42 | 43 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 44 | #pragma mark - 45 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 46 | 47 | @protocol EloXMPPMessageArchiveManagementStorage 48 | @required 49 | 50 | // 51 | // 52 | // -- PUBLIC METHODS -- 53 | // 54 | // There are no public methods required by this protocol. 55 | // 56 | // Each individual roster storage class will provide a proper way to access/enumerate the 57 | // users/resources according to the underlying storage mechanism. 58 | // 59 | 60 | 61 | // 62 | // 63 | // -- PRIVATE METHODS -- 64 | // 65 | // These methods are designed to be used ONLY by the EloXMPPMessageArchiveManagement class. 66 | // 67 | // 68 | 69 | /** 70 | * Configures the storage class, passing its parent and parent's dispatch queue. 71 | * 72 | * This method is called by the init method of the EloXMPPMessageArchiveManagement class. 73 | * This method is designed to inform the storage class of its parent 74 | * and of the dispatch queue the parent will be operating on. 75 | * 76 | * The storage class may choose to operate on the same queue as its parent, 77 | * or it may operate on its own internal dispatch queue. 78 | * 79 | * This method should return YES if it was configured properly. 80 | * If a storage class is designed to be used with a single parent at a time, this method may return NO. 81 | * The EloXMPPMessageArchiveManagement class is configured to ignore the passed 82 | * storage class in its init method if this method returns NO. 83 | **/ 84 | - (BOOL)configureWithParent:(EloXMPPMessageArchiveManagement *)aParent queue:(dispatch_queue_t)queue; 85 | 86 | /** 87 | * 88 | **/ 89 | - (void)archiveMessage:(XMPPMessage *)message outgoing:(BOOL)isOutgoing xmppStream:(XMPPStream *)stream; 90 | 91 | @optional 92 | 93 | /** 94 | * The storage class may optionally persistently store the client preferences. 95 | **/ 96 | - (void)setPreferences:(NSXMLElement *)prefs forUser:(XMPPJID *)bareUserJid; 97 | 98 | /** 99 | * The storage class may optionally persistently store the client preferences. 100 | * This method is then used to fetch previously known preferences when the client first connects to the xmpp server. 101 | **/ 102 | - (NSXMLElement *)preferencesForUser:(XMPPJID *)bareUserJid; 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Eloquence/Core/EloXMPPRoster.xcdatamodeld/EloXMPPRoster.xcdatamodel/contents: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/Core/EloXMPPRosterCoreDataStorage.m: -------------------------------------------------------------------------------- 1 | #import "EloXMPPRosterCoreDataStorage.h" 2 | #import 3 | #import 4 | #import 5 | 6 | #import "EloContactListCoreDataStorage.h" 7 | 8 | #if ! __has_feature(objc_arc) 9 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 10 | #endif 11 | 12 | // Log levels: off, error, warn, info, verbose 13 | #if DEBUG 14 | static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE; 15 | #else 16 | static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN; 17 | #endif 18 | 19 | #define AssertPrivateQueue() \ 20 | NSAssert(dispatch_get_specific(storageQueueTag), @"Private method: MUST run on storageQueue"); 21 | 22 | 23 | @implementation EloXMPPRosterCoreDataStorage 24 | 25 | static EloXMPPRosterCoreDataStorage *sharedInstance; 26 | 27 | + (instancetype)sharedInstance 28 | { 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | 32 | sharedInstance = [[EloXMPPRosterCoreDataStorage alloc] initWithDatabaseFilename:nil storeOptions:nil]; 33 | }); 34 | 35 | return sharedInstance; 36 | } 37 | 38 | - (BOOL)configureWithParent:(XMPPRoster *)aParent queue:(dispatch_queue_t)queue 39 | { 40 | NSParameterAssert(aParent != nil); 41 | NSParameterAssert(queue != NULL); 42 | 43 | @synchronized(self) 44 | { 45 | if ((parent == nil) && (parentQueue == NULL)) 46 | { 47 | parent = aParent; 48 | parentQueue = queue; 49 | parentQueueTag = &parentQueueTag; 50 | dispatch_queue_set_specific(parentQueue, parentQueueTag, parentQueueTag, NULL); 51 | 52 | #if !OS_OBJECT_USE_OBJC 53 | dispatch_retain(parentQueue); 54 | #endif 55 | 56 | return YES; 57 | } 58 | } 59 | 60 | return YES; 61 | } 62 | 63 | //TODO also handle presence 64 | 65 | - (void)handleRosterItem:(NSXMLElement *)itemSubElement xmppStream:(XMPPStream *)stream 66 | { 67 | XMPPLogTrace(); 68 | 69 | NSLog(@"JOOOOO"); 70 | 71 | // Remember XML heirarchy memory management rules. 72 | // The passed parameter is a subnode of the IQ, and we need to pass it to an asynchronous operation. 73 | NSXMLElement *item = [itemSubElement copy]; 74 | 75 | [self scheduleBlock:^{ 76 | 77 | NSManagedObjectContext *moc = [self managedObjectContext]; 78 | 79 | if ([rosterPopulationSet containsObject:[NSNumber xmpp_numberWithPtr:(__bridge void *)stream]]) 80 | { 81 | NSString *streamBareJidStr = [[self myJIDForXMPPStream:stream] bare]; 82 | 83 | 84 | 85 | XMPPUserCoreDataStorageObject* user = [XMPPUserCoreDataStorageObject insertInManagedObjectContext:moc 86 | withItem:item 87 | streamBareJidStr:streamBareJidStr]; 88 | 89 | [EloContactListCoreDataStorage.sharedInstance didUpdateOrInsertUser:user]; 90 | 91 | } 92 | else 93 | { 94 | NSString *jidStr = [item attributeStringValueForName:@"jid"]; 95 | XMPPJID *jid = [[XMPPJID jidWithString:jidStr] bareJID]; 96 | 97 | //TODO also add to contact list ?? 98 | 99 | XMPPUserCoreDataStorageObject *user = [self userForJID:jid xmppStream:stream managedObjectContext:moc]; 100 | 101 | NSString *subscription = [item attributeStringValueForName:@"subscription"]; 102 | if ([subscription isEqualToString:@"remove"]) 103 | { 104 | if (user) 105 | { 106 | [moc deleteObject:user]; 107 | } 108 | } 109 | else 110 | { 111 | if (user) 112 | { 113 | [user updateWithItem:item]; 114 | } 115 | else 116 | { 117 | NSString *streamBareJidStr = [[self myJIDForXMPPStream:stream] bare]; 118 | 119 | [XMPPUserCoreDataStorageObject insertInManagedObjectContext:moc 120 | withItem:item 121 | streamBareJidStr:streamBareJidStr]; 122 | } 123 | } 124 | } 125 | }]; 126 | } 127 | 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/GlobalMenuViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | class GlobalMenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 5 | 6 | @IBOutlet weak var tableView: UITableView! 7 | 8 | enum ItemType { 9 | case Accounts 10 | case Preferences 11 | } 12 | 13 | struct Item { 14 | var type:ItemType; 15 | var name = ""; 16 | var groups:[Group]; 17 | } 18 | 19 | enum Group { 20 | case Roster 21 | case Message 22 | } 23 | 24 | 25 | let items = [ 26 | Item(type: ItemType.Accounts, name:"Accounts", groups: [Group.Roster, Group.Message] ), 27 | Item(type: ItemType.Preferences, name:"Preferences", groups: [Group.Roster, Group.Message] ) 28 | ]; 29 | 30 | var currentGroup = Group.Roster 31 | var currentItems = [Item]() 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad(); 35 | self.modalInPopover = false; 36 | initItems(); 37 | 38 | // tableView.registerClass(GlobalMenuCell.self, forCellReuseIdentifier: "GlobalMenuCell"); 39 | 40 | //[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"WYSettingsTableViewCell"]; 41 | 42 | //[self.navigationItem.rightBarButtonItem setTintColor:[UIColor colorWithRed:195./255. green:4./255. blue:94./255. alpha:1.]]; 43 | tableView.delegate = self; 44 | tableView.dataSource = self; 45 | 46 | } 47 | 48 | override func viewWillAppear(animated: Bool){ 49 | //NSLog(@"view WILL appear"); 50 | } 51 | 52 | override func viewDidAppear(animated: Bool){ 53 | //NSLog(@"view DID appear"); 54 | } 55 | 56 | override func viewWillDisappear(animated: Bool){ 57 | //NSLog(@"view WILL disappear"); 58 | } 59 | 60 | override func viewDidDisappear(animated: Bool){ 61 | //NSLog(@"view DID disappear"); 62 | } 63 | 64 | override func didReceiveMemoryWarning(){ 65 | super.didReceiveMemoryWarning(); 66 | // Dispose of any resources that can be recreated. 67 | } 68 | 69 | //pragma mark - UITableViewDataSource 70 | 71 | func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 72 | return ""; 73 | } 74 | 75 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 76 | 77 | //UITableViewCell* cell = [aTableView dequeueReusableCellWithIdentifier:@"WYSettingsTableViewCell" forIndexPath:indexPath]; 78 | var cell = tableView.dequeueReusableCellWithIdentifier("GlobalMenuCell", forIndexPath: indexPath) as? GlobalMenuCell; 79 | 80 | if(cell == nil) { 81 | cell = GlobalMenuCell(style: UITableViewCellStyle.Default, reuseIdentifier: "GlobalMenuCell"); 82 | } 83 | 84 | self.updateCell(cell!, indexPath:indexPath); 85 | 86 | return cell!; 87 | 88 | } 89 | 90 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 91 | return items.count 92 | } 93 | 94 | func numberOfSectionsInTableView(tableView: UITableView) -> Int { 95 | return 1; 96 | } 97 | 98 | //pragma mark - UITableViewDelegate 99 | 100 | func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 101 | tableView.deselectRowAtIndexPath(indexPath, animated:true); 102 | 103 | switch(items[indexPath.row].type){ 104 | case ItemType.Accounts: 105 | NSNotificationCenter.defaultCenter().postNotificationName(EloConstants.SHOW_ACCOUNTS, object: self); 106 | break 107 | case ItemType.Preferences: 108 | NSNotificationCenter.defaultCenter().postNotificationName(EloConstants.SHOW_PREFERENCES, object: self); 109 | break 110 | } 111 | 112 | 113 | } 114 | 115 | //#pragma mark - Private 116 | func updateCell(cell: GlobalMenuCell, indexPath: NSIndexPath){ 117 | 118 | cell.textLabel?.text = items[indexPath.row].name; 119 | } 120 | 121 | func initItems(){ 122 | 123 | currentItems = [Item]() 124 | 125 | var height:CGFloat = 0; 126 | 127 | for item in items { 128 | if(item.groups.contains(currentGroup)){ 129 | height += 44; 130 | currentItems.append(item); 131 | } 132 | } 133 | 134 | self.preferredContentSize = CGSizeMake(200, height); 135 | } 136 | 137 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloXMPPRoster.xcdatamodeld/EloXMPPRoster.xcdatamodel/contents: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/Core/EloRoster.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XMPPFramework 3 | 4 | 5 | protocol EloRosterDelegate:NSObjectProtocol { 6 | 7 | func rosterWillChangeContent(); 8 | func roster(didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: EloFetchedResultsChangeType, newIndexPath: NSIndexPath?); 9 | func rosterDidChangeContent(); 10 | } 11 | 12 | class EloRoster:NSObject, EloFetchedResultsControllerDelegate { 13 | 14 | var fetchedResultsController: EloFetchedResultsController!; 15 | 16 | var delegate:EloRosterDelegate?; 17 | 18 | override init() { 19 | super.init() 20 | } 21 | 22 | func initializeData(){ 23 | 24 | let request = NSFetchRequest(entityName: "EloContactList_Item_CoreDataObject"); 25 | let departmentSort = NSSortDescriptor(key: "bareJidStr", ascending: true) 26 | request.sortDescriptors = [departmentSort] 27 | 28 | let moc = EloContactListCoreDataStorage.sharedInstance().mainThreadManagedObjectContext 29 | 30 | initFRC(request,managedObjectContext: moc); 31 | fetchedResultsController.delegate = self 32 | 33 | do { 34 | try fetchedResultsController.performFetch() 35 | } catch { 36 | fatalError("Failed to initialize FetchedResultsController: \(error)") 37 | } 38 | } 39 | 40 | #if os(iOS) 41 | func initFRC(request: NSFetchRequest, managedObjectContext: NSManagedObjectContext){ 42 | fetchedResultsController = EloFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext, sectionNameKeyPath:nil, cacheName: nil) 43 | } 44 | #else 45 | func initFRC(request: NSFetchRequest, managedObjectContext: NSManagedObjectContext){ 46 | fetchedResultsController = EloFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext) 47 | } 48 | #endif 49 | 50 | #if os(iOS) 51 | func numberOfRowsInSection(section: Int) -> Int { 52 | 53 | if(fetchedResultsController.sections!.count > 0){ 54 | let sectionInfo = fetchedResultsController.sections![section] 55 | return sectionInfo.numberOfObjects 56 | }else{ 57 | return 0 58 | } 59 | } 60 | #else 61 | 62 | func numberOfRows() -> Int { 63 | return fetchedResultsController.fetchedObjects!.count; 64 | } 65 | 66 | #endif 67 | 68 | 69 | 70 | 71 | func getContactListItem(index: Int) -> EloContactList_Item_CoreDataObject { 72 | #if os(iOS) 73 | return fetchedResultsController.objectAtIndexPath(NSIndexPath(forItem: index, inSection: 0) ) as! EloContactList_Item_CoreDataObject 74 | #else 75 | return fetchedResultsController.fetchedObjects![index] as! EloContactList_Item_CoreDataObject 76 | #endif 77 | } 78 | 79 | func chatWishedByUserAction(index:Int) { 80 | let contactListItem = getContactListItem(index); 81 | let chatId = EloChatId(from: EloAccountJid(contactListItem.streamBareJidStr) ,to: EloContactJid(contactListItem.bareJidStr)) 82 | NSNotificationCenter.defaultCenter().postNotificationName(EloConstants.ACTIVATE_CONTACT, object: chatId) 83 | 84 | } 85 | 86 | func getChatId(index:Int) -> EloChatId { 87 | let contactListItem = getContactListItem(index); 88 | return EloChatId(from: EloAccountJid(contactListItem.streamBareJidStr) ,to: EloContactJid(contactListItem.bareJidStr)) 89 | } 90 | 91 | /* EloFetchedResultsControllerDelegate */ 92 | 93 | func controllerWillChangeContent(controller: EloFetchedResultsController) { 94 | if(delegate != nil){ 95 | delegate!.rosterWillChangeContent() 96 | } 97 | } 98 | 99 | #if os(iOS) 100 | func controller(controller: EloFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: EloFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 101 | if(delegate != nil){ 102 | delegate!.roster(didChangeObject: anObject, atIndexPath:indexPath,forChangeType: type, newIndexPath: newIndexPath) 103 | } 104 | } 105 | #else 106 | func controller(controller: EloFetchedResultsController, didChangeObject anObject: NSManagedObject, atIndex index: UInt, forChangeType type: EloFetchedResultsChangeType, newIndex: UInt) { 107 | if(delegate != nil){ 108 | delegate!.roster(didChangeObject: anObject, atIndexPath:NSIndexPath(index: Int(index)),forChangeType: type, newIndexPath: NSIndexPath(index:Int(newIndex))) 109 | } 110 | } 111 | 112 | #endif 113 | 114 | 115 | func controllerDidChangeContent(controller: EloFetchedResultsController) { 116 | NSLog("Didchange") 117 | if(delegate != nil){ 118 | delegate!.rosterDidChangeContent() 119 | } 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /Eloquence/Eloquence.xcodeproj/xcshareddata/xcschemes/Eloquence.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 89 | 90 | 91 | 92 | 93 | 94 | 100 | 102 | 108 | 109 | 110 | 111 | 113 | 114 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/EMAddAccountViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | protocol EMAddAccountViewControllerDelegate:NSObjectProtocol { 5 | func doCloseAddAccountView(); 6 | } 7 | 8 | class EMAddAccountViewController:UIViewController { 9 | 10 | var delegate:EMAddAccountViewControllerDelegate? 11 | 12 | var account:EloAccount?; 13 | 14 | @IBOutlet weak var titleBar: UINavigationBar! 15 | @IBOutlet weak var jidField: UITextField! 16 | 17 | @IBOutlet weak var passwordField: UITextField! 18 | 19 | @IBOutlet var advancedSettingsSwitch: UISwitch! 20 | @IBOutlet weak var infoText: UILabel! 21 | 22 | @IBOutlet var priorityLabel: UILabel! 23 | @IBOutlet var priorityField: UITextField! 24 | 25 | @IBOutlet var resourceLabel: UILabel! 26 | @IBOutlet var resourceField: UITextField! 27 | 28 | @IBOutlet var serverLabel: UILabel! 29 | @IBOutlet var serverField: UITextField! 30 | 31 | @IBOutlet var portLabel: UILabel! 32 | @IBOutlet var portField: UITextField! 33 | 34 | override func viewWillAppear(animated: Bool) { 35 | if(account != nil){ 36 | 37 | let safeAccount = account! 38 | 39 | titleBar!.items![0].title = "Account Details" 40 | jidField.text = safeAccount.getJid().jid 41 | passwordField.text = safeAccount.getPassword() 42 | 43 | var infoTextString = "Server Info"; 44 | 45 | let capabilities = EloCapabilities().getCapabilities(safeAccount.getJid()) 46 | 47 | for capability in capabilities { 48 | var supported = "No" 49 | if(capability.supportedByServer){ supported = "Yes" } 50 | infoTextString.appendContentsOf("\n" + capability.xep + " " + capability.name + "\t" + supported) 51 | } 52 | 53 | infoText.sizeToFit() 54 | if(capabilities.count == 0){ 55 | infoText.numberOfLines = 2 56 | infoTextString.appendContentsOf("Account currently offline") 57 | }else{ 58 | infoText.numberOfLines = 1 + capabilities.count 59 | } 60 | infoText.text = infoTextString 61 | 62 | }else{ 63 | titleBar!.items![0].title = "New Account" 64 | } 65 | } 66 | 67 | 68 | override func preferredStatusBarStyle() -> UIStatusBarStyle { 69 | return UIStatusBarStyle.LightContent; 70 | } 71 | 72 | @IBAction func cancelClick(sender: AnyObject) { 73 | close(); 74 | } 75 | 76 | @IBAction func saveClick(sender: AnyObject) { 77 | 78 | 79 | if(account == nil){ 80 | account = DataController.sharedInstance.insertAccount(); 81 | } 82 | 83 | let safeAccount = account!; 84 | safeAccount.jid = NSString(UTF8String: jidField.text!)!; 85 | 86 | if(priorityField.text != nil){ 87 | let priority = Int(priorityField.text!); 88 | if(priority != nil){ 89 | safeAccount.priority = priority!; 90 | } 91 | } 92 | 93 | if(priorityField.text != nil){ 94 | safeAccount.resource = resourceField.text; 95 | } 96 | 97 | safeAccount.server = serverField.text; 98 | 99 | if(portField.text != nil){ 100 | let port = Int(portField.text!); 101 | if(port != nil){ 102 | safeAccount.port = port!; 103 | } 104 | } 105 | 106 | safeAccount.autoconnect = 1; //autoConnect.state; TODO 107 | 108 | if(passwordField.text != nil){ 109 | safeAccount.setPassword(passwordField.text!); 110 | } 111 | 112 | DataController.sharedInstance.save(); 113 | 114 | 115 | close(); 116 | } 117 | 118 | @IBAction func toggleAdvancedSettings(sender: AnyObject) { 119 | if(advancedSettingsSwitch.on){ 120 | priorityLabel.hidden = false 121 | priorityField.hidden = false 122 | resourceLabel.hidden = false 123 | resourceField.hidden = false 124 | serverLabel.hidden = false 125 | serverField.hidden = false 126 | portLabel.hidden = false 127 | portField.hidden = false 128 | }else{ 129 | priorityLabel.hidden = true 130 | priorityField.hidden = true 131 | resourceLabel.hidden = true 132 | resourceField.hidden = true 133 | serverLabel.hidden = true 134 | serverField.hidden = true 135 | portLabel.hidden = true 136 | portField.hidden = true 137 | } 138 | } 139 | 140 | func close(){ 141 | if(delegate != nil){ 142 | delegate!.doCloseAddAccountView(); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/XEP-0313/EloXMPPMessageArchiveManagement_Message_CoreDataObject.m: -------------------------------------------------------------------------------- 1 | #import "EloXMPPMessageArchiveManagement_Message_CoreDataObject.h" 2 | #import "XMPPMessage+Elo_XEP_0313.h" 3 | 4 | 5 | @interface EloXMPPMessageArchiveManagement_Message_CoreDataObject () 6 | 7 | @property(nonatomic,strong) XMPPMessage * primitiveMessage; 8 | @property(nonatomic,strong) NSString * primitiveMessageStr; 9 | @property(nonatomic,strong) NSString * primitiveMessageId; 10 | 11 | @property(nonatomic,strong) XMPPJID * primitiveBareJid; 12 | @property(nonatomic,strong) NSString * primitiveBareJidStr; 13 | 14 | @end 15 | 16 | @implementation EloXMPPMessageArchiveManagement_Message_CoreDataObject 17 | 18 | @dynamic message, primitiveMessage; 19 | @dynamic messageStr, primitiveMessageStr; 20 | @dynamic bareJid, primitiveBareJid; 21 | @dynamic bareJidStr, primitiveBareJidStr; 22 | @dynamic body; 23 | @dynamic thread; 24 | @dynamic outgoing; 25 | @dynamic composing; 26 | @dynamic timestamp; 27 | @dynamic streamBareJidStr; 28 | @dynamic messageId, primitiveMessageId; 29 | 30 | #pragma mark Transient message 31 | 32 | - (XMPPMessage *)message 33 | { 34 | // Create and cache on demand 35 | 36 | [self willAccessValueForKey:@"message"]; 37 | XMPPMessage *message = self.primitiveMessage; 38 | [self didAccessValueForKey:@"message"]; 39 | 40 | if (message == nil) 41 | { 42 | NSString *messageStr = self.messageStr; 43 | if (messageStr) 44 | { 45 | NSXMLElement *element = [[NSXMLElement alloc] initWithXMLString:messageStr error:nil]; 46 | message = [XMPPMessage messageFromElement:element]; 47 | self.primitiveMessage = message; 48 | } 49 | } 50 | 51 | return message; 52 | } 53 | 54 | - (void)setMessage:(XMPPMessage *)message 55 | { 56 | [self willChangeValueForKey:@"message"]; 57 | [self willChangeValueForKey:@"messageStr"]; 58 | [self willChangeValueForKey:@"messageId"]; 59 | 60 | self.primitiveMessage = message; 61 | self.primitiveMessageStr = [message compactXMLString]; 62 | self.primitiveMessageId = [message messageId]; 63 | 64 | [self didChangeValueForKey:@"message"]; 65 | [self didChangeValueForKey:@"messageStr"]; 66 | [self didChangeValueForKey:@"messageId"]; 67 | } 68 | 69 | - (void)setMessageStr:(NSString *)messageStr 70 | { 71 | [self willChangeValueForKey:@"message"]; 72 | [self willChangeValueForKey:@"messageStr"]; 73 | [self willChangeValueForKey:@"messageId"]; 74 | 75 | NSXMLElement *element = [[NSXMLElement alloc] initWithXMLString:messageStr error:nil]; 76 | self.primitiveMessage = [XMPPMessage messageFromElement:element]; 77 | self.primitiveMessageStr = messageStr; 78 | self.primitiveMessageId = nil; 79 | 80 | [self didChangeValueForKey:@"messageId"]; 81 | [self didChangeValueForKey:@"message"]; 82 | [self didChangeValueForKey:@"messageStr"]; 83 | } 84 | 85 | #pragma mark Transient bareJid 86 | 87 | - (XMPPJID *)bareJid 88 | { 89 | // Create and cache on demand 90 | 91 | [self willAccessValueForKey:@"bareJid"]; 92 | XMPPJID *tmp = self.primitiveBareJid; 93 | [self didAccessValueForKey:@"bareJid"]; 94 | 95 | if (tmp == nil) 96 | { 97 | NSString *bareJidStr = self.bareJidStr; 98 | if (bareJidStr) 99 | { 100 | tmp = [XMPPJID jidWithString:bareJidStr]; 101 | self.primitiveBareJid = tmp; 102 | } 103 | } 104 | 105 | return tmp; 106 | } 107 | 108 | - (void)setBareJid:(XMPPJID *)bareJid 109 | { 110 | if ([self.bareJid isEqualToJID:bareJid options:XMPPJIDCompareBare]) 111 | { 112 | return; // No change 113 | } 114 | 115 | [self willChangeValueForKey:@"bareJid"]; 116 | [self willChangeValueForKey:@"bareJidStr"]; 117 | 118 | self.primitiveBareJid = [bareJid bareJID]; 119 | self.primitiveBareJidStr = [bareJid bare]; 120 | 121 | [self didChangeValueForKey:@"bareJid"]; 122 | [self didChangeValueForKey:@"bareJidStr"]; 123 | } 124 | 125 | - (void)setBareJidStr:(NSString *)bareJidStr 126 | { 127 | if ([self.bareJidStr isEqualToString:bareJidStr]) 128 | { 129 | return; // No change 130 | } 131 | 132 | [self willChangeValueForKey:@"bareJid"]; 133 | [self willChangeValueForKey:@"bareJidStr"]; 134 | 135 | XMPPJID *bareJid = [[XMPPJID jidWithString:bareJidStr] bareJID]; 136 | 137 | self.primitiveBareJid = bareJid; 138 | self.primitiveBareJidStr = [bareJid bare]; 139 | 140 | [self didChangeValueForKey:@"bareJid"]; 141 | [self didChangeValueForKey:@"bareJidStr"]; 142 | } 143 | 144 | #pragma mark Convenience properties 145 | 146 | - (BOOL)isOutgoing 147 | { 148 | return [self.outgoing boolValue]; 149 | } 150 | 151 | - (void)setIsOutgoing:(BOOL)flag 152 | { 153 | self.outgoing = @(flag); 154 | } 155 | 156 | - (BOOL)isComposing 157 | { 158 | return [self.composing boolValue]; 159 | } 160 | 161 | - (void)setIsComposing:(BOOL)flag 162 | { 163 | self.composing = @(flag); 164 | } 165 | 166 | #pragma mark Hooks 167 | 168 | - (void)willInsertObject 169 | { 170 | // If you extend EloXMPPMessageArchiveManagement_Message_CoreDataObject, 171 | // you can override this method to use as a hook to set your own custom properties. 172 | } 173 | 174 | - (void)didUpdateObject 175 | { 176 | // If you extend EloXMPPMessageArchiveManagement_Message_CoreDataObject, 177 | // you can override this method to use as a hook to set your own custom properties. 178 | } 179 | 180 | @end 181 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/MessageViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import JNWCollectionView 3 | 4 | class MessageViewController: NSViewController, JNWCollectionViewDelegate, JNWCollectionViewDataSource, JNWCollectionViewListLayoutDelegate, EloChatDelegate { 5 | 6 | var chat:EloChat; 7 | dynamic var messages = [EloMessage](); 8 | 9 | @IBOutlet weak var mainInput: NSTextField! 10 | @IBOutlet weak var sendButton: NSButton! 11 | @IBOutlet var adressLabel: NSTextField! 12 | @IBOutlet var scrollView: JNWCollectionView! 13 | 14 | init(nibName nibNameOrNil:String?, chatId:EloChatId){ 15 | chat = EloChats.sharedInstance.getChat(chatId.from, to: chatId.to); 16 | super.init(nibName: nibNameOrNil, bundle: nil)!; 17 | chat.delegate = self; 18 | } 19 | 20 | required init?(coder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | adressLabel.stringValue = chat.to.jid + " via " + chat.from.jid; 28 | NSLog("Load message controller"); 29 | view.autoresizingMask = [.ViewWidthSizable, .ViewHeightSizable] 30 | 31 | let listLayout = JNWCollectionViewListLayout(collectionView: scrollView) 32 | listLayout.rowHeight = 60 33 | listLayout.delegate = self 34 | 35 | scrollView .collectionViewLayout = listLayout 36 | 37 | scrollView.registerClass(EXMessageViewCellIncoming.self, forCellWithReuseIdentifier: "incomingCell") 38 | scrollView.registerClass(EXMessageViewCellOutgoing.self, forCellWithReuseIdentifier: "outgoingCell") 39 | 40 | scrollView.delegate = self 41 | scrollView.dataSource = self 42 | 43 | chat.loadInitialArchive(); 44 | 45 | reloadAndScroll() 46 | 47 | } 48 | 49 | override func viewDidAppear() { 50 | scrollToBottom(); 51 | } 52 | 53 | 54 | @IBAction func clickSendButton(sender: AnyObject) { 55 | 56 | let text = mainInput.stringValue; 57 | 58 | mainInput.stringValue = ""; 59 | let msg = EloMessage(); 60 | msg.text = "Ich: " + text; 61 | messages.append(msg); 62 | try! chat.sendTextMessage(text); //TODO 63 | } 64 | 65 | /** DELEGATE **/ 66 | 67 | 68 | func didReceiveMessage(msg: EloMessage) { 69 | dispatch_async(dispatch_get_main_queue(), { 70 | self.messages.append(msg); 71 | }) 72 | 73 | } 74 | 75 | func didFailSendMessage(msg: EloMessage){ 76 | 77 | } 78 | 79 | private func scrollToBottom(){ 80 | scrollView.scrollToItemAtIndexPath( NSIndexPath(forItem: chat.numberOfRows()-1, inSection: 0) , atScrollPosition: JNWCollectionViewScrollPosition.Top, animated: true) 81 | } 82 | 83 | private func reloadAndScroll() { 84 | scrollView.reloadData() 85 | scrollToBottom(); 86 | } 87 | 88 | //Mark: EloChatDelegate 89 | func chatWillChangeContent() { 90 | 91 | } 92 | 93 | func chat(didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: EloFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 94 | 95 | } 96 | func chatDidChangeContent() { 97 | 98 | reloadAndScroll(); 99 | } 100 | 101 | 102 | //Mark: JNWCollectionViewDelegate 103 | func collectionView(collectionView: JNWCollectionView!, didSelectItemAtIndexPath indexPath: NSIndexPath!) { 104 | //TODO ? 105 | } 106 | 107 | //Mark: JNWCollectionViewDataSource 108 | func collectionView(collectionView: JNWCollectionView!, numberOfItemsInSection section: Int) -> UInt { 109 | return UInt(chat.numberOfRows()); 110 | } 111 | 112 | 113 | //Mark: JNWCollectionViewListLayoutDelegate 114 | func collectionView(collectionView: JNWCollectionView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat { 115 | 116 | let message = chat.getMessage(indexPath.item) 117 | 118 | let maximumLabelSize = CGSizeMake(scrollView.frame.size.width, CGFloat(FLT_MAX)) 119 | let textRect = NSString(string:message.text!).boundingRectWithSize(maximumLabelSize, options: .UsesLineFragmentOrigin , attributes: [ NSFontAttributeName: NSFont.systemFontOfSize(13) ], context: nil) 120 | 121 | 122 | return max(48 /* avatar height */,textRect.height + 20 /* the date */ + 30 /* text padding */) + 20 /* cell padding */ 123 | } 124 | 125 | /// Asks the data source for the view that should be used for the cell at the specified index path. The returned 126 | func collectionView(collectionView: JNWCollectionView!, cellForItemAtIndexPath indexPath: NSIndexPath!) -> JNWCollectionViewCell! { 127 | 128 | let message = chat.getMessage(indexPath.item) 129 | 130 | var cell:EXMessageViewCell 131 | 132 | if(message.isOutgoing){ 133 | cell = scrollView.dequeueReusableCellWithIdentifier("outgoingCell") as! EXMessageViewCellOutgoing 134 | }else{ 135 | cell = scrollView.dequeueReusableCellWithIdentifier("incomingCell") as! EXMessageViewCellIncoming 136 | } 137 | 138 | cell.setMessage(message); 139 | 140 | 141 | 142 | return cell; 143 | } 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/Cells/EXRosterContactCell.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloContactListCoreDataStorage.m: -------------------------------------------------------------------------------- 1 | #import "XMPPLogging.h" 2 | #import 3 | #import "EloContactListCoreDataStorage.h" 4 | #import "EloContactList_Item_CoreDataObject.h" 5 | 6 | #if ! __has_feature(objc_arc) 7 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 8 | #endif 9 | 10 | // Log levels: off, error, warn, info, verbose 11 | #if DEBUG 12 | static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE; 13 | #else 14 | static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN; 15 | #endif 16 | 17 | #define AssertPrivateQueue() \ 18 | NSAssert(dispatch_get_specific(storageQueueTag), @"Private method: MUST run on storageQueue"); 19 | 20 | 21 | @implementation EloContactListCoreDataStorage 22 | 23 | static EloContactListCoreDataStorage *sharedInstance; 24 | 25 | + (instancetype)sharedInstance 26 | { 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | 30 | sharedInstance = [[EloContactListCoreDataStorage alloc] initWithDatabaseFilename: nil storeOptions:nil]; 31 | }); 32 | 33 | return sharedInstance; 34 | } 35 | 36 | /* 37 | - (void)commonInit { 38 | 39 | [super commonInit]; 40 | 41 | [[NSNotificationCenter defaultCenter] addObserver:self 42 | selector:@selector(handleDataModelChange:) 43 | name:NSManagedObjectContextObjectsDidChangeNotification 44 | object:[self managedObjectContext]]; 45 | 46 | 47 | } 48 | 49 | - (void)handleDataModelChange:(NSNotification *)note 50 | { 51 | NSSet *updatedObjects = [[note userInfo] objectForKey:NSUpdatedObjectsKey]; 52 | NSSet *deletedObjects = [[note userInfo] objectForKey:NSDeletedObjectsKey]; 53 | NSSet *insertedObjects = [[note userInfo] objectForKey:NSInsertedObjectsKey]; 54 | 55 | NSLog(@"called"); 56 | } 57 | */ 58 | 59 | - (NSEntityDescription *)itemEntity:(NSManagedObjectContext *)moc 60 | { 61 | return [NSEntityDescription entityForName:@"EloContactList_Item_CoreDataObject" inManagedObjectContext:moc]; 62 | } 63 | 64 | - (EloContactList_Item_CoreDataObject*) itemForBareJid:(NSString*) itemBareJidStr 65 | managedObjectContext:(NSManagedObjectContext *)moc{ 66 | 67 | NSEntityDescription *entity = [self itemEntity:moc]; 68 | 69 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr == %@", itemBareJidStr]; 70 | 71 | NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 72 | [fetchRequest setEntity:entity]; 73 | [fetchRequest setFetchLimit:1]; 74 | [fetchRequest setPredicate:predicate]; 75 | 76 | NSError *error = nil; 77 | NSArray *results = [moc executeFetchRequest:fetchRequest error:&error]; 78 | 79 | if (results == nil) 80 | { 81 | XMPPLogError(@"%@: %@ - Fetch request error: %@", THIS_FILE, THIS_METHOD, error); 82 | return nil; 83 | } 84 | else 85 | { 86 | return (EloContactList_Item_CoreDataObject *)[results lastObject]; 87 | } 88 | } 89 | 90 | 91 | - (void)didUpdateOrInsertMamContact:(EloXMPPMessageArchiveManagement_Contact_CoreDataObject*) mamContact { 92 | 93 | [self scheduleBlock:^{ 94 | 95 | BOOL didCreateNewItem = NO; 96 | 97 | NSManagedObjectContext *moc = [self managedObjectContext]; 98 | 99 | EloContactList_Item_CoreDataObject *item = [self itemForBareJid:mamContact.bareJidStr managedObjectContext:moc]; 100 | 101 | if (item == nil) 102 | { 103 | item = (EloContactList_Item_CoreDataObject *) 104 | [[NSManagedObject alloc] initWithEntity:[self itemEntity:moc] 105 | insertIntoManagedObjectContext:nil]; 106 | 107 | didCreateNewItem = YES; 108 | } 109 | 110 | item.bareJid = mamContact.bareJid; 111 | item.bareJidStr = mamContact.bareJidStr; 112 | item.streamBareJidStr = mamContact.streamBareJidStr;; 113 | 114 | XMPPLogVerbose(@"New contact list item: %@", item); 115 | 116 | if (didCreateNewItem) 117 | { 118 | XMPPLogVerbose(@"Inserting contact list item ..."); 119 | 120 | [moc insertObject:item]; 121 | } 122 | else 123 | { 124 | XMPPLogVerbose(@"Updating contact..."); 125 | } 126 | }]; 127 | 128 | } 129 | 130 | - (void)didUpdateOrInsertUser:(XMPPUserCoreDataStorageObject*) user { 131 | [self scheduleBlock:^{ 132 | 133 | BOOL didCreateNewItem = NO; 134 | 135 | NSManagedObjectContext *moc = [self managedObjectContext]; 136 | 137 | EloContactList_Item_CoreDataObject *item = [self itemForBareJid:user.jidStr managedObjectContext:moc]; 138 | 139 | if (item == nil) 140 | { 141 | item = (EloContactList_Item_CoreDataObject *) [[NSManagedObject alloc] initWithEntity:[self itemEntity:moc] 142 | insertIntoManagedObjectContext:nil]; 143 | 144 | didCreateNewItem = YES; 145 | } 146 | 147 | item.bareJid = user.jid; 148 | item.bareJidStr = user.jidStr; 149 | item.streamBareJidStr = user.streamBareJidStr;; 150 | 151 | XMPPLogVerbose(@"New contact list item: %@", item); 152 | 153 | if (didCreateNewItem) 154 | { 155 | XMPPLogVerbose(@"Inserting contact list item ..."); 156 | 157 | [moc insertObject:item]; 158 | } 159 | else 160 | { 161 | XMPPLogVerbose(@"Updating contact..."); 162 | } 163 | }]; 164 | } 165 | 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/EXMessageViewCellOutgoing.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/Eloquence/EXMessageViewCellIncoming.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/MessageViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | import XMPPFramework 4 | import JSQMessagesViewController 5 | import MBProgressHUD 6 | 7 | 8 | class MessageViewController:JSQMessagesViewController, UIActionSheetDelegate, JSQMessagesComposerTextViewPasteDelegate, EloChatDelegate { 9 | 10 | private var chat: EloChat?; 11 | var outgoingBubbleImageView: JSQMessagesBubbleImage! 12 | var incomingBubbleImageView: JSQMessagesBubbleImage! 13 | 14 | override func viewDidLoad() { 15 | 16 | super.viewDidLoad() 17 | 18 | self.title = ""; 19 | setupBubbles() 20 | 21 | /** 22 | @IBOutlet var adressLabel: NSTextField! 23 | @IBOutlet var adressLabel: NSTextField! 24 | * You MUST set your senderId and display name 25 | */ 26 | 27 | self.senderId = "foo" 28 | self.senderDisplayName = "foo" 29 | 30 | // self.inputToolbar.contentView.textView.pasteDelegate = self; 31 | 32 | self.view!.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] 33 | 34 | // No avatars 35 | collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSizeZero 36 | collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero 37 | 38 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MessageViewController.loadChat(_:)), name: EloConstants.ACTIVATE_CONTACT, object: nil) 39 | } 40 | 41 | private func setupBubbles() { 42 | let factory = JSQMessagesBubbleImageFactory() 43 | outgoingBubbleImageView = factory.outgoingMessagesBubbleImageWithColor(UIColor.jsq_messageBubbleBlueColor()) 44 | incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(UIColor.jsq_messageBubbleLightGrayColor()) 45 | } 46 | 47 | override func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! { 48 | 49 | if(chat != nil){ 50 | let msg = chat!.getMessage(indexPath.item); 51 | return JSQMessage(senderId: msg.author, senderDisplayName:msg.author, date: NSDate(), text: msg.text ) 52 | }else{ 53 | return nil; 54 | } 55 | 56 | } 57 | 58 | override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 59 | if(chat != nil){ 60 | NSLog("Items %d",chat!.numberOfRowsInSection(0)); 61 | return chat!.numberOfRowsInSection(0); 62 | }else { 63 | return 0; 64 | } 65 | 66 | } 67 | 68 | override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! { 69 | return nil 70 | } 71 | 72 | override func collectionView(collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! { 73 | let msg = chat!.getMessage(indexPath.item);// 1 74 | if msg.isOutgoing { // 2 75 | return outgoingBubbleImageView 76 | } else { // 3 77 | return incomingBubbleImageView 78 | } 79 | } 80 | 81 | private func getSafeChat() -> EloChat { 82 | return chat! 83 | } 84 | 85 | 86 | func loadChat(notification:NSNotification){ 87 | 88 | let chatId = notification.object as! EloChatId 89 | 90 | chat = EloChats.sharedInstance.getChat(chatId.from, to: chatId.to); 91 | chat!.delegate = self; 92 | 93 | self.automaticallyScrollsToMostRecentMessage = false; 94 | 95 | let progressHUD = MBProgressHUD.showHUDAddedTo(self.view, animated: true) 96 | progressHUD.labelText = "Loading History ..." 97 | 98 | dispatch_async(dispatch_get_main_queue()) { 99 | 100 | self.chat!.loadInitialArchive() 101 | 102 | dispatch_async(dispatch_get_main_queue()) { 103 | self.finishReceivingMessageAnimated(false) 104 | progressHUD.hide(true) 105 | self.automaticallyScrollsToMostRecentMessage = true 106 | } 107 | } 108 | 109 | 110 | } 111 | 112 | //Mark: EloChatDelegate 113 | 114 | func didReceiveMessage(msg: EloMessage) { 115 | 116 | dispatch_async(dispatch_get_main_queue(), { 117 | 118 | self.finishReceivingMessageAnimated(true) 119 | }) 120 | 121 | } 122 | 123 | func didFailSendMessage(msg: EloMessage) { 124 | dispatch_async(dispatch_get_main_queue(), { 125 | 126 | self.finishReceivingMessageAnimated(true) 127 | }) 128 | } 129 | 130 | func chatWillChangeContent() { 131 | dispatch_async(dispatch_get_main_queue(), { 132 | 133 | self.finishReceivingMessageAnimated(true) 134 | }) 135 | } 136 | 137 | func chat(didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: EloFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 138 | dispatch_async(dispatch_get_main_queue(), { 139 | 140 | self.finishReceivingMessageAnimated(true) 141 | }) } 142 | 143 | func chatDidChangeContent() { 144 | dispatch_async(dispatch_get_main_queue(), { 145 | 146 | self.finishReceivingMessageAnimated(true) 147 | }) 148 | } 149 | 150 | 151 | //Mark: JSQMessagesComposerTextViewPasteDelegate 152 | 153 | func composerTextView(textView: JSQMessagesComposerTextView!, shouldPasteWithSender sender: AnyObject!) -> Bool { 154 | return true; 155 | } 156 | 157 | 158 | //Mark JSQMessagesViewController 159 | 160 | override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!) { 161 | 162 | JSQSystemSoundPlayer.jsq_playMessageSentSound(); 163 | 164 | //let message = JSQMessage(senderId: senderId, senderDisplayName:senderDisplayName, date: date, text: text) 165 | //messages.append(message) 166 | 167 | do { 168 | try getSafeChat().sendTextMessage(text); 169 | }catch EloChatError.NotConnected { 170 | //TODO 171 | }catch { 172 | //TODO 173 | } 174 | 175 | finishSendingMessageAnimated(true) 176 | 177 | 178 | 179 | } 180 | } -------------------------------------------------------------------------------- /Eloquence/Core/XMPP/EloContactListAndArchive.xcdatamodeld/EloContactListAndArchive.xcdatamodel/contents: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Eloquence/EloquenceIOS/PhoneMainViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | import WYPopoverController; 4 | import XMPPFramework 5 | 6 | class PhoneMainViewController:UIViewController, WYPopoverControllerDelegate, EMPreferencesViewControllerDelegate, EMAccountsViewControllerDelegate{ 7 | 8 | @IBOutlet weak var mainView: UIView! 9 | 10 | @IBOutlet weak var secondView: UIView! 11 | 12 | @IBOutlet weak var backButton: UIBarButtonItem! 13 | 14 | @IBOutlet weak var toolbar: UINavigationBar! 15 | 16 | @IBOutlet weak var titleItem: UINavigationItem! 17 | 18 | var settingsPopoverController:WYPopoverController?; 19 | 20 | var preferencesController:EMPreferencesViewController?; 21 | var accountsController:EMAccountsViewController?; 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | let roster = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("Roster") as! RosterViewController 27 | 28 | roster.view.frame = mainView.bounds 29 | mainView.addSubview(roster.view); 30 | addChildViewController(roster); 31 | roster.didMoveToParentViewController(self); 32 | 33 | let message = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("Message") as! MessageViewController 34 | 35 | message.view.frame = secondView.bounds 36 | secondView.addSubview(message.view); 37 | addChildViewController(message); 38 | message.didMoveToParentViewController(self); 39 | 40 | secondView.hidden = true; 41 | backButton.enabled = false; 42 | 43 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "showAccounts", name: EloConstants.SHOW_ACCOUNTS, object: nil); 44 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "showPreferences", name: EloConstants.SHOW_PREFERENCES, object: nil); 45 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "showMessage:", name: EloConstants.ACTIVATE_CONTACT, object: nil) 46 | } 47 | 48 | @IBAction func backClicked(sender: AnyObject) { 49 | UIView.animateWithDuration(0.3, 50 | delay: 0.0, 51 | options: .AllowUserInteraction, 52 | animations: { 53 | self.secondView.frame.origin.x = self.secondView.frame.width-80; 54 | }, 55 | completion: { finished in 56 | self.backButton.enabled = false; 57 | self.titleItem.title = "Eloquence" 58 | }) 59 | 60 | 61 | } 62 | 63 | func close(){ 64 | settingsPopoverController!.dismissPopoverAnimated(false, completion: { 65 | self.popoverControllerDidDismissPopover(self.settingsPopoverController!); 66 | }); 67 | } 68 | 69 | @IBAction func menuClicked(sender: AnyObject) { 70 | 71 | if (settingsPopoverController == nil) 72 | { 73 | let btn = sender as! UIBarButtonItem; 74 | 75 | let view = toolbar.subviews[3]; 76 | 77 | 78 | let settings = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("GlobalMenu") ; 79 | 80 | settingsPopoverController = WYPopoverController(contentViewController: settings); 81 | settingsPopoverController!.delegate = self; 82 | settingsPopoverController!.passthroughViews = [btn]; 83 | settingsPopoverController!.popoverLayoutMargins = UIEdgeInsetsMake(10, 10, 10, 10); 84 | settingsPopoverController!.wantsDefaultContentAppearance = false; 85 | settingsPopoverController!.theme.fillTopColor = UIColor(white: 1, alpha: 1); 86 | 87 | settingsPopoverController!.presentPopoverFromRect( 88 | view.bounds, 89 | inView: view, 90 | permittedArrowDirections:WYPopoverArrowDirection.Any, 91 | animated:true); 92 | 93 | } 94 | else 95 | { 96 | close(); 97 | } 98 | } 99 | 100 | 101 | func showMessage(notification: NSNotification) { 102 | 103 | if(secondView.hidden){ 104 | secondView.hidden = false; 105 | secondView.frame.origin.x = secondView.frame.width; 106 | } 107 | UIView.animateWithDuration(0.3, 108 | delay: 0.0, 109 | options: .AllowUserInteraction, 110 | animations: { 111 | self.secondView.frame.origin.x = 0; 112 | }, 113 | completion: { finished in 114 | self.backButton.enabled = true; 115 | }) 116 | 117 | let chatId = notification.object as! EloChatId 118 | 119 | self.titleItem.title = chatId.to.jid 120 | 121 | } 122 | 123 | func didReceiveChat(msg: EloMessage) { 124 | 125 | } 126 | 127 | 128 | 129 | func popoverControllerDidDismissPopover(controller:WYPopoverController?) { 130 | if(settingsPopoverController != nil){ 131 | settingsPopoverController!.dismissPopoverAnimated(true); 132 | settingsPopoverController!.delegate = nil; 133 | settingsPopoverController = nil; 134 | } 135 | } 136 | 137 | //pragma EMPreferencesViewControllerDelegate 138 | func didClickDoneInPreferencesViewController(){ 139 | if(preferencesController != nil){ 140 | preferencesController!.dismissViewControllerAnimated(true, completion: nil) 141 | preferencesController!.delegate = nil; 142 | preferencesController = nil; 143 | } 144 | } 145 | 146 | //pragma EMAccountsViewControllerDelegate 147 | func didClickDoneInAccountsViewController(){ 148 | if(accountsController != nil){ 149 | accountsController!.dismissViewControllerAnimated(true, completion: nil) 150 | accountsController!.delegate = nil; 151 | accountsController = nil; 152 | } 153 | } 154 | 155 | //private 156 | 157 | func showAccounts() { 158 | popoverControllerDidDismissPopover(settingsPopoverController); 159 | 160 | accountsController = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("Accounts") as? EMAccountsViewController; 161 | accountsController!.delegate = self; 162 | self.presentViewController(accountsController!, animated: true, completion: {}) 163 | } 164 | 165 | func showPreferences() { 166 | popoverControllerDidDismissPopover(settingsPopoverController); 167 | 168 | preferencesController = UIStoryboard.init(name: "Shared", bundle: nil).instantiateViewControllerWithIdentifier("Preferences") as? EMPreferencesViewController; 169 | preferencesController!.delegate = self; 170 | self.presentViewController(preferencesController!, animated: true, completion: {}) 171 | } 172 | 173 | override func preferredStatusBarStyle() -> UIStatusBarStyle { 174 | return UIStatusBarStyle.LightContent; 175 | } 176 | 177 | 178 | } --------------------------------------------------------------------------------