HomeView.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. // Copyright (C) 2016 Inigo Valentin
  2. //
  3. // This file is part of the Gasteizko Margolariak IOS app.
  4. //
  5. // The Gasteizko Margolariak IOS app is free software: you can
  6. // redistribute it and/or modify it under the terms of the
  7. // GNU General Public License as published by the Free Software
  8. // Foundation, either version 3 of the License, or (at your
  9. // option) any later version.
  10. //
  11. // The Gasteizko Margolariak IOS app is distributed in the
  12. // hope that it will be useful, but WITHOUT ANY WARRANTY;
  13. // without even the implied warranty of MERCHANTABILITY or
  14. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  15. // Public License for more details.
  16. //
  17. // You should have received a copy of the GNU General Public
  18. // License along with the Gasteizko Margolariak IOS app.
  19. // If not, see <http://www.gnu.org/licenses/>.
  20. import Foundation
  21. import CoreData
  22. import UIKit
  23. /**
  24. Class to handle the home view.
  25. */
  26. class HomeView: UIView {
  27. // Outlets
  28. @IBOutlet weak var scrollView: UIScrollView!
  29. @IBOutlet weak var container: UIView!
  30. @IBOutlet weak var locationMessage: UILabel!
  31. @IBOutlet weak var lablancaImage: UIImageView!
  32. @IBOutlet weak var lablancaText: UILabel!
  33. @IBOutlet weak var btSchedule: UIButton!
  34. //Each of the sections of the view.
  35. @IBOutlet weak var locationSection: UIView!
  36. @IBOutlet weak var lablancaSection: Section!
  37. @IBOutlet weak var futureActivitiesSection: Section!
  38. @IBOutlet weak var blogSection: Section!
  39. @IBOutlet weak var gallerySection: Section!
  40. @IBOutlet weak var pastActivitiesSection: Section!
  41. @IBOutlet weak var socialSection: Section!
  42. var moc: NSManagedObjectContext? = nil
  43. var delegate: AppDelegate? = nil
  44. var lang: String? = nil
  45. var locationTimer: Timer? = nil
  46. var controller: ViewController
  47. var storyboard: UIStoryboard
  48. /**
  49. Default constructor for the storyboard.
  50. :param: frame View frame.
  51. */
  52. override init(frame: CGRect){
  53. self.storyboard = UIStoryboard(name: "Main", bundle: nil)
  54. self.controller = storyboard.instantiateViewController(withIdentifier: "GMViewController") as! ViewController
  55. super.init(frame: frame)
  56. }
  57. /**
  58. Run when the view is started.
  59. :param: coder Coder.
  60. */
  61. required init?(coder aDecoder: NSCoder) {
  62. self.storyboard = UIStoryboard(name: "Main", bundle: nil)
  63. self.controller = storyboard.instantiateViewController(withIdentifier: "GMViewController") as! ViewController
  64. super.init(coder: aDecoder)
  65. //Load the contents of the HomeView.xib file.
  66. Bundle.main.loadNibNamed("HomeView", owner: self, options: nil)
  67. self.addSubview(container)
  68. container.frame = self.bounds
  69. //Set titles for each section
  70. futureActivitiesSection.setTitle(text: "Próximas actividades")
  71. blogSection.setTitle(text: "Últimos posts")
  72. gallerySection.setTitle(text: "Últimas fotos")
  73. pastActivitiesSection.setTitle(text: "Últimas actividades")
  74. socialSection.setTitle(text: "Síguenos")
  75. //Get info to populate sections
  76. self.moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
  77. self.delegate = UIApplication.shared.delegate as! AppDelegate
  78. self.moc?.persistentStoreCoordinator = self.delegate?.persistentStoreCoordinator
  79. self.lang = getLanguage()
  80. // Populate section.
  81. populate()
  82. // Special case: Location
  83. // Set up once and start and periodically witha timer.
  84. setUpLocation()
  85. Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(setUpLocation), userInfo: nil, repeats: true)
  86. }
  87. /**
  88. Sets all the sections up
  89. */
  90. func populate(){
  91. //Populate sections
  92. setUpPastActivities(context: self.moc!, delegate: self.delegate!, lang: self.lang!, parent: self.pastActivitiesSection.getContentStack())
  93. setUpBlog(context: self.moc!, delegate: self.delegate!, lang: self.lang!, parent: self.blogSection.getContentStack())
  94. setUpFutureActivities(context: self.moc!, delegate: self.delegate!, lang: self.lang!, parent: self.futureActivitiesSection.getContentStack())
  95. setUpSocial(parent: self.socialSection.getContentStack())
  96. setUpGallery(context: self.moc!, delegate: self.delegate!, parent: self.gallerySection.getContentStack())
  97. setUpLablanca(context: self.moc!, delegate: self.delegate!, lang: self.lang!)
  98. }
  99. /**
  100. Gets the device language. The only recognized languages are Spanish, English and Basque.
  101. If the device has another language, Spanish will be selected by default.
  102. :return: Two-letter language code.
  103. */
  104. func getLanguage() -> String{
  105. let pre = NSLocale.preferredLanguages[0].subStr(start: 0, end: 1)
  106. if(pre == "es" || pre == "en" || pre == "eu"){
  107. return pre
  108. }
  109. else{
  110. return "es"
  111. }
  112. }
  113. /**
  114. Sets up the location section.
  115. If no location is reported, it hiddes the section.
  116. */
  117. func setUpLocation(){
  118. let defaults = UserDefaults.standard
  119. if (defaults.value(forKey: "GMLocLat") != nil && defaults.value(forKey: "GMLocLon") != nil){
  120. let lat = defaults.value(forKey: "GMLocLat") as! Double
  121. let lon = defaults.value(forKey: "GMLocLon") as! Double
  122. let time = defaults.value(forKey: "GMLocTime") as! Date
  123. let cTime = Date()
  124. let minutes = Calendar.current.dateComponents([.minute], from: time, to: cTime).minute
  125. if (minutes! < 30){
  126. self.locationSection.isHidden = false
  127. // Set tap recognizer.
  128. let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector (HomeView.openLocation (_:)))
  129. tapRecognizer.delegate = (UIApplication.shared.delegate as! AppDelegate).controller
  130. self.locationSection.addGestureRecognizer(tapRecognizer)
  131. let location = self.controller.getLocation()
  132. if location != nil {
  133. let d: Int = calculateDistance(lat1: location.latitude, lon1: location.longitude, lat2: lat, lon2: lon)
  134. if d <= 1000 {
  135. self.locationMessage.text = "¡Gasteizko Margolariak está por ahí! A \(d) metros de ti."
  136. }
  137. else{
  138. self.locationMessage.text = "¡Gasteizko Margolariak está por ahí! A \(Int(d/1000)) kilómetros de ti."
  139. }
  140. }
  141. else{
  142. self.locationMessage.text = "¡Gasteizko Margolariak está por ahí!"
  143. }
  144. }
  145. else{
  146. self.locationSection.isHidden = true
  147. }
  148. }
  149. else{
  150. self.locationSection.isHidden = true
  151. }
  152. }
  153. /**
  154. Sets up the La Blanca secction.
  155. Hiddes it if no current festivals.
  156. :param: context App context.
  157. :param: delegate App delegate.
  158. :param: lang Language code (two letter code, lowercase. Only 'es', 'en' and 'eu' supported).
  159. */
  160. func setUpLablanca(context : NSManagedObjectContext, delegate: AppDelegate, lang: String){
  161. let defaults = UserDefaults.standard
  162. if (defaults.value(forKey: "festivals") != nil){
  163. let festivals = defaults.value(forKey: "festivals") as! Int
  164. if festivals == 1{
  165. // Set tap recognizer
  166. let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector (HomeView.openLablanca (_:)))
  167. tapRecognizer.delegate = (UIApplication.shared.delegate as! AppDelegate).controller
  168. self.lablancaSection.addGestureRecognizer(tapRecognizer)
  169. // TODO: Get current year
  170. let year = 2017
  171. // Get info about festivals
  172. let fetchRequest: NSFetchRequest<Festival> = Festival.fetchRequest()
  173. fetchRequest.predicate = NSPredicate(format: "year = %i", year)
  174. do {
  175. // Get info from festivals
  176. let searchResults = try context.fetch(fetchRequest)
  177. if searchResults.count > 0 {
  178. let r = searchResults[0]
  179. // Set image and text
  180. self.lablancaText.text = (r.value(forKey: "text_\(lang)") as! String?)?.decode().stripHtml()
  181. let filename: String = r.value(forKey: "img") as! String
  182. if (filename == ""){
  183. // Hide the imageview
  184. self.lablancaImage.isHidden = true;
  185. }
  186. else{
  187. let path = "img/fiestas/thumb/\(filename)"
  188. self.lablancaImage.setImage(localPath: path, remotePath: "https://margolariak.com/\(path)")
  189. }
  190. // Set button up
  191. let tapRecognizerGMSchedule = UITapGestureRecognizer(target: self, action: #selector(openGMSchedule(_:)))
  192. btSchedule.isUserInteractionEnabled = true
  193. btSchedule.addGestureRecognizer(tapRecognizerGMSchedule)
  194. }
  195. }
  196. catch {
  197. NSLog(":HOME:ERROR: Error getting setting the La Blanca section up: \(error)")
  198. }
  199. }
  200. else{
  201. self.lablancaSection.isHidden = true
  202. }
  203. }
  204. }
  205. /**
  206. Sets up the future activities section.
  207. If none, it hiddes the section.
  208. :param: context App context.
  209. :param: delegate App delegate.
  210. :param: lang Language code (two letter code, lowercase. Only 'es', 'en' and 'eu' supported).
  211. :param: parent Stack view to load the rows in.
  212. */
  213. func setUpFutureActivities(context : NSManagedObjectContext, delegate: AppDelegate, lang: String, parent : UIStackView){
  214. let fetchRequest: NSFetchRequest<Activity> = Activity.fetchRequest()
  215. let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
  216. let sortDescriptors = [sortDescriptor]
  217. fetchRequest.sortDescriptors = sortDescriptors
  218. fetchRequest.predicate = NSPredicate(format: "date > %@", NSDate())
  219. fetchRequest.fetchLimit = 2
  220. do {
  221. let searchResults = try context.fetch(fetchRequest)
  222. var row : RowHomeFutureActivities
  223. var count = 0
  224. var id: Int
  225. var title: String
  226. var text: String
  227. var image: String
  228. if searchResults.count == 0{
  229. self.futureActivitiesSection.isHidden = true
  230. }
  231. else{
  232. for r in searchResults as [NSManagedObject] {
  233. count = count + 1
  234. //Create a new row
  235. row = RowHomeFutureActivities.init(s: "rowHomeFutureActivities\(count)", i: count)
  236. id = r.value(forKey: "id")! as! Int
  237. title = r.value(forKey: "title_\(lang)")! as! String
  238. text = r.value(forKey: "text_\(lang)")! as! String
  239. row.setTitle(text: title)
  240. row.setText(text: text)
  241. row.id = id
  242. // Get main image
  243. image = ""
  244. let imgFetchRequest: NSFetchRequest<Activity_image> = Activity_image.fetchRequest()
  245. let imgSortDescriptor = NSSortDescriptor(key: "idx", ascending: true)
  246. let imgSortDescriptors = [imgSortDescriptor]
  247. imgFetchRequest.sortDescriptors = imgSortDescriptors
  248. imgFetchRequest.predicate = NSPredicate(format: "activity == %i", id)
  249. imgFetchRequest.fetchLimit = 1
  250. do{
  251. let imgSearchResults = try context.fetch(imgFetchRequest)
  252. for imgR in imgSearchResults as [NSManagedObject]{
  253. image = imgR.value(forKey: "image")! as! String
  254. row.setImage(filename: image)
  255. }
  256. }
  257. catch {
  258. NSLog(":HOME:ERROR: Error getting image for activity \(id): \(error)")
  259. }
  260. parent.addArrangedSubview(row)
  261. }
  262. }
  263. }
  264. catch {
  265. NSLog(":HOME:ERROR: Error loading future activities: \(error)")
  266. }
  267. }
  268. /**
  269. Sets up the blog section.
  270. :param: context App context.
  271. :param: delegate App delegate.
  272. :param: lang Language code (two letter code, lowercase. Only 'es', 'en' and 'eu' supported).
  273. :param: parent Stack view to load the rows in.
  274. */
  275. func setUpBlog(context : NSManagedObjectContext, delegate: AppDelegate, lang: String, parent : UIStackView){
  276. let fetchRequest: NSFetchRequest<Post> = Post.fetchRequest()
  277. let sortDescriptor = NSSortDescriptor(key: "dtime", ascending: false)
  278. let sortDescriptors = [sortDescriptor]
  279. fetchRequest.sortDescriptors = sortDescriptors
  280. fetchRequest.fetchLimit = 2
  281. do {
  282. let searchResults = try context.fetch(fetchRequest)
  283. var row : RowHomeBlog
  284. var count = 0
  285. var id: Int
  286. var title: String
  287. var text: String
  288. var image: String
  289. for r in searchResults as [NSManagedObject] {
  290. count = count + 1
  291. //Create a new row
  292. row = RowHomeBlog.init(s: "rowHomeBlog\(count)", i: count)
  293. id = r.value(forKey: "id")! as! Int
  294. title = r.value(forKey: "title_\(lang)")! as! String
  295. text = r.value(forKey: "text_\(lang)")! as! String
  296. row.id = id
  297. row.setTitle(text: title)
  298. row.setText(text: text)
  299. // Get main image
  300. image = ""
  301. let imgFetchRequest: NSFetchRequest<Post_image> = Post_image.fetchRequest()
  302. let imgSortDescriptor = NSSortDescriptor(key: "idx", ascending: true)
  303. let imgSortDescriptors = [imgSortDescriptor]
  304. imgFetchRequest.sortDescriptors = imgSortDescriptors
  305. imgFetchRequest.predicate = NSPredicate(format: "post == %i", id)
  306. imgFetchRequest.fetchLimit = 1
  307. do{
  308. let imgSearchResults = try context.fetch(imgFetchRequest)
  309. for imgR in imgSearchResults as [NSManagedObject]{
  310. image = imgR.value(forKey: "image")! as! String
  311. row.setImage(filename: image)
  312. }
  313. } catch {
  314. NSLog(":HOME:ERROR: Error getting image for post \(id): \(error)")
  315. }
  316. parent.addArrangedSubview(row)
  317. let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(openPost(_:)))
  318. row.isUserInteractionEnabled = true
  319. row.addGestureRecognizer(tapRecognizer)
  320. }
  321. }
  322. catch {
  323. NSLog(":HOME:ERROR: Error loading the blog section: \(error)")
  324. }
  325. }
  326. /**
  327. Sets up the past activities section.
  328. :param: context App context.
  329. :param: delegate App delegate.
  330. :param: lang Language code (two letter code, lowercase. Only 'es', 'en' and 'eu' supported).
  331. :param: parent Stack view to load the rows in.
  332. */
  333. func setUpPastActivities(context : NSManagedObjectContext, delegate: AppDelegate, lang: String, parent : UIStackView){
  334. let fetchRequest: NSFetchRequest<Activity> = Activity.fetchRequest()
  335. let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
  336. let sortDescriptors = [sortDescriptor]
  337. fetchRequest.sortDescriptors = sortDescriptors
  338. fetchRequest.predicate = NSPredicate(format: "date <= %@", NSDate())
  339. fetchRequest.fetchLimit = 2
  340. do {
  341. let searchResults = try context.fetch(fetchRequest)
  342. var row : RowHomePastActivities
  343. var count = 0
  344. var id: Int
  345. var title: String
  346. var text: String
  347. var image: String
  348. for r in searchResults as [NSManagedObject] {
  349. count = count + 1
  350. //Create a new row
  351. row = RowHomePastActivities.init(s: "rowHomePastActivities\(count)", i: count)
  352. id = r.value(forKey: "id")! as! Int
  353. title = r.value(forKey: "title_\(lang)")! as! String
  354. text = r.value(forKey: "text_\(lang)")! as! String
  355. row.setTitle(text: title)
  356. row.setText(text: text)
  357. row.id = id
  358. // Get main image
  359. image = ""
  360. let imgFetchRequest: NSFetchRequest<Activity_image> = Activity_image.fetchRequest()
  361. let imgSortDescriptor = NSSortDescriptor(key: "idx", ascending: true)
  362. let imgSortDescriptors = [imgSortDescriptor]
  363. imgFetchRequest.sortDescriptors = imgSortDescriptors
  364. imgFetchRequest.predicate = NSPredicate(format: "activity == %i", id)
  365. imgFetchRequest.fetchLimit = 1
  366. do{
  367. let imgSearchResults = try context.fetch(imgFetchRequest)
  368. for imgR in imgSearchResults as [NSManagedObject]{
  369. image = imgR.value(forKey: "image")! as! String
  370. row.setImage(filename: image)
  371. }
  372. }
  373. catch {
  374. NSLog(":HOME:ERROR: Error getting image for past activity \(id): \(error)")
  375. }
  376. parent.addArrangedSubview(row)
  377. }
  378. }
  379. catch {
  380. NSLog(":HOME:ERROR: Error loading past activities: \(error)")
  381. }
  382. }
  383. /**
  384. Sets up the future activities section.
  385. :param: context App context.
  386. :param: delegate App delegate.
  387. :param: parent Stack view to load the rows in.
  388. */
  389. func setUpGallery(context : NSManagedObjectContext, delegate: AppDelegate,parent: UIStackView){
  390. // Create the row
  391. var row: RowHomeGallery
  392. row = RowHomeGallery.init(s: "rowHomeGallery", i: 0)
  393. parent.addArrangedSubview(row)
  394. // Set images
  395. let fetchRequest: NSFetchRequest<Photo> = Photo.fetchRequest()
  396. let sortDescriptor = NSSortDescriptor(key: "uploaded", ascending: false)
  397. let sortDescriptors = [sortDescriptor]
  398. fetchRequest.sortDescriptors = sortDescriptors
  399. fetchRequest.fetchLimit = 4
  400. do {
  401. let searchResults = try context.fetch(fetchRequest)
  402. var id: Int
  403. var image: String
  404. var albumId: Int
  405. var i = 0
  406. for r in searchResults as [NSManagedObject] {
  407. image = r.value(forKey: "file")! as! String
  408. id = r.value(forKey: "id")! as! Int
  409. // TODO Get album id
  410. // Get album title
  411. let albumFetchRequest: NSFetchRequest<Photo_album> = Photo_album.fetchRequest()
  412. albumFetchRequest.predicate = NSPredicate(format: "photo = %i", id)
  413. do {
  414. let results = try context.fetch(albumFetchRequest)
  415. let r = results[0]
  416. albumId = r.value(forKey: "album")! as! Int
  417. row.albumIds[i] = albumId
  418. }
  419. catch {
  420. NSLog(":GALLERYCONTROLLER:ERROR: Error getting album info: \(error)")
  421. }
  422. row.setImage(idx: i, filename: image)
  423. row.photoIds[i] = id
  424. i = i + 1
  425. }
  426. }
  427. catch{
  428. NSLog(":HOME:ERROR: Error setting gallery up: \(error)")
  429. }
  430. }
  431. /**
  432. Sets up the social section.
  433. :param: parent Stack view to load the rows in.
  434. */
  435. func setUpSocial(parent : UIStackView){
  436. //Create a new row
  437. var row : RowHomeSocial
  438. row = RowHomeSocial.init(s: "rowHomeSocial", i: 0)
  439. parent.addArrangedSubview(row)
  440. }
  441. /**
  442. Converts degrees to radians.
  443. :params: degrees Angle in degrees.
  444. :return: Angle in radians.
  445. */
  446. func degreesToRadians(degrees: Double) -> Double {
  447. return degrees * Double.pi / 180;
  448. }
  449. /**
  450. Calculates the distance between two coordinates.
  451. :param: lat1 Latitude of the first coordinate.
  452. :param: lon1 Longitude of the first coordinate.
  453. :param: lat2 Latitude of the second coordinate.
  454. :param: lon2 Longitude of the second coordinate.
  455. :return: Distance between the points, in meters.
  456. */
  457. func calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Int {
  458. let eRadius: Double = 6371
  459. let dLat: Double = degreesToRadians(degrees: lat2-lat1)
  460. let dLon: Double = degreesToRadians(degrees: lon2-lon1)
  461. let l1: Double = degreesToRadians(degrees: lat1)
  462. let l2: Double = degreesToRadians(degrees: lat2)
  463. let a: Double = sin(dLat/2) * sin(dLat/2) + sin(dLon/2) * sin(dLon/2) * cos(l1) * cos(l2)
  464. let c: Double = 2 * atan2(sqrt(a), sqrt(1-a))
  465. let m: Double = eRadius * c * 1000
  466. return Int(m)
  467. }
  468. /**
  469. Opens a post.
  470. */
  471. func openPost(_ sender:UITapGestureRecognizer? = nil){
  472. let delegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
  473. delegate.controller?.showPost(id: (sender?.view as! RowHomeBlog).id)
  474. }
  475. /**
  476. Opens the La Blanca section when tapping the section.
  477. */
  478. func openLablanca(_ sender:UITapGestureRecognizer? = nil){
  479. let delegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
  480. delegate.controller?.showComponent(selected: 2)
  481. }
  482. /**
  483. Opens the Location section when tapping the section.
  484. */
  485. func openLocation(_ sender:UITapGestureRecognizer? = nil){
  486. let delegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
  487. delegate.controller?.showComponent(selected: 1)
  488. }
  489. /**
  490. Commands the view controller to show the Gasteizko Margolariak schedule view.
  491. */
  492. func openGMSchedule(_ sender:UITapGestureRecognizer? = nil){
  493. let delegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
  494. delegate.controller?.showSchedule(margolari: true)
  495. }
  496. }