├── DisType ├── app_logo.png ├── Resources │ └── alarm.wav ├── Assets.xcassets │ ├── Contents.json │ ├── more.imageset │ │ ├── more.png │ │ ├── more@2x.png │ │ ├── more@3x.png │ │ └── Contents.json │ ├── phone.imageset │ │ ├── phone.png │ │ ├── phone@2x.png │ │ ├── phone@3x.png │ │ └── Contents.json │ ├── square.imageset │ │ ├── square.png │ │ └── Contents.json │ ├── x-square.imageset │ │ ├── x-square.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-60@3x-1.png │ │ ├── Icon-60@3x-2.png │ │ ├── Icon-76@2x-1.png │ │ ├── Icon-76@2x-2.png │ │ ├── Icon-83.5@2x.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-83.5@2x-1.png │ │ ├── distype_app_1024.png │ │ └── Contents.json │ └── app_logo.imageset │ │ ├── Icon-83.5@2x.png │ │ ├── Icon-83.5@2x-1.png │ │ ├── Icon-83.5@2x-2.png │ │ └── Contents.json ├── Source │ ├── Miscellaneous │ │ └── Constants.swift │ ├── Flow │ │ ├── Feedback │ │ │ ├── FeedbackScreen.swift │ │ │ ├── FeedbackCoordinator.swift │ │ │ └── FeedbackScreen.storyboard │ │ ├── Main │ │ │ ├── MainScreen │ │ │ │ ├── UI │ │ │ │ │ ├── MessageCell │ │ │ │ │ │ └── MessageCell.swift │ │ │ │ │ ├── CategoryCell │ │ │ │ │ │ └── CategoryCell.swift │ │ │ │ │ └── ChatCell │ │ │ │ │ │ └── ChatCell.swift │ │ │ │ ├── Logic │ │ │ │ │ ├── ChatCollection │ │ │ │ │ │ └── ChatCollection.swift │ │ │ │ │ ├── MessageManager │ │ │ │ │ │ └── MessageManager.swift │ │ │ │ │ └── CategoryManager │ │ │ │ │ │ └── CategoryManager.swift │ │ │ │ ├── MainScreen.swift │ │ │ │ └── MainScreen.storyboard │ │ │ ├── MainCoordinatorProtocols.swift │ │ │ └── MainCoordinator.swift │ │ ├── App │ │ │ ├── AppSettingsManager.swift │ │ │ └── AppCoordinator.swift │ │ ├── Menu │ │ │ ├── MenuScreen.swift │ │ │ ├── MenuCoordinator.swift │ │ │ └── MenuScreen.storyboard │ │ └── SelectVoice │ │ │ ├── SelectVoiceScreen.swift │ │ │ ├── SelectVoiceCoordinator.swift │ │ │ └── SelectVoiceScreen.storyboard │ ├── Logic │ │ ├── Feedback.swift │ │ ├── Coordinator.swift │ │ ├── Metrica.swift │ │ ├── BaseCoordinator.swift │ │ ├── AssemblyScreen.swift │ │ ├── AssemblyCoordinator.swift │ │ ├── Router.swift │ │ ├── TTSManager.swift │ │ └── DataBase │ │ │ └── DataBase.swift │ ├── Models │ │ ├── Message.swift │ │ ├── Category.swift │ │ ├── Settings.swift │ │ └── Chat.swift │ └── Extentions │ │ ├── UIColor.swift │ │ ├── View.swift │ │ ├── String.swift │ │ └── ViewController.swift ├── Info.plist ├── AppDelegate.swift └── Base.lproj │ └── LaunchScreen.storyboard ├── Pods ├── Target Support Files │ ├── Pods-DisType │ │ ├── Pods-distype.modulemap │ │ ├── Pods-distype-dummy.m │ │ ├── Pods-distype-umbrella.h │ │ ├── Info.plist │ │ ├── Pods-distype.debug.xcconfig │ │ ├── Pods-distype.release.xcconfig │ │ ├── Pods-distype-frameworks.sh │ │ └── Pods-DisType-resources.sh │ └── Realm │ │ └── Realm.xcconfig └── Manifest.lock ├── distype.xcodeproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Podfile ├── distype.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── DisType.xcodeproj └── project.xcworkspace │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── .gitignore /DisType/app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/app_logo.png -------------------------------------------------------------------------------- /DisType/Resources/alarm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Resources/alarm.wav -------------------------------------------------------------------------------- /DisType/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DisType/Assets.xcassets/more.imageset/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/more.imageset/more.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/phone.imageset/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/phone.imageset/phone.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/more.imageset/more@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/more.imageset/more@2x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/more.imageset/more@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/more.imageset/more@3x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/square.imageset/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/square.imageset/square.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/phone.imageset/phone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/phone.imageset/phone@2x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/phone.imageset/phone@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/phone.imageset/phone@3x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/x-square.imageset/x-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/x-square.imageset/x-square.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/app_logo.imageset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/app_logo.imageset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@3x-1.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@3x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-60@3x-2.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-76@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-76@2x-1.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-76@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-76@2x-2.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/app_logo.imageset/Icon-83.5@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/app_logo.imageset/Icon-83.5@2x-1.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/app_logo.imageset/Icon-83.5@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/app_logo.imageset/Icon-83.5@2x-2.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x-1.png -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/distype_app_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkasu/linkatype-ios/HEAD/DisType/Assets.xcassets/AppIcon.appiconset/distype_app_1024.png -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/Pods-distype.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_DisType { 2 | umbrella header "Pods-DisType-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/Pods-distype-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_DisType : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_DisType 5 | @end 6 | -------------------------------------------------------------------------------- /distype.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | use_frameworks! 4 | 5 | target 'DisType' do 6 | pod 'RealmSwift', '~> 3.0' 7 | pod 'YandexMobileMetrica', '~> 2.9.4' 8 | pod 'Alamofire', '~> 4.5' 9 | end 10 | -------------------------------------------------------------------------------- /distype.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /distype.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DisType.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DisType/Source/Miscellaneous/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // DisType 4 | // 5 | // Created by Grigory on 22/12/2017. 6 | // Copyright © 2017 NixSolutions. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Constants { 12 | struct Sounds { 13 | static let soundName = "alarm.wav" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DisType/Assets.xcassets/square.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "square.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DisType/Assets.xcassets/x-square.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "x-square.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/Pods-distype-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_DisTypeVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_DisTypeVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /DisType/Assets.xcassets/more.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "more.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "more@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "more@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /DisType/Assets.xcassets/phone.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "phone.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "phone@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "phone@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /DisType/Assets.xcassets/app_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon-83.5@2x-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Icon-83.5@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Icon-83.5@2x-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.5.1) 3 | - Realm (3.0.0): 4 | - Realm/Headers (= 3.0.0) 5 | - Realm/Headers (3.0.0) 6 | - RealmSwift (3.0.0): 7 | - Realm (= 3.0.0) 8 | - YandexMobileMetrica (2.9.4): 9 | - YandexMobileMetrica/Static (= 2.9.4) 10 | - YandexMobileMetrica/Static (2.9.4) 11 | 12 | DEPENDENCIES: 13 | - Alamofire (~> 4.5) 14 | - RealmSwift (~> 3.0) 15 | - YandexMobileMetrica (~> 2.9.4) 16 | 17 | SPEC CHECKSUMS: 18 | Alamofire: 2d95912bf4c34f164fdfc335872e8c312acaea4a 19 | Realm: dec0de73be8c57506e39db72694d513c189493b3 20 | RealmSwift: 44a7090b16d1b22a406c6bf129bdd602db1bbecc 21 | YandexMobileMetrica: 8f150e2d04cf34e3af9506a75310a6229bcf05c5 22 | 23 | PODFILE CHECKSUM: 271234c06d8ee6db4e1ac9bcfbec534863d6d83b 24 | 25 | COCOAPODS: 1.3.1 26 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Realm/Realm.xcconfig: -------------------------------------------------------------------------------- 1 | APPLICATION_EXTENSION_API_ONLY = YES 2 | CLANG_CXX_LANGUAGE_STANDARD = c++14 3 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Realm 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YandexMobileMetrica" 6 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Realm/core" 7 | OTHER_CPLUSPLUSFLAGS = -isystem "${PODS_ROOT}/Realm/include/core" 8 | OTHER_LDFLAGS = -l"c++" -l"realmcore-ios" -l"z" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_ROOT = ${SRCROOT} 12 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Realm 13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 14 | SKIP_INSTALL = YES 15 | USER_HEADER_SEARCH_PATHS = "${PODS_ROOT}/Realm/include" "${PODS_ROOT}/Realm/include/Realm" 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Feedback/FeedbackScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FeedbackScreen.swift 3 | // DisType 4 | // 5 | // Created by Mike Kholomeev on 12/12/17. 6 | // Copyright © 2017 NixSolutions. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | protocol FeedbackCoordinatorDelegate { 13 | func didSend(text:String, to email:String?) 14 | func didCloseScreen() 15 | } 16 | 17 | class FeedbackScreen: UIViewController { 18 | 19 | var delegate: FeedbackCoordinatorDelegate? 20 | 21 | @IBOutlet weak var sendButton: UIButton! 22 | @IBOutlet weak var emailTextField: UITextField! 23 | @IBOutlet weak var textTextView: UITextView! 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | sendButton.corner() 28 | } 29 | 30 | override func viewDidDisappear(_ animated: Bool) { 31 | delegate?.didCloseScreen() 32 | } 33 | 34 | @IBAction func send(_ sender: Any) { 35 | delegate?.didSend(text:textTextView.text, to:emailTextField.text) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /DisType/Source/Logic/Feedback.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Feedback.swift 3 | // DisType 4 | // 5 | // Created by Mike Kholomeev on 12/12/17. 6 | // Copyright © 2017 NixSolutions. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | class Feedback { 13 | 14 | let feedbackURL = "http://feedback.aacidov.ru" 15 | 16 | func send(text:String, to email:String) { 17 | var params:Parameters = [:] 18 | 19 | params["email"] = email 20 | params["text"] = text 21 | params["app"] = "distype" 22 | 23 | let start = CACurrentMediaTime() 24 | 25 | Alamofire.request(feedbackURL, method: .post, parameters: params, encoding: JSONEncoding.default, headers: nil).responseJSON { response in 26 | let end = CACurrentMediaTime() 27 | let elapsedTime = end - start 28 | 29 | switch response.result { 30 | case .success: 31 | print(response) 32 | 33 | break 34 | case .failure(let error): 35 | 36 | print(error) 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/Pods-distype.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Realm" "$PODS_CONFIGURATION_BUILD_DIR/RealmSwift" "${PODS_ROOT}/YandexMobileMetrica/static" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YandexMobileMetrica" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Realm/Realm.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/RealmSwift/RealmSwift.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/YandexMobileMetrica" 7 | OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AdSupport" -framework "Alamofire" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "Foundation" -framework "Realm" -framework "RealmSwift" -framework "UIKit" -framework "YandexMobileMetrica" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 12 | PODS_ROOT = ${SRCROOT}/Pods 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/Pods-distype.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Realm" "$PODS_CONFIGURATION_BUILD_DIR/RealmSwift" "${PODS_ROOT}/YandexMobileMetrica/static" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YandexMobileMetrica" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Realm/Realm.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/RealmSwift/RealmSwift.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/YandexMobileMetrica" 7 | OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AdSupport" -framework "Alamofire" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreTelephony" -framework "Foundation" -framework "Realm" -framework "RealmSwift" -framework "UIKit" -framework "YandexMobileMetrica" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 12 | PODS_ROOT = ${SRCROOT}/Pods 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | # Xcode 3 | # 4 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData/ 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata/ 20 | 21 | ## Other 22 | *.moved-aside 23 | *.xcuserstate 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | .build/ 40 | 41 | # CocoaPods 42 | # 43 | # We recommend against adding the Pods directory to your .gitignore. However 44 | # you should judge for yourself, the pros and cons are mentioned at: 45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 46 | # 47 | Pods/ 48 | Podfile.lock 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /DisType/Source/Models/Message.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | 26 | // 27 | // Message.swift 28 | // DisType 29 | // 30 | // Created by Mike Kholomeev on 11/20/17. 31 | // 32 | 33 | import Foundation 34 | import RealmSwift 35 | 36 | class Message: Object { 37 | @objc dynamic var text = "" 38 | // @objc dynamic var categoryId : String = "" 39 | } 40 | -------------------------------------------------------------------------------- /DisType/Source/Logic/Coordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // Coordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | 34 | protocol Coordinator { 35 | func start() 36 | } 37 | 38 | protocol CoordinatorOutput { 39 | var finishFlow: ((Any) -> Void)? { get set } 40 | } 41 | -------------------------------------------------------------------------------- /DisType/Source/Models/Category.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | 26 | // 27 | // Category.swift 28 | // DisType 29 | // 30 | // Created by Mike Kholomeev on 11/20/17. 31 | // 32 | 33 | import Foundation 34 | import RealmSwift 35 | 36 | class Category: Object { 37 | @objc dynamic var name = "" 38 | @objc dynamic var id = NSUUID().uuidString 39 | let messages = List() 40 | } 41 | -------------------------------------------------------------------------------- /DisType/Source/Models/Settings.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // Settings.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/29/17. 30 | // 31 | 32 | import Foundation 33 | import RealmSwift 34 | 35 | class Settings:Object { 36 | @objc dynamic var voiceId: String = "" 37 | @objc dynamic var isUseInternet: Bool = false 38 | @objc dynamic var isSpeakEveryWord: Bool = false 39 | } 40 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/UI/MessageCell/MessageCell.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // MessageCell.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/23/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | class MessageCell: UITableViewCell { 36 | static let id = String(describing:MessageCell.self) 37 | 38 | @objc func delete(row:Int){} 39 | @objc func rename(row:Int){} 40 | } 41 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/UI/CategoryCell/CategoryCell.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // CategoryCell.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/23/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | class CategoryCell: UITableViewCell { 36 | static let id = String(describing:CategoryCell.self) 37 | 38 | @objc func delete(row:Int){} 39 | @objc func rename(row:Int){} 40 | 41 | } 42 | -------------------------------------------------------------------------------- /DisType/Source/Models/Chat.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | 26 | // 27 | // Chat.swift 28 | // DisType 29 | // 30 | // Created by Mike Kholomeev on 11/20/17. 31 | // 32 | 33 | import Foundation 34 | import RealmSwift 35 | 36 | class Chat: Object { 37 | @objc dynamic var name = "" 38 | @objc dynamic var text = "" 39 | @objc dynamic var id = NSUUID().uuidString 40 | 41 | func update(text:String) { 42 | DB.update(self, text:text) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /DisType/Source/Extentions/UIColor.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // UIColor.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/21/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | extension UIColor { 36 | class var dtBlue:UIColor { 37 | return UIColor(red:0.10, green:0.45, blue:0.47, alpha:1.00) 38 | } 39 | class var highlitedCell: UIColor { 40 | return UIColor(red:0.11, green:0.52, blue:0.54, alpha:1.00) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /DisType/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | linka: напиши 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSApplicationCategoryType 24 | 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | MainScreen 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /DisType/Source/Extentions/View.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // View.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | extension UIView { 36 | func corner(radius:CGFloat = 5, color:UIColor = UIColor.white, width:CGFloat = 0.5) { 37 | layer.cornerRadius = radius 38 | layer.borderColor = color.cgColor 39 | layer.borderWidth = width 40 | clipsToBounds = true 41 | } 42 | 43 | func makeRound() { 44 | self.layer.cornerRadius = frame.size.width/2 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /DisType/Source/Flow/App/AppSettingsManager.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // AppPreference.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/29/17. 30 | // 31 | 32 | import Foundation 33 | import RealmSwift 34 | 35 | class AppSettingsManager { 36 | var voiceId:String { return DB.settings.voiceId} 37 | var isUseInternet:Bool { return DB.settings.isUseInternet } 38 | var isSpeakEveryWord:Bool { return DB.settings.isSpeakEveryWord } 39 | 40 | 41 | // MARK: - Public 42 | func useInternet(_ value:Bool) { 43 | DB.update(useInternet: value) 44 | } 45 | func speakEveryWord(_ value:Bool) { 46 | DB.update(speakEveryWord: value) 47 | } 48 | 49 | func voiceId(_ _id:String) { 50 | DB.update(voiceId: _id) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /DisType/Source/Extentions/String.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // String.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/21/17. 30 | // 31 | 32 | import Foundation 33 | 34 | extension String { 35 | var guessLanguage: String { 36 | let length = self.utf16.count 37 | let languageCode = CFStringTokenizerCopyBestStringLanguage(self as CFString, CFRange(location: 0, length: length)) as String? ?? "" 38 | 39 | let locale = Locale(identifier: languageCode) 40 | return locale.localizedString(forLanguageCode: languageCode) ?? "Unknown" 41 | } 42 | 43 | var language: String? { 44 | let tagger = NSLinguisticTagger(tagSchemes: [NSLinguisticTagScheme.language], options: 0) 45 | tagger.string = self 46 | return tagger.tag(at: 0, scheme: NSLinguisticTagScheme.language, tokenRange: nil, sentenceRange: nil).map { $0.rawValue } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /DisType/Source/Logic/Metrica.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // Metrica.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 12/4/17. 30 | // 31 | 32 | import Foundation 33 | import YandexMobileMetrica 34 | 35 | class Metrica { 36 | 37 | init() { 38 | YMMYandexMetrica.activate(withApiKey: "74b3edb9-a12c-4ec6-96e2-817e75e03941") 39 | } 40 | 41 | func saidEvent() { 42 | YMMYandexMetrica.reportEvent("said", onFailure: nil) 43 | } 44 | 45 | func categoryCreateEvent() { 46 | YMMYandexMetrica.reportEvent("create category", onFailure: nil) 47 | } 48 | 49 | func messageCreateEvent() { 50 | YMMYandexMetrica.reportEvent("create statement", onFailure: nil) 51 | } 52 | 53 | func categoryChangeEvent() { 54 | YMMYandexMetrica.reportEvent("change category", onFailure: nil) 55 | } 56 | 57 | func changeSayingAfterWordValueEvent() { 58 | YMMYandexMetrica.reportEvent("say after word status", onFailure: nil) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/UI/ChatCell/ChatCell.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // ChatCell.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/20/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | class ChatCell: UICollectionViewCell { 36 | static let id = String(describing:ChatCell.self) 37 | 38 | @IBOutlet weak var title: UILabel! 39 | @IBOutlet weak var selectedMarkView: UIView! 40 | 41 | override var isSelected: Bool { 42 | didSet { 43 | title.textColor = isSelected ? UIColor.white : UIColor.lightGray 44 | UIView.animate(withDuration: 0.2) { 45 | self.selectedMarkView.isHidden = !self.isSelected 46 | } 47 | } 48 | } 49 | 50 | override var isHighlighted: Bool { 51 | didSet { 52 | title.textColor = isHighlighted ? UIColor.lightGray : title.textColor 53 | UIView.animate(withDuration: 0.2) { 54 | self.backgroundColor = self.isHighlighted ? UIColor.highlitedCell : UIColor.clear 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /DisType/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-60@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-60@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "idiom" : "iphone", 17 | "size" : "29x29", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "size" : "29x29", 22 | "idiom" : "iphone", 23 | "filename" : "Icon-Small@2x.png", 24 | "scale" : "3x" 25 | }, 26 | { 27 | "idiom" : "iphone", 28 | "size" : "40x40", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-60@3x-1.png", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "idiom" : "iphone", 39 | "size" : "60x60", 40 | "scale" : "2x" 41 | }, 42 | { 43 | "size" : "60x60", 44 | "idiom" : "iphone", 45 | "filename" : "Icon-60@3x-2.png", 46 | "scale" : "3x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "1x" 52 | }, 53 | { 54 | "size" : "20x20", 55 | "idiom" : "ipad", 56 | "filename" : "Icon-76@2x.png", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "idiom" : "ipad", 61 | "size" : "29x29", 62 | "scale" : "1x" 63 | }, 64 | { 65 | "size" : "29x29", 66 | "idiom" : "ipad", 67 | "filename" : "Icon-76@2x-1.png", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-76@2x-2.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "idiom" : "ipad", 83 | "size" : "76x76", 84 | "scale" : "1x" 85 | }, 86 | { 87 | "size" : "76x76", 88 | "idiom" : "ipad", 89 | "filename" : "Icon-83.5@2x-1.png", 90 | "scale" : "2x" 91 | }, 92 | { 93 | "size" : "83.5x83.5", 94 | "idiom" : "ipad", 95 | "filename" : "Icon-83.5@2x.png", 96 | "scale" : "2x" 97 | }, 98 | { 99 | "size" : "1024x1024", 100 | "idiom" : "ios-marketing", 101 | "filename" : "distype_app_1024.png", 102 | "scale" : "1x" 103 | } 104 | ], 105 | "info" : { 106 | "version" : 1, 107 | "author" : "xcode" 108 | } 109 | } -------------------------------------------------------------------------------- /DisType/Source/Flow/App/AppCoordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // AppCoordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | 34 | enum AppState { 35 | case main 36 | } 37 | 38 | final class AppCoordinator: BaseCoordinator, Coordinator { 39 | fileprivate let router: Router 40 | fileprivate let assembly: AssemblyCoordinator 41 | 42 | fileprivate var state: AppState 43 | 44 | init(_ router: Router, _ assembly: AssemblyCoordinator) { 45 | self.router = router 46 | self.assembly = assembly 47 | state = .main 48 | } 49 | 50 | func start() { 51 | switch state { 52 | case .main: 53 | runMainFlow() 54 | } 55 | } 56 | 57 | fileprivate func runMainFlow() { 58 | let mainCoordinator = assembly.mainCoordinator 59 | addDependency(mainCoordinator) 60 | 61 | mainCoordinator.finishFlow = { [weak self, weak mainCoordinator] item in 62 | self?.router.dismissTopScreen() 63 | self?.removeDependency(mainCoordinator) 64 | self?.start() 65 | } 66 | 67 | mainCoordinator.start() 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainCoordinatorProtocols.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // MainCoordinatorProtocols.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/23/17. 30 | // 31 | 32 | import Foundation 33 | 34 | protocol HomeDelegate { 35 | func didEntered(_ text:String) 36 | func speak(_ text:String, with languageCode:String?) 37 | func addNewChat() 38 | func chatTextDidChanged(_ _text:String?) 39 | func deleteCurrentChat(_ complition: @escaping (IndexPath)->()) 40 | func beepSound() 41 | func showMenu() 42 | func finish() 43 | } 44 | 45 | protocol CategoryManagerDelegate { 46 | func didSelect(_ category:Category) 47 | func willAddNewCategory(_ complition: @escaping allertReturn) 48 | func didAddNewCategory() 49 | func didDelete(_ category:Category) 50 | func willRename(_ category:Category, complition: @escaping allertReturn) 51 | } 52 | 53 | protocol MessageManagerDelegate { 54 | func currentCategory() -> Category 55 | func didSelect(_ message:Message) 56 | func willAddNewMessage(for category:Category, complition: @escaping allertReturn) 57 | func didAddNewMessage() 58 | func didDelete(_ message:Message) 59 | func willRename(_ message:Message, complition: @escaping allertReturn) 60 | } 61 | -------------------------------------------------------------------------------- /DisType/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // AppDelegate.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import UIKit 33 | 34 | @UIApplicationMain 35 | class AppDelegate: UIResponder, UIApplicationDelegate { 36 | 37 | var window: UIWindow? 38 | var appCoordinator: Coordinator! 39 | var coordinatorAssembly:AssemblyCoordinator! 40 | 41 | fileprivate let metrica: Metrica = Metrica() 42 | 43 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 44 | 45 | let rootViewController = UINavigationController(); 46 | rootViewController.isNavigationBarHidden = true 47 | application.statusBarStyle = .lightContent 48 | 49 | let router = Router(navController:rootViewController) 50 | coordinatorAssembly = AssemblyCoordinator(router, metrica: metrica) 51 | appCoordinator = coordinatorAssembly.appCoordinator 52 | 53 | window = UIWindow.init(frame:UIScreen.main.bounds); 54 | window?.rootViewController = rootViewController 55 | window?.makeKeyAndVisible() 56 | 57 | appCoordinator.start() 58 | 59 | return true 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /DisType/Source/Extentions/ViewController.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // ViewController.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | extension UIViewController 36 | { 37 | class func instantiateFromStoryboard() -> Self 38 | { 39 | return instantiateFromStoryboardHelper(type: self, storyboardName: String(describing: self)) 40 | } 41 | 42 | class func instantiateFromStoryboard(storyboardName: String) -> Self 43 | { 44 | return instantiateFromStoryboardHelper(type: self, storyboardName: storyboardName) 45 | } 46 | 47 | private class func instantiateFromStoryboardHelper(type: T.Type, storyboardName: String) -> T 48 | { 49 | // var storyboardId = "" 50 | // let components = "\(type(of: type))".components(separatedBy:".") 51 | // 52 | // if components.count > 1 53 | // { 54 | // storyboardId = components[1] 55 | // } 56 | 57 | let storyboad = UIStoryboard(name: storyboardName, bundle: nil) 58 | let controller = storyboad.instantiateViewController(withIdentifier: storyboardName) as! T 59 | 60 | return controller 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DisType/Source/Logic/BaseCoordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // BaseCoordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | 34 | class BaseCoordinator:Equatable { 35 | var childCoordinators: [BaseCoordinator] = [] 36 | 37 | func addDependency(_ coordinator: BaseCoordinator) { 38 | guard notContains(coordinator) else { return } 39 | 40 | childCoordinators.append(coordinator) 41 | } 42 | 43 | func removeDependency(_ coordinator: BaseCoordinator?) { 44 | guard 45 | let coordinator = coordinator, 46 | let index = childCoordinators.index(of: coordinator) 47 | else { return } 48 | 49 | childCoordinators.remove(at: index) 50 | } 51 | 52 | // MARK: - Equatable 53 | static func ==(lhs: BaseCoordinator, rhs: BaseCoordinator) -> Bool { 54 | return type(of: lhs) == type(of: rhs) 55 | } 56 | 57 | // MARK: - Private 58 | fileprivate func contains(_ coordinator:BaseCoordinator) -> Bool { 59 | return childCoordinators.contains{ $0 == coordinator } 60 | } 61 | fileprivate func notContains(_ coordinator:BaseCoordinator) -> Bool { 62 | return !contains(coordinator) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Feedback/FeedbackCoordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // FeedbackCoordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 12/12/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | class FeedbackCoordinator: BaseCoordinator, Coordinator, CoordinatorOutput, FeedbackCoordinatorDelegate { 36 | 37 | var finishFlow: ((Any) -> Void)? 38 | 39 | fileprivate let router: Router 40 | fileprivate let assembly:AssemblyCoordinator 41 | fileprivate let screenAssembly: AssemblyScreen 42 | fileprivate let feedback: Feedback 43 | fileprivate let sourceView: UIView? 44 | 45 | init(_ router: Router, assembly: AssemblyCoordinator, screenAssembly: AssemblyScreen, feedback: Feedback, sourceView: UIView) { 46 | self.router = router 47 | self.assembly = assembly 48 | self.screenAssembly = screenAssembly 49 | self.feedback = feedback 50 | self.sourceView = sourceView 51 | } 52 | 53 | fileprivate lazy var feedbackVC: FeedbackScreen = { 54 | let vc = self.screenAssembly.feedbackScreen(delegate:self) 55 | return vc 56 | }() 57 | 58 | func start() { 59 | router.presentModaly(feedbackVC, sourceView:sourceView) 60 | } 61 | 62 | // MARK: - FeedbackCoordinatorDelegate 63 | internal func didSend(text: String, to email: String?) { 64 | guard let _email = email else { return } 65 | feedback.send(text:text, to:_email) 66 | // feedbackVC.dismiss(animated: true, completion: nil) 67 | finishFlow?(true) 68 | } 69 | 70 | internal func didCloseScreen() { 71 | finishFlow?(false) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /DisType/Source/Logic/AssemblyScreen.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // AssemblyScreen.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import MessageUI 34 | 35 | class AssemblyScreen { 36 | 37 | func mainScreen(delegate:MainCoordinator, 38 | chatCollection:ChatCollection, 39 | categoryManager:CategoryManager, 40 | messageManager:MessageManager) -> MainScreen { 41 | let vc = MainScreen.instantiateFromStoryboard() 42 | vc.messageDelegate = messageManager 43 | vc.categoryDelegate = categoryManager 44 | vc.chatDelegate = chatCollection 45 | vc.delegate = delegate 46 | return vc 47 | } 48 | 49 | 50 | func menuScreen(delegate:MenuCoordinator) -> MenuScreen { 51 | let vc = MenuScreen.instantiateFromStoryboard() 52 | vc.delegate = delegate 53 | return vc 54 | } 55 | 56 | func selectVoiceScreen(delegate:SelectVoiceCoordinator) -> SelectVoiceScreen { 57 | let vc = SelectVoiceScreen.instantiateFromStoryboard() 58 | vc.delegate = delegate 59 | return vc 60 | } 61 | 62 | func feedbackScreen(delegate:FeedbackCoordinatorDelegate) -> FeedbackScreen { 63 | let vc = FeedbackScreen.instantiateFromStoryboard() 64 | vc.delegate = delegate 65 | return vc 66 | } 67 | 68 | // func sendFeedback() -> MFMailComposeViewController? { 69 | // guard 70 | // MFMailComposeViewController.canSendMail() 71 | // else { 72 | // print("Mail services are not available") 73 | // return nil 74 | // } 75 | // 76 | // let composeVC = MFMailComposeViewController() 77 | // 78 | // // Configure the fields of the interface. 79 | // composeVC.setSubject("DisType-Pro feedback") 80 | // 81 | // return composeVC 82 | // } 83 | } 84 | -------------------------------------------------------------------------------- /DisType/Source/Logic/AssemblyCoordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // AssemblyCoordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | class AssemblyCoordinator { 36 | fileprivate let router: Router 37 | fileprivate let screenAssembly = AssemblyScreen() 38 | fileprivate let appPreference = AppSettingsManager() 39 | fileprivate let ttsManager: TTSManager 40 | fileprivate let metrica: Metrica 41 | fileprivate let feedback = Feedback() 42 | 43 | init(_ router:Router, metrica:Metrica) { 44 | self.router = router 45 | self.metrica = metrica 46 | ttsManager = TTSManager(appPreference:appPreference) 47 | } 48 | 49 | lazy var appCoordinator: AppCoordinator = { 50 | return AppCoordinator(router, self) 51 | }() 52 | 53 | lazy var mainCoordinator: MainCoordinator = { 54 | let coordinator = MainCoordinator(router, assembly:self, screenAssembly:screenAssembly, appPreference:appPreference, ttsManager:ttsManager, metrica:metrica) 55 | return coordinator 56 | }() 57 | 58 | lazy var menuCoordinator: MenuCoordinator = { 59 | let coordinator = MenuCoordinator(router, assembly:self, screenAssembly:screenAssembly, appPreference:appPreference, ttsManager:ttsManager, metrica:metrica, sourceView:mainCoordinator.sourceView) 60 | return coordinator 61 | }() 62 | 63 | lazy var selectVoiceCoordinator: SelectVoiceCoordinator = { 64 | let coordinator = SelectVoiceCoordinator(router, assembly:self, screenAssembly:screenAssembly, appPreference:appPreference, ttsManager:ttsManager, sourceView:mainCoordinator.sourceView) 65 | return coordinator 66 | }() 67 | 68 | lazy var feedbackCoordinator: FeedbackCoordinator = { 69 | let coordinator = FeedbackCoordinator(router, assembly:self, screenAssembly:screenAssembly, feedback: feedback, sourceView:mainCoordinator.sourceView) 70 | return coordinator 71 | }() 72 | 73 | } 74 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Menu/MenuScreen.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // MenuScreen.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/29/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | protocol MenuScreenDelegate { 36 | func isUseInternet() -> Bool 37 | func isSpeakEveryWord() -> Bool 38 | func saveVoice() 39 | func sendFeedback() 40 | func selectVoice() 41 | func useInternetToggle(_ value:Bool) 42 | func speakAfterEveryWordToggle(_ value:Bool) 43 | } 44 | 45 | class MenuScreen: UIViewController { 46 | 47 | var delegate:MenuScreenDelegate? 48 | 49 | @IBOutlet weak var useInternetButton: UIButton! 50 | @IBOutlet weak var useInternetCheckBox: UIButton! 51 | @IBOutlet weak var speakAfterEveryWordButton: UIButton! 52 | @IBOutlet weak var speakAfterEveryWordCheckBox: UIButton! 53 | @IBOutlet weak var screenHeightConstraint: NSLayoutConstraint! 54 | 55 | override func viewDidLoad() { 56 | super.viewDidLoad() 57 | 58 | let width = UIApplication.shared.delegate?.window??.bounds.width 59 | preferredContentSize = CGSize(width:width!, height:screenHeightConstraint.constant) 60 | } 61 | 62 | override func viewWillAppear(_ animated: Bool) { 63 | speakAfterEveryWordCheckBox.isSelected = delegate!.isSpeakEveryWord() 64 | super.viewWillAppear(animated) 65 | } 66 | 67 | // MARK: - Actions 68 | @IBAction func saveVoice(_ sender: Any) { 69 | delegate?.saveVoice() 70 | } 71 | @IBAction func sendFeedback(_ sender: Any) { 72 | delegate?.sendFeedback() 73 | } 74 | @IBAction func selectVoice(_ sender: Any) { 75 | delegate?.selectVoice() 76 | } 77 | @IBAction func useInternetToggle(_ sender: Any) { 78 | useInternetCheckBox.isSelected = !useInternetCheckBox.isSelected 79 | delegate?.useInternetToggle(useInternetCheckBox.isSelected) 80 | } 81 | @IBAction func speakAfterEveryWordToggle(_ sender: Any) { 82 | speakAfterEveryWordCheckBox.isSelected = !speakAfterEveryWordCheckBox.isSelected 83 | delegate?.speakAfterEveryWordToggle(speakAfterEveryWordCheckBox.isSelected) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /DisType/Source/Logic/Router.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // Router.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | class Router:NSObject, UIPopoverPresentationControllerDelegate { 36 | fileprivate let navController: UINavigationController 37 | fileprivate var modalVC:UIViewController? 38 | 39 | init(navController:UINavigationController) { 40 | self.navController = navController 41 | } 42 | 43 | func presentModaly(_ vc:UIViewController, sourceView:UIView? = nil, barButtonItem:UIBarButtonItem? = nil, animated:Bool = true) { 44 | vc.modalPresentationStyle = UIModalPresentationStyle.popover 45 | 46 | // set up the popover presentation controller 47 | vc.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.init(rawValue: 0) 48 | vc.popoverPresentationController?.delegate = self 49 | if let barItem = barButtonItem { 50 | vc.popoverPresentationController?.barButtonItem = barItem 51 | } else if let view = sourceView { 52 | vc.popoverPresentationController?.sourceView = view 53 | } else { 54 | assertionFailure("Not specified sourceView or barButtonItem for menu popover") 55 | } 56 | 57 | // present the popover 58 | modalVC = vc 59 | navController.present(vc, animated: animated, completion: { 60 | vc.view.superview?.layer.cornerRadius = 4 61 | }) 62 | } 63 | 64 | func push(_ vc:UIViewController, animated:Bool = true) { 65 | navController.pushViewController(vc, animated: animated) 66 | } 67 | func dismissTopScreen(animated:Bool = true) { 68 | navController.popViewController(animated: animated) 69 | } 70 | 71 | func dismissPopoverScreen(animated:Bool = true) { 72 | navController.presentedViewController?.dismiss(animated: animated, completion: nil) 73 | } 74 | 75 | // MARK: - UIPopoverPresentationControllerDelegate 76 | func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle { 77 | return UIModalPresentationStyle.none 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /DisType/Source/Logic/TTSManager.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // TTSManager.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/30/17. 30 | // 31 | 32 | import Foundation 33 | import AVFoundation 34 | 35 | class TTSManager { 36 | fileprivate var languageCode:String? = "ru-RU" 37 | fileprivate let appPreference: AppSettingsManager 38 | 39 | var selectedVoice:AVSpeechSynthesisVoice { 40 | didSet { 41 | // guard let selectedVoice = selectedVoice else { appPreference.voiceId(""); return } 42 | appPreference.voiceId(selectedVoice.identifier) 43 | } 44 | } 45 | 46 | init(appPreference: AppSettingsManager) { 47 | self.appPreference = appPreference 48 | 49 | let voiceId = appPreference.voiceId 50 | if let voice = AVSpeechSynthesisVoice(identifier: voiceId) { 51 | selectedVoice = voice 52 | } else { 53 | let voices = AVSpeechSynthesisVoice.speechVoices() 54 | let voice = voices.filter({ $0.language == "ru-RU" }).first 55 | selectedVoice = voice! 56 | } 57 | } 58 | 59 | fileprivate func voices(for language: String? = nil) -> [AVSpeechSynthesisVoice] { 60 | let voices = AVSpeechSynthesisVoice.speechVoices() 61 | languageCode = language 62 | guard let code = language else { return voices } 63 | 64 | let languageVoices = voices.filter { $0.language == code } 65 | return languageVoices 66 | } 67 | 68 | public func voicesNamesAndIDs(for language: String? = "ru-RU") -> [String : String] { 69 | let ids = voices(for: language).reduce([:]) { (result, obj) -> [String : String] in 70 | var acc = result 71 | acc[obj.name] = obj.identifier 72 | return acc 73 | } 74 | 75 | return ids 76 | } 77 | 78 | public func select(voiceID id:String) { 79 | guard let voice = voices(for: languageCode).filter({$0.identifier==id}).first else { return } 80 | selectedVoice = voice 81 | } 82 | 83 | public func speak(_ text: String, with languageCode:String? = "ru_RU") { 84 | let utterance = AVSpeechUtterance(string: text) 85 | utterance.voice = selectedVoice 86 | let synth = AVSpeechSynthesizer() 87 | synth.speak(utterance) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /DisType/Source/Flow/SelectVoice/SelectVoiceScreen.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // SelectVoiceScreen.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/30/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | protocol SelectVoiceScreenDelegate { 36 | func voicesNamesIDs() -> [String:String] 37 | func selectedVoiceName() -> String 38 | func didSelect(_ position: Int) 39 | func didCloseScreen() 40 | } 41 | 42 | class SelectVoiceScreen: UIViewController, UITableViewDelegate, UITableViewDataSource { 43 | 44 | var delegate:SelectVoiceScreenDelegate? 45 | 46 | fileprivate var voicesIDs:[String:String] = [:] 47 | fileprivate var selectedVoiceName:String = "" 48 | 49 | override func viewDidLoad() { 50 | super.viewDidLoad() 51 | voicesIDs = delegate?.voicesNamesIDs() ?? [:] 52 | selectedVoiceName = delegate?.selectedVoiceName() ?? "" 53 | let height = 44 * voicesIDs.count > 400 ? 400 : 44 * voicesIDs.count 54 | preferredContentSize = CGSize(width: 400, height: height) 55 | } 56 | 57 | override func viewDidDisappear(_ animated: Bool) { 58 | delegate?.didCloseScreen() 59 | } 60 | 61 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 62 | return voicesIDs.count 63 | } 64 | 65 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 66 | guard let cell = tableView.dequeueReusableCell(withIdentifier: LanguageCell.id, for: indexPath) as? LanguageCell else { return UITableViewCell()} 67 | 68 | let name = Array(voicesIDs.keys)[indexPath.row] 69 | cell.textLabel?.text = name 70 | cell.isSelected = (name == selectedVoiceName) 71 | 72 | return cell 73 | } 74 | 75 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 76 | delegate?.didSelect(indexPath.row) 77 | tableView.cellForRow(at: indexPath)?.isSelected = true 78 | } 79 | 80 | } 81 | 82 | class LanguageCell: UITableViewCell { 83 | static let id = String(describing:LanguageCell.self) 84 | 85 | override func setSelected(_ selected: Bool, animated: Bool) { 86 | super.setSelected(selected, animated: animated) 87 | self.accessoryType = selected ? .checkmark : .none 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /DisType/Source/Flow/SelectVoice/SelectVoiceCoordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // SelectVoiceCoordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/30/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | class SelectVoiceCoordinator: BaseCoordinator, Coordinator, CoordinatorOutput, SelectVoiceScreenDelegate { 36 | 37 | // MARK: - CoordinatorOutput 38 | internal var finishFlow: ((Any) -> Void)? 39 | 40 | // MARK: - Init 41 | fileprivate let router: Router 42 | fileprivate let assembly:AssemblyCoordinator 43 | fileprivate let screenAssembly: AssemblyScreen 44 | fileprivate let appPreference: AppSettingsManager 45 | fileprivate let ttsManager: TTSManager 46 | fileprivate let sourceView: UIView? 47 | 48 | 49 | fileprivate var menuSelection:MenuSelection? 50 | fileprivate var voices: [String: String] = [:] 51 | 52 | fileprivate lazy var selectVoiceVC:SelectVoiceScreen = { 53 | let vc = self.screenAssembly.selectVoiceScreen(delegate:self) 54 | return vc 55 | }() 56 | 57 | init(_ router: Router, assembly: AssemblyCoordinator, screenAssembly:AssemblyScreen, appPreference:AppSettingsManager, ttsManager: TTSManager, sourceView: UIView? = nil) { 58 | self.router = router 59 | self.assembly = assembly 60 | self.screenAssembly = screenAssembly 61 | self.appPreference = appPreference 62 | self.sourceView = sourceView 63 | self.ttsManager = ttsManager 64 | voices = ttsManager.voicesNamesAndIDs(for: nil) 65 | } 66 | 67 | // MARK: - Coordinator 68 | internal func start() { 69 | router.presentModaly(selectVoiceVC, sourceView: sourceView) 70 | } 71 | 72 | // MARK: - SelectVoiceScreenDelegate 73 | internal func voicesNamesIDs() -> [String:String] { 74 | return voices 75 | } 76 | 77 | internal func selectedVoiceName() -> String { 78 | // guard let name = ttsManager.selectedVoice?.name else { return "" } 79 | let name = ttsManager.selectedVoice.name 80 | return name 81 | } 82 | 83 | internal func didSelect(_ position: Int) { 84 | let name = Array(voices.keys)[position] 85 | ttsManager.select(voiceID: voices[name]!) 86 | } 87 | 88 | internal func didCloseScreen() { 89 | finishFlow?("") 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Menu/MenuCoordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // MenuCoordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/29/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | enum MenuSelection { 36 | case saveVoice, sendFeedback, selectVoice//, useInternet, speakEveryWord 37 | } 38 | 39 | class MenuCoordinator: BaseCoordinator, MenuScreenDelegate { 40 | 41 | var finishFlow: ((Any) -> Void)? 42 | 43 | fileprivate let router: Router 44 | fileprivate let assembly:AssemblyCoordinator 45 | fileprivate let screenAssembly: AssemblyScreen 46 | fileprivate let appPreference: AppSettingsManager 47 | fileprivate let ttsManager: TTSManager 48 | fileprivate let metrica: Metrica 49 | fileprivate let sourceView: UIView? 50 | fileprivate let barButtonitem: UIBarButtonItem? 51 | 52 | fileprivate var menuSelection:MenuSelection? 53 | 54 | init(_ router: Router, assembly: AssemblyCoordinator, screenAssembly:AssemblyScreen, appPreference:AppSettingsManager, ttsManager: TTSManager, metrica: Metrica, sourceView: UIView? = nil, barButtonitem:UIBarButtonItem? = nil) { 55 | self.router = router 56 | self.assembly = assembly 57 | self.screenAssembly = screenAssembly 58 | self.appPreference = appPreference 59 | self.sourceView = sourceView 60 | self.barButtonitem = barButtonitem 61 | self.ttsManager = ttsManager 62 | self.metrica = metrica 63 | } 64 | 65 | fileprivate lazy var menuVC:MenuScreen = { 66 | let vc = self.screenAssembly.menuScreen(delegate:self) 67 | return vc 68 | }() 69 | 70 | // MARK: - Public 71 | func start() { 72 | router.presentModaly(menuVC, sourceView: sourceView, barButtonItem:barButtonitem) 73 | } 74 | 75 | // MARK: - MenuScreenDelegate 76 | func isUseInternet() -> Bool { 77 | return appPreference.isUseInternet 78 | } 79 | func isSpeakEveryWord() -> Bool { 80 | metrica.changeSayingAfterWordValueEvent() 81 | return appPreference.isSpeakEveryWord 82 | } 83 | 84 | func saveVoice() { 85 | // menuVC.dismiss(animated: true, completion: nil) 86 | finishFlow?(MenuSelection.saveVoice) 87 | } 88 | 89 | func sendFeedback() { 90 | // menuVC.dismiss(animated: true, completion: nil) 91 | finishFlow?(MenuSelection.sendFeedback) 92 | } 93 | 94 | func selectVoice() { 95 | // menuVC.dismiss(animated: true, completion: nil) 96 | finishFlow?(MenuSelection.selectVoice) 97 | } 98 | 99 | func useInternetToggle(_ value:Bool) { 100 | appPreference.useInternet(value) 101 | } 102 | 103 | func speakAfterEveryWordToggle(_ value:Bool) { 104 | appPreference.speakEveryWord(value) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/Logic/ChatCollection/ChatCollection.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // ChatCollection.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | protocol ChatCollectionDelegate { 36 | func didSelect(_ chat:Chat) 37 | } 38 | 39 | class ChatCollection: NSObject, UICollectionViewDelegate, UICollectionViewDataSource { 40 | let delegate:ChatCollectionDelegate 41 | var collectionView:UICollectionView? 42 | 43 | var selectedIndexPath:IndexPath { 44 | didSet { selectedIndex = selectedIndexPath.row } 45 | } 46 | 47 | var selectedIndex:Int = 0 { 48 | didSet { delegate.didSelect(DB.chats[selectedIndex]) } 49 | } 50 | 51 | init(delegate:ChatCollectionDelegate) { 52 | self.delegate = delegate 53 | selectedIndexPath = IndexPath(row:0, section:0) 54 | super.init() 55 | } 56 | 57 | // MARK: - Public 58 | func updateLastCell() { 59 | let indexPath = IndexPath(row: DB.chats.count - 1, section: 0) 60 | collectionView?.performBatchUpdates({ 61 | collectionView?.insertItems(at: [indexPath]) 62 | }, completion: nil) 63 | } 64 | 65 | func updateSelectedIndex() { 66 | let chatsCount = DB.chats.count 67 | guard selectedIndex >= chatsCount else { return } 68 | 69 | selectedIndexPath = IndexPath(row:chatsCount - 1, section:0) 70 | } 71 | 72 | // MARK: - Private 73 | fileprivate func selectCell(at collectionView:UICollectionView) { 74 | collectionView.selectItem(at: selectedIndexPath, animated: false, scrollPosition: .centeredVertically) 75 | delegate.didSelect(DB.chats[selectedIndex]) 76 | } 77 | 78 | // MARK: - UICollectionViewDelegate, UICollectionViewDataSource 79 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 80 | self.collectionView = collectionView 81 | let chatsCount = DB.chats.count 82 | updateSelectedIndex() 83 | return chatsCount 84 | } 85 | 86 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 87 | guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier:ChatCell.id, for: indexPath) as? ChatCell else { return UICollectionViewCell()} 88 | cell.title.text = DB.chats[indexPath.row].name 89 | cell.isSelected = (selectedIndexPath == indexPath) 90 | if (selectedIndexPath == indexPath) { 91 | selectCell(at: collectionView) 92 | } 93 | return cell 94 | } 95 | 96 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 97 | selectedIndexPath = indexPath 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/Pods-distype-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 12 | 13 | install_framework() 14 | { 15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 16 | local source="${BUILT_PRODUCTS_DIR}/$1" 17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 19 | elif [ -r "$1" ]; then 20 | local source="$1" 21 | fi 22 | 23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 24 | 25 | if [ -L "${source}" ]; then 26 | echo "Symlinked..." 27 | source="$(readlink "${source}")" 28 | fi 29 | 30 | # Use filter instead of exclude so missing patterns don't throw errors. 31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 33 | 34 | local basename 35 | basename="$(basename -s .framework "$1")" 36 | binary="${destination}/${basename}.framework/${basename}" 37 | if ! [ -r "$binary" ]; then 38 | binary="${destination}/${basename}" 39 | fi 40 | 41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 43 | strip_invalid_archs "$binary" 44 | fi 45 | 46 | # Resign the code if required by the build settings to avoid unstable apps 47 | code_sign_if_enabled "${destination}/$(basename "$1")" 48 | 49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 51 | local swift_runtime_libs 52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 53 | for lib in $swift_runtime_libs; do 54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 56 | code_sign_if_enabled "${destination}/${lib}" 57 | done 58 | fi 59 | } 60 | 61 | # Copies the dSYM of a vendored framework 62 | install_dsym() { 63 | local source="$1" 64 | if [ -r "$source" ]; then 65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\"" 66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}" 67 | fi 68 | } 69 | 70 | # Signs a framework with the provided identity 71 | code_sign_if_enabled() { 72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 73 | # Use the current code_sign_identitiy 74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 76 | 77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 78 | code_sign_cmd="$code_sign_cmd &" 79 | fi 80 | echo "$code_sign_cmd" 81 | eval "$code_sign_cmd" 82 | fi 83 | } 84 | 85 | # Strip invalid architectures 86 | strip_invalid_archs() { 87 | binary="$1" 88 | # Get architectures for current file 89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 90 | stripped="" 91 | for arch in $archs; do 92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 93 | # Strip non-valid architectures in-place 94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 95 | stripped="$stripped $arch" 96 | fi 97 | done 98 | if [[ "$stripped" ]]; then 99 | echo "Stripped $binary of architectures:$stripped" 100 | fi 101 | } 102 | 103 | 104 | if [[ "$CONFIGURATION" == "Debug" ]]; then 105 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 106 | install_framework "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework" 107 | install_framework "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework" 108 | fi 109 | if [[ "$CONFIGURATION" == "Release" ]]; then 110 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 111 | install_framework "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework" 112 | install_framework "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework" 113 | fi 114 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 115 | wait 116 | fi 117 | -------------------------------------------------------------------------------- /DisType/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 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-DisType/Pods-DisType-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /DisType/Source/Flow/SelectVoice/SelectVoiceScreen.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 | 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 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/Logic/MessageManager/MessageManager.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // MessageTable.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | import RealmSwift 35 | 36 | enum StaticMessageCells:Int { 37 | case addMessage, total 38 | 39 | var text:String { 40 | switch self { 41 | case .addMessage: 42 | return "Добавить высказывание" 43 | case .total: 44 | return "" 45 | } 46 | } 47 | } 48 | 49 | 50 | class MessageManager: NSObject, UITableViewDelegate, UITableViewDataSource { 51 | let delegate:MessageManagerDelegate 52 | var tableView:UITableView? 53 | var category:Category { 54 | didSet { 55 | UIView.animate(withDuration: 0.1) { 56 | self.tableView?.reloadData() 57 | } 58 | } 59 | } 60 | 61 | var messages: List { 62 | return category.messages 63 | } 64 | 65 | var messagesCount:Int { 66 | let count = messages.count 67 | return count + StaticMessageCells.total.rawValue 68 | } 69 | 70 | var lastIndex:Int { 71 | return messagesCount - 1 72 | } 73 | 74 | init(delegate:MessageManagerDelegate) { 75 | self.delegate = delegate 76 | category = delegate.currentCategory() 77 | super.init() 78 | } 79 | 80 | // MARK: - Private 81 | fileprivate func addMessage(with text:String) { 82 | guard text.count > 0 else { return } 83 | delegate.didAddNewMessage() 84 | let message = Message() 85 | message.text = text 86 | DB.add(message, to:category) 87 | update(message) 88 | } 89 | 90 | fileprivate func delete(row:Int) { 91 | guard UIMenuController.shared.isMenuVisible 92 | else { return } 93 | let message = messages[row] 94 | DB.delete(message) 95 | tableView?.deleteRows(at: [IndexPath(row:row, section:0)], with: .fade) 96 | } 97 | 98 | fileprivate func rename(row:Int){ 99 | guard UIMenuController.shared.isMenuVisible 100 | else { return } 101 | let message = messages[row] 102 | delegate.willRename(message) { name in 103 | DB.update(message, text: name) 104 | self.tableView?.reloadRows(at: [IndexPath(row:row, section:0)], with: .fade) 105 | } 106 | } 107 | 108 | fileprivate func update(_ message:Message) { 109 | guard let index = messages.index(of: message) else { return } 110 | let indexPath = IndexPath(row: index, section: 0) 111 | tableView?.insertRows(at: [indexPath], with: .fade) 112 | } 113 | 114 | // MARK: - UITableViewDelegate, UITableViewDataSource 115 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 116 | self.tableView = tableView 117 | return messagesCount 118 | } 119 | 120 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 121 | let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) 122 | 123 | let text:String 124 | let index = indexPath.row 125 | 126 | switch index { 127 | case lastIndex: 128 | text = StaticMessageCells.addMessage.text 129 | default: 130 | text = messages[index].text 131 | } 132 | 133 | cell.textLabel?.text = text 134 | return cell 135 | } 136 | 137 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 138 | switch indexPath.row { 139 | case lastIndex : 140 | delegate.willAddNewMessage(for:category) { self.addMessage(with: $0) } 141 | default: 142 | let message = messages[indexPath.row] 143 | delegate.didSelect(message) 144 | } 145 | } 146 | 147 | func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool { 148 | return indexPath.row < lastIndex 149 | } 150 | 151 | func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool { 152 | if action == #selector(MessageCell.delete(row:)) { 153 | delete(row: indexPath.row) 154 | } else if action == #selector(MessageCell.rename(row:)) { 155 | rename(row: indexPath.row) 156 | } else { return false} 157 | 158 | return true 159 | } 160 | 161 | func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) { 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /DisType/Source/Logic/DataBase/DataBase.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // DataBase.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/20/17. 30 | // 31 | 32 | import Foundation 33 | import RealmSwift 34 | 35 | let DB = DataBase() 36 | 37 | class DataBase { 38 | fileprivate static let noCategoryCategoryName = "Без категории" 39 | 40 | fileprivate let minChatCount = 3 41 | fileprivate let chatName = "ЧАТ" 42 | 43 | fileprivate let noCategoryCategory:Category 44 | 45 | fileprivate var realm :Realm { return try! Realm() } 46 | 47 | var settings: Settings { 48 | guard let settings = realm.objects(Settings.self).first 49 | else { 50 | initSettings() 51 | return self.settings 52 | } 53 | return settings 54 | } 55 | 56 | var chats: Results { 57 | return realm.objects(Chat.self).sorted(byKeyPath: #keyPath(Chat.name)) 58 | } 59 | 60 | var categories = List() 61 | 62 | // MARK: - INIT 63 | init() { 64 | if let version = try? schemaVersionAtURL(Realm.Configuration.defaultConfiguration.fileURL!) { 65 | let config = Realm.Configuration(schemaVersion: version + 1) 66 | Realm.Configuration.defaultConfiguration = config 67 | } 68 | 69 | let realm = try! Realm() 70 | let categories = realm.objects(Category.self) 71 | 72 | if !categories.isEmpty, 73 | let category = categories.first(where:{ $0.id == DataBase.noCategoryCategoryName}) { 74 | noCategoryCategory = category 75 | } else { 76 | noCategoryCategory = Category() 77 | noCategoryCategory.name = DataBase.noCategoryCategoryName 78 | noCategoryCategory.id = DataBase.noCategoryCategoryName 79 | self.add(noCategoryCategory) 80 | } 81 | 82 | updateCategoriesList() 83 | initChats() 84 | initSettings() 85 | } 86 | 87 | fileprivate func updateCategoriesList() { 88 | let firstIndex = 0 89 | let categories = List() 90 | let objs = realm.objects(Category.self) 91 | objs.forEach {categories.append($0)} 92 | 93 | if !categories.isEmpty, 94 | let index = categories.index(of: noCategoryCategory), 95 | index != firstIndex { 96 | categories.move(from: index, to:firstIndex) 97 | } 98 | 99 | self.categories = categories 100 | } 101 | 102 | fileprivate func initChats() { 103 | let chatsCount = self.chats.count 104 | if chatsCount < minChatCount { 105 | var count = chatsCount + 1 106 | while count <= minChatCount { 107 | let chat = Chat() 108 | chat.name = "\(chatName)\(count)" 109 | self.add(chat) 110 | count += 1 111 | } 112 | } 113 | } 114 | 115 | fileprivate func initSettings() { 116 | guard realm.objects(Settings.self).first == nil else { return } 117 | let settings = Settings() 118 | realm.beginWrite() 119 | realm.add(settings) 120 | try! realm.commitWrite() 121 | } 122 | 123 | // MARK: - Public 124 | // MARK: - Add 125 | func addNewChat() { 126 | let chat = Chat() 127 | chat.name = "\(chatName)\(DB.chats.count+1)" 128 | DB.add(chat) 129 | } 130 | 131 | func add(_ chat:Chat) { 132 | realm.beginWrite() 133 | realm.add(chat) 134 | try! realm.commitWrite() 135 | } 136 | 137 | func add(_ category:Category) { 138 | realm.beginWrite() 139 | realm.add(category) 140 | try! realm.commitWrite() 141 | updateCategoriesList() 142 | } 143 | 144 | func add(_ message:Message, to category:Category) { 145 | realm.beginWrite() 146 | category.messages.append(message) 147 | try! realm.commitWrite() 148 | } 149 | 150 | // MARK: - Update 151 | func update(_ chat:Chat, text:String) { 152 | realm.beginWrite() 153 | chat.text = text 154 | try! realm.commitWrite() 155 | } 156 | func update(_ category:Category, name:String) { 157 | realm.beginWrite() 158 | category.name = name 159 | try! realm.commitWrite() 160 | updateCategoriesList() 161 | } 162 | func update(_ message:Message, text:String) { 163 | realm.beginWrite() 164 | message.text = text 165 | try! realm.commitWrite() 166 | } 167 | 168 | func update(useInternet:Bool) { 169 | realm.beginWrite() 170 | settings.isUseInternet = useInternet 171 | try! realm.commitWrite() 172 | } 173 | 174 | func update(speakEveryWord:Bool) { 175 | realm.beginWrite() 176 | settings.isSpeakEveryWord = speakEveryWord 177 | try! realm.commitWrite() 178 | } 179 | func update(voiceId:String) { 180 | realm.beginWrite() 181 | settings.voiceId = voiceId 182 | try! realm.commitWrite() 183 | } 184 | // MARK: - Delete 185 | func delete(_ chat:Chat) { 186 | guard let index = DB.chats.index(of: chat), index > 2 else { return } 187 | realm.beginWrite() 188 | realm.delete(chat) 189 | try! realm.commitWrite() 190 | } 191 | 192 | func delete(_ category:Category) { 193 | realm.beginWrite() 194 | realm.delete(category) 195 | try! realm.commitWrite() 196 | updateCategoriesList() 197 | } 198 | 199 | func delete(_ message:Message) { 200 | realm.beginWrite() 201 | realm.delete(message) 202 | try! realm.commitWrite() 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/Logic/CategoryManager/CategoryManager.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // CategoryManager.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | enum StaticCategoryCells:Int { 36 | case addCategory, total 37 | 38 | var text:String { 39 | switch self { 40 | case .addCategory: 41 | return "Добавить категорию" 42 | case .total: 43 | return "" 44 | } 45 | } 46 | } 47 | 48 | 49 | class CategoryManager: NSObject, UITableViewDelegate, UITableViewDataSource { 50 | fileprivate let delegate:CategoryManagerDelegate 51 | fileprivate var tableView:UITableView? 52 | 53 | fileprivate var selectedIndexPath:IndexPath { 54 | didSet { 55 | performSelection() 56 | } 57 | } 58 | 59 | var currentCategory:Category 60 | 61 | var rowsCount:Int { 62 | return DB.categories.count + StaticCategoryCells.total.rawValue 63 | } 64 | 65 | let firstRowIndex:Int = 0 66 | var lastRowIndex:Int { 67 | return rowsCount - 1 68 | } 69 | 70 | init(delegate:CategoryManagerDelegate) { 71 | self.delegate = delegate 72 | selectedIndexPath = IndexPath(row:0, section:0) 73 | currentCategory = DB.categories[selectedIndexPath.row] 74 | 75 | super.init() 76 | } 77 | 78 | // MARK: - Private 79 | fileprivate func performSelection() { 80 | switch selectedIndexPath.row { 81 | case lastRowIndex : 82 | delegate.willAddNewCategory { 83 | guard $0 == "" 84 | else { 85 | self.delegate.didAddNewCategory() 86 | self.addCategory(named: $0) 87 | return 88 | } 89 | guard let index = DB.categories.index(of: self.currentCategory) else { return } 90 | self.selectedIndexPath = IndexPath(row:index, section:0) 91 | } 92 | default: 93 | let category = DB.categories[selectedIndexPath.row] 94 | delegate.didSelect(category) 95 | currentCategory = category 96 | tableView?.reloadRows(at: [selectedIndexPath], with: .fade) 97 | } 98 | } 99 | 100 | fileprivate func addCategory(named name:String) { 101 | let category = Category() 102 | category.name = name 103 | DB.add(category) 104 | currentCategory = category 105 | addTableRow(with:currentCategory) 106 | } 107 | 108 | fileprivate func addTableRow(with category:Category) { 109 | guard let index = DB.categories.index(of: category) else { assertionFailure("Category dissapeared!!");return } 110 | let indexPath = IndexPath(row: index, section: 0) 111 | tableView?.insertRows(at: [indexPath], with: .fade) 112 | selectedIndexPath = indexPath 113 | } 114 | 115 | fileprivate func delete(row:Int) { 116 | guard UIMenuController.shared.isMenuVisible else { return } 117 | let deleteCategory = DB.categories[row] 118 | DB.delete(deleteCategory) 119 | tableView?.deleteRows(at: [IndexPath(row:row, section:0)], with: .fade) 120 | 121 | let newSelectedIndex:Int 122 | if let currentCategoryIndex = DB.categories.index(of: currentCategory) { 123 | newSelectedIndex = currentCategoryIndex 124 | } else if selectedIndexPath.row >= lastRowIndex { 125 | newSelectedIndex = lastRowIndex - 1 126 | } else { 127 | newSelectedIndex = selectedIndexPath.row 128 | } 129 | 130 | selectedIndexPath = IndexPath(row:newSelectedIndex, section:0) 131 | } 132 | 133 | fileprivate func rename(row:Int){ 134 | guard UIMenuController.shared.isMenuVisible else { return } 135 | let category = DB.categories[row] 136 | delegate.willRename(category) { name in 137 | DB.update(category, name: name) 138 | self.tableView?.reloadRows(at: [IndexPath(row:row, section:0)], with: .fade) 139 | } 140 | } 141 | 142 | // MARK: - UITableViewDelegate, UITableViewDataSource 143 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 144 | self.tableView = tableView 145 | return rowsCount 146 | } 147 | 148 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 149 | let cell = tableView.dequeueReusableCell(withIdentifier: CategoryCell.id, for: indexPath) 150 | 151 | let text:String 152 | let index = indexPath.row 153 | 154 | if index == DB.categories.count { 155 | text = StaticCategoryCells.addCategory.text 156 | } else { 157 | text = DB.categories[index].name 158 | } 159 | 160 | if (selectedIndexPath == indexPath) { 161 | cell.isSelected = true 162 | tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) 163 | } 164 | 165 | cell.textLabel?.text = text 166 | return cell 167 | } 168 | 169 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 170 | selectedIndexPath = indexPath 171 | } 172 | 173 | func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool { 174 | return indexPath.row != lastRowIndex && indexPath.row != firstRowIndex 175 | } 176 | 177 | func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool { 178 | 179 | if action == #selector(CategoryCell.delete(row:)) { delete(row: indexPath.row) } 180 | else if action == #selector(CategoryCell.rename(row:)) { rename(row: indexPath.row) } 181 | else { return false} 182 | 183 | return true 184 | } 185 | 186 | func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) { 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Feedback/FeedbackScreen.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 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainCoordinator.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // MainCoordinator.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import AVFoundation 34 | import UIKit 35 | import MessageUI 36 | 37 | typealias allertReturn = (String)->() 38 | 39 | extension MainCoordinator: ChatCollectionDelegate, CategoryManagerDelegate, MessageManagerDelegate {} 40 | 41 | class MainCoordinator: BaseCoordinator, HomeDelegate, Coordinator, CoordinatorOutput { 42 | 43 | let systemSoundID: SystemSoundID = 1070 44 | var finishFlow: ((Any) -> Void)? 45 | var barButtonItem: UIBarButtonItem { 46 | return mainVC.menuButton 47 | } 48 | var sourceView: UIView { 49 | return mainVC.menuSourceView 50 | } 51 | 52 | let audioPlayer: AVAudioPlayer = { 53 | // need crash here if there is no such resource. 54 | let soundUrl = Bundle.main.url(forResource: Constants.Sounds.soundName, 55 | withExtension: nil)! 56 | let player = try! AVAudioPlayer(contentsOf: soundUrl) 57 | return player 58 | }() 59 | 60 | fileprivate let router: Router 61 | fileprivate let assembly:AssemblyCoordinator 62 | fileprivate let screenAssembly: AssemblyScreen 63 | fileprivate let appPreference: AppSettingsManager 64 | fileprivate let ttsManager: TTSManager 65 | fileprivate let metrica: Metrica 66 | 67 | init(_ router: Router, assembly:AssemblyCoordinator, screenAssembly:AssemblyScreen, appPreference:AppSettingsManager, ttsManager: TTSManager, metrica: Metrica) { 68 | self.router = router 69 | self.assembly = assembly 70 | self.screenAssembly = screenAssembly 71 | self.appPreference = appPreference 72 | self.ttsManager = ttsManager 73 | self.metrica = metrica 74 | } 75 | 76 | fileprivate lazy var chatCollection:ChatCollection = { 77 | let chatCollection = ChatCollection(delegate:self) 78 | return chatCollection 79 | }() 80 | fileprivate lazy var categoryManager: CategoryManager = { 81 | let categoryManager = CategoryManager(delegate:self) 82 | return categoryManager 83 | }() 84 | fileprivate lazy var messageManager: MessageManager = { 85 | let messageManager = MessageManager(delegate:self) 86 | return messageManager 87 | }() 88 | 89 | fileprivate lazy var mainVC:MainScreen = { 90 | let vc = self.screenAssembly.mainScreen(delegate:self, 91 | chatCollection:chatCollection, 92 | categoryManager:categoryManager, 93 | messageManager:messageManager) 94 | return vc 95 | }() 96 | 97 | // MARK: - Public 98 | func start() { 99 | router.push(mainVC) 100 | } 101 | 102 | // MARK: - HomeDelegate 103 | func didEntered(_ text:String) { 104 | } 105 | 106 | func speak(_ text: String, with languageCode:String? = "ru_RU") { 107 | metrica.saidEvent() 108 | ttsManager.speak(text) 109 | } 110 | 111 | func addNewChat() { 112 | DB.addNewChat() 113 | chatCollection.updateLastCell() 114 | } 115 | 116 | func chatTextDidChanged(_ _text:String?) { 117 | let spaceChar = Character(" ") 118 | let text = _text ?? "" 119 | currentChat().update(text:text) 120 | 121 | guard 122 | appPreference.isSpeakEveryWord, 123 | let lastChar = text.last, 124 | lastChar == spaceChar, 125 | let lastWord = text.split(separator: spaceChar).map({String($0)}).last 126 | else { return } 127 | speak(lastWord) 128 | } 129 | 130 | func deleteCurrentChat(_ complition: @escaping (IndexPath)->()) { 131 | let chat = currentChat() 132 | guard let index = DB.chats.index(of:chat), index >= 3 else { return } 133 | DB.delete(chat) 134 | chatCollection.updateSelectedIndex() 135 | complition(chatCollection.selectedIndexPath) 136 | } 137 | 138 | func beepSound() { 139 | audioPlayer.play() 140 | } 141 | 142 | func showMenu() { 143 | let menuCoordinator = assembly.menuCoordinator 144 | addDependency(menuCoordinator) 145 | mainVC.view.endEditing(true) 146 | 147 | menuCoordinator.finishFlow = { result in 148 | self.removeDependency(menuCoordinator) 149 | self.router.dismissPopoverScreen() 150 | guard let selection = result as? MenuSelection else { return } 151 | 152 | switch selection { 153 | case .saveVoice: 154 | () 155 | case .sendFeedback: 156 | self.showSendFeedback() 157 | case .selectVoice: 158 | self.showSelectVoice() 159 | } 160 | } 161 | 162 | menuCoordinator.start() 163 | } 164 | 165 | fileprivate func showSelectVoice() { 166 | let selectVoiceCoordinator = assembly.selectVoiceCoordinator 167 | addDependency(selectVoiceCoordinator) 168 | 169 | selectVoiceCoordinator.finishFlow = { result in 170 | selectVoiceCoordinator.finishFlow = nil 171 | self.removeDependency(selectVoiceCoordinator) 172 | self.router.dismissPopoverScreen() 173 | } 174 | 175 | selectVoiceCoordinator.start() 176 | } 177 | 178 | fileprivate func showSendFeedback() { 179 | let feedbackCoordinator = assembly.feedbackCoordinator 180 | addDependency(feedbackCoordinator) 181 | 182 | feedbackCoordinator.finishFlow = { result in 183 | feedbackCoordinator.finishFlow = nil 184 | self.removeDependency(feedbackCoordinator) 185 | self.router.dismissPopoverScreen() 186 | } 187 | 188 | feedbackCoordinator.start() 189 | } 190 | 191 | func finish() { 192 | finishFlow!("sss") 193 | } 194 | 195 | // MARK: - ChatCollectionDelegate 196 | func didSelect(_ chat: Chat) { 197 | let text = chat.text 198 | mainVC.set(inputText:text) 199 | } 200 | 201 | // MARK: - CategoryManagerDelegate 202 | func willAddNewCategory(_ complition: @escaping allertReturn) { 203 | mainVC.showGetNewCategoryName(complition) 204 | } 205 | 206 | func didAddNewCategory() { 207 | metrica.categoryCreateEvent() 208 | } 209 | 210 | func didSelect(_ category: Category) { 211 | metrica.categoryChangeEvent() 212 | messageManager.category = category 213 | } 214 | func didDelete(_ category:Category) { 215 | } 216 | 217 | func willRename(_ category:Category, complition: @escaping allertReturn) { 218 | mainVC.showRename(category, block: complition) 219 | } 220 | 221 | // MARK: - MessageManagerDelegate 222 | func currentCategory() -> Category { 223 | return categoryManager.currentCategory 224 | } 225 | 226 | func didSelect(_ message: Message) { 227 | self.speak(message.text) 228 | } 229 | 230 | func willAddNewMessage(for category:Category, complition: @escaping allertReturn) { 231 | mainVC.showGetNewMessageName(complition) 232 | } 233 | 234 | func didAddNewMessage() { 235 | metrica.messageCreateEvent() 236 | } 237 | 238 | func didDelete(_ message:Message) { 239 | } 240 | func willRename(_ message:Message, complition: @escaping allertReturn) { 241 | mainVC.showRename(message, block: complition) 242 | } 243 | 244 | // MARK: - Private 245 | fileprivate func currentChat() -> Chat { 246 | let index = chatCollection.selectedIndex 247 | return DB.chats[index] 248 | } 249 | } 250 | 251 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Menu/MenuScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 46 | 58 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/MainScreen.swift: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright © 2016 Alex Makushkin 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | // 26 | // MainScreen.swift 27 | // DisType 28 | // 29 | // Created by Mike Kholomeev on 11/17/17. 30 | // 31 | 32 | import Foundation 33 | import UIKit 34 | 35 | 36 | class MainScreen: UIViewController, UITextViewDelegate { 37 | fileprivate var allertReturnBlock: allertReturn? 38 | 39 | var alertVC: UIAlertController? 40 | 41 | var delegate:HomeDelegate? 42 | var chatDelegate:ChatCollection! 43 | var categoryDelegate:CategoryManager! 44 | var messageDelegate:MessageManager! 45 | 46 | var currentChat: Chat { 47 | let index = chatCollectionView.indexPathsForSelectedItems![0].row 48 | return DB.chats[index] 49 | } 50 | 51 | lazy var singleInputTextLineHeight: CGFloat = { 52 | return inputTextHeight.constant 53 | }() 54 | 55 | lazy var allertCloseGesture: UITapGestureRecognizer = { 56 | return UITapGestureRecognizer(target: self, action: #selector(hideAlert)) 57 | }() 58 | 59 | @IBOutlet weak var menuSourceView: UIView! 60 | @IBOutlet weak var chatCollectionView: UICollectionView! 61 | @IBOutlet weak var sayButton: UIButton! 62 | @IBOutlet weak var menuButton: UIBarButtonItem! 63 | 64 | @IBOutlet weak var inputTextView: UITextView! 65 | @IBOutlet weak var inputTextHeight: NSLayoutConstraint! 66 | 67 | @IBOutlet weak var categoryTableView: UITableView! 68 | @IBOutlet weak var messageTableView: UITableView! 69 | // MARK: - Setup 70 | override func viewDidLoad() { 71 | super.viewDidLoad() 72 | UIApplication.shared.statusBarStyle = .lightContent 73 | setupSayButton() 74 | setupTextView() 75 | setupChatCollectionView() 76 | setupCategoryTableView() 77 | setupMessageTableView() 78 | setupTableMenu() 79 | } 80 | 81 | fileprivate func setupTableMenu() { 82 | let deleteMenuItem = UIMenuItem(title: "Удалить", action: #selector(CategoryCell.delete(row:))) 83 | let renameMenuItem = UIMenuItem(title: "Переименовать", action: #selector(CategoryCell.rename(row:))) 84 | UIMenuController.shared.menuItems = [deleteMenuItem, renameMenuItem] 85 | UIMenuController.shared.update() 86 | } 87 | 88 | 89 | fileprivate func setupMessageTableView() { 90 | messageTableView.delegate = messageDelegate 91 | messageTableView.dataSource = messageDelegate 92 | } 93 | 94 | fileprivate func setupCategoryTableView() { 95 | categoryTableView.delegate = categoryDelegate 96 | categoryTableView.dataSource = categoryDelegate 97 | } 98 | 99 | fileprivate func setupChatCollectionView() { 100 | chatCollectionView.delegate = chatDelegate 101 | chatCollectionView.dataSource = chatDelegate 102 | } 103 | 104 | fileprivate func setupSayButton() { 105 | sayButton.corner(radius: 4, width: 0) 106 | } 107 | 108 | fileprivate func setupTextView() { 109 | inputTextView.delegate = self 110 | inputTextView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil) 111 | } 112 | 113 | fileprivate func showAllert(named name:String, withPreFilled text:String = "", buttonText:String = "", block:@escaping allertReturn) { 114 | allertReturnBlock = block 115 | alertVC = UIAlertController(title: name, message: nil, preferredStyle: .alert) 116 | alertVC?.addTextField(configurationHandler: { textField in 117 | guard text != "" else { return } 118 | textField.text = text 119 | textField.selectAll(nil) 120 | }) 121 | alertVC?.addAction(UIAlertAction(title: buttonText, style: .default, handler: { (action) in 122 | guard 123 | let textField = self.alertVC?.textFields?[0], 124 | let text = textField.text 125 | else { self.hideAlert(); return } 126 | 127 | block(text) 128 | self.hideAlert() 129 | })) 130 | 131 | present(alertVC!, animated: true, completion: { 132 | self.alertVC?.view.superview?.addGestureRecognizer(self.allertCloseGesture) 133 | }) 134 | } 135 | 136 | @objc fileprivate func hideAlert() { 137 | alertVC?.view.superview?.removeGestureRecognizer(self.allertCloseGesture) 138 | alertVC?.dismiss(animated: true, completion: { 139 | guard let block = self.allertReturnBlock else { return } 140 | block("") 141 | }) 142 | } 143 | 144 | // MARK: - Public 145 | func set(inputText:String) { 146 | UIView.animate(withDuration: 0.2) { 147 | self.inputTextView.text = inputText 148 | } 149 | } 150 | 151 | func showGetNewCategoryName(_ block:@escaping allertReturn) { 152 | showAllert(named: "Введите имя новой категории", buttonText:"Добавить", block: block) 153 | } 154 | 155 | func showGetNewMessageName(_ block:@escaping allertReturn) { 156 | showAllert(named: "Введите новое высказывание", buttonText:"Добавить", block: block) 157 | } 158 | 159 | func showRename(_ category:Category, block:@escaping allertReturn) { 160 | showAllert(named: "Переименуйте категорию", 161 | withPreFilled:category.name, 162 | buttonText:"Обновить", 163 | block: block) 164 | } 165 | 166 | func showRename(_ message:Message, block:@escaping allertReturn) { 167 | showAllert(named: "Переименуйте высказывание", 168 | withPreFilled:message.text, 169 | buttonText:"Обновить", 170 | block: block) 171 | } 172 | 173 | // MARK: - Actions 174 | @IBAction func deleteChatAction(_ sender: UIBarButtonItem) { 175 | guard let collection = self.chatCollectionView else { return } 176 | guard let indexPathes = collection.indexPathsForSelectedItems else { return } 177 | 178 | delegate?.deleteCurrentChat { newSelectedIndexPath in 179 | collection.deleteItems(at: indexPathes) 180 | collection.reloadItems(at: [newSelectedIndexPath]) 181 | } 182 | } 183 | @IBAction func addChatAction(_ sender: UIBarButtonItem) { 184 | delegate?.addNewChat() 185 | } 186 | 187 | @IBAction func clearInputAction(_ sender: UIBarButtonItem) { 188 | inputTextView.text = "" 189 | } 190 | 191 | @IBAction func toneSignalAction(_ sender: UIBarButtonItem) { 192 | delegate?.beepSound() 193 | } 194 | 195 | @IBAction func menuAction(_ sender: Any) { 196 | delegate?.showMenu() 197 | } 198 | 199 | @IBAction func speakInputAction(_ sender: Any) { 200 | guard let text = inputTextView.text else {return} 201 | let languageCode = inputTextView.textInputMode?.primaryLanguage 202 | delegate?.speak(text, with:languageCode) 203 | } 204 | 205 | // MARK: - UITextViewDelegate 206 | func textViewDidChange(_ textView: UITextView) { 207 | let text = textView.text 208 | delegate?.chatTextDidChanged(text) 209 | } 210 | 211 | func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { 212 | let result = text=="\n" 213 | result ? speakInputAction(textView) : () 214 | 215 | return true && !result 216 | } 217 | 218 | // MARK: - Observations 219 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 220 | guard keyPath == "contentSize", let textView = object as? UITextView else { return } 221 | 222 | let contentHeight = textView.contentSize.height 223 | let inputBarHeight = textView.bounds.size.height 224 | 225 | //Center vertical alignment 226 | var topCorrect = (inputBarHeight - contentHeight * textView.zoomScale) / 2.0; 227 | topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); 228 | textView.contentOffset = CGPoint(x: 0, y: -topCorrect) 229 | inputTextHeight.constant = textView.contentSize.height; 230 | 231 | UIView.animate(withDuration: 0.2) { [weak self] in 232 | self?.view.layoutIfNeeded() 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /DisType/Source/Flow/Main/MainScreen/MainScreen.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 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | --------------------------------------------------------------------------------