Skip to content

Commit

Permalink
Merge pull request #406 from pennlabs/development
Browse files Browse the repository at this point in the history
Merge for 6.6.3
  • Loading branch information
01jongmin authored Jan 27, 2022
2 parents e0a18c8 + 5e67823 commit fc650a2
Show file tree
Hide file tree
Showing 49 changed files with 817 additions and 793 deletions.
68 changes: 48 additions & 20 deletions PennMobile.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "Event@1x.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Event@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Event@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 1 addition & 10 deletions PennMobile/Dining/Controllers/DiningViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,7 @@ class DiningViewController: GenericTableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchDiningHours()

if UserDefaults.standard.hasDiningPlan() {
if viewModel.balance == nil {
fetchBalance()
} else {
updateBalanceIfNeeded()
}
} else {
viewModel.balance = DiningBalance(diningDollars: 0, visits: 0, guestVisits: 0, lastUpdated: Date())
}
fetchBalance()

if viewModel.venues[.dining]?.isEmpty ?? true {
viewModel.refresh()
Expand Down
47 changes: 9 additions & 38 deletions PennMobile/Dining/Networking + Cache/DiningAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ class DiningAPI: Requestable {

static let instance = DiningAPI()

let diningUrl = "https://api.pennlabs.org/dining/venues"
let diningMenuUrl = "https://api.pennlabs.org/dining/daily_menu/"
let diningPrefs = "https://api.pennlabs.org/dining/preferences"
let diningBalanceUrl = "https://api.pennlabs.org/dining/balance"
let diningInsightsUrl = "https://studentlife.pennlabs.org/dining/"
let diningUrl = "https://pennmobile.org/api/dining/venues/"
let diningMenuUrl = "https://pennmobile.org/api/dining/daily_menu/"
// TODO: Broken API, need to fetch locally
let diningInsightsUrl = "https://pennmobile.org/api/dining/"

func fetchDiningHours(_ completion: @escaping (_ result: Result<DiningAPIResponse, NetworkingError>) -> Void) {
getRequestData(url: diningUrl) { (data, error, statusCode) in
Expand All @@ -30,7 +29,7 @@ class DiningAPI: Requestable {
}

guard let data = data else { return completion(.failure(.other)) }

if let diningAPIResponse = try? JSONDecoder().decode(DiningAPIResponse.self, from: data) {
self.saveToCache(diningAPIResponse.document.venues)
return completion(.success(diningAPIResponse))
Expand Down Expand Up @@ -64,7 +63,6 @@ class DiningAPI: Requestable {

func fetchDiningInsights(_ completion: @escaping (_ result: Result<DiningInsightsAPIResponse, NetworkingError>) -> Void ) {
OAuth2NetworkManager.instance.getAccessToken { (token) in
print("token:" + token!.value)
guard let token = token else {
// TODO: - Add network error handling for OAuth2
completion(.failure(.noInternet))
Expand Down Expand Up @@ -97,7 +95,6 @@ class DiningAPI: Requestable {
}
task.resume()
}

}

func fetchDetailPageHTML(for venue: DiningVenue, _ completion: @escaping (_ html: String?) -> Void) {
Expand All @@ -112,36 +109,10 @@ class DiningAPI: Requestable {
// MARK: - Dining Balance API
extension DiningAPI {
func fetchDiningBalance(_ completion: @escaping (_ diningBalance: DiningBalance?) -> Void) {
OAuth2NetworkManager.instance.getAccessToken { (token) in
guard let token = token else {
completion(nil)
return
}

let url = URL(string: self.diningBalanceUrl)!
let request = URLRequest(url: url, accessToken: token)

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse, let data = data, httpResponse.statusCode == 200 {
let json = JSON(data)
let balance = json["balance"]
if let diningDollars = balance["dining_dollars"].float,
let swipes = balance["swipes"].int,
let guestSwipes = balance["guest_swipes"].int,
let timestamp = balance["timestamp"].string {

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
if let lastUpdated = formatter.date(from: timestamp){
let balance = DiningBalance(diningDollars: diningDollars, visits: swipes, guestVisits: guestSwipes, lastUpdated: lastUpdated)
completion(balance)
return
}
}
}
completion(nil)
}
task.resume()
if Account.isLoggedIn {
CampusExpressNetworkManager.instance.getDiningBalance(completion)
} else {
completion(DiningBalance(diningDollars: 0, visits: 0, guestVisits: 0, lastUpdated: Date()))
}
}
}
Expand Down
51 changes: 51 additions & 0 deletions PennMobile/Events/EventsAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// EventsAPI.swift
// PennMobile
//
// Created by Samantha Su on 10/1/21.
// Copyright © 2021 PennLabs. All rights reserved.
//

import SwiftyJSON
import Foundation

class EventsAPI: Requestable {
static let instance = EventsAPI()

let eventsUrl = "https://penntoday.upenn.edu/events-feed?_format=json"

func fetchEvents(_ completion: @escaping (_ result: Result<[PennEvents], NetworkingError>) -> Void) {
getRequestData(url: eventsUrl) { (data, error, statusCode) in
if statusCode == nil {
return completion(.failure(.noInternet))
}

if statusCode != 200 {
return completion(.failure(.serverError))
}

guard let data = data else { return completion(.failure(.other)) }

let decoder = JSONDecoder()

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(abbreviation: "EST")
formatter.dateFormat = "MM/dd/yyyy"

decoder.dateDecodingStrategy = .formatted(formatter)

if let events = try? decoder.decode([PennEvents].self, from: data){
self.saveToCache(events)
completion(.success(events))
} else {
completion(.failure(.serverError))
}
}
}

// MARK: - Cache Methods
func saveToCache(_ response: [PennEvents]) {
Storage.store(response, to: .caches, as: PennEvents.directory)
}
}
43 changes: 43 additions & 0 deletions PennMobile/Events/PennEvents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// PennEvents.swift
// PennMobile
//
// Created by Samantha Su on 10/1/21.
// Copyright © 2021 PennLabs. All rights reserved.
//

import Foundation

struct PennEvents: Codable {
static let directory = "events.json"

let id: String
let title: String
let body: String
let image: String
let location: StringOrBool
let category: String
let path: String
let start: Date?
let end: Date?
let starttime: String
let endtime: String
let allday: String
var media_image: String
let shortdate: String

var isAllDay: Bool {
return allday == "All day"
}
}

struct StringOrBool: Codable{
var value: String?
init(from decoder: Decoder) throws {
if let string = try? String(from: decoder) {
value = string
return
}
value = nil
}
}
108 changes: 108 additions & 0 deletions PennMobile/Events/PennEventsTableViewCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// EventsTableViewCell.swift
// PennMobile
//
// Created by Samantha Su on 10/1/21.
// Copyright © 2021 PennLabs. All rights reserved.
//

import UIKit
import Kingfisher
import SwiftSoup

class PennEventsTableViewCell: UITableViewCell {

lazy var imageExistsConstraint = eventImageView.widthAnchor.constraint(equalToConstant:120)
lazy var imageMissingConstraint = eventImageView.widthAnchor.constraint(equalToConstant:0)
var isExpanded = false

var pennEvent: PennEvents? {
didSet {
guard let event = pennEvent else {return}
if event.media_image == "" {
imageExistsConstraint.isActive = false
imageMissingConstraint.isActive = true
} else {
let imageString = "https://penntoday.upenn.edu" + (event.media_image.slice(from: "<img src=\"", to: "\n") ?? "").trimmingCharacters(in: .whitespaces)
eventImageView.kf.setImage(with: URL(string: imageString))
imageExistsConstraint.isActive = true
imageMissingConstraint.isActive = false
}
titleLabel.text = event.shortdate + ": " + event.title
if let doc: Document = try? SwiftSoup.parse(event.body), let text = try? doc.text() {
bodyLabel.text = text
}
}
}

// MARK: - Views

let containerView:UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true // this will make sure its children do not go out of the boundary
return view
}()

let eventImageView:UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFill // image will never be strecthed vertially or horizontally
img.translatesAutoresizingMaskIntoConstraints = false // enable autolayout
img.layer.cornerRadius = 7
img.clipsToBounds = true
return img
}()

let titleLabel:UILabel = {
var view = UILabel()
view.backgroundColor = .clear
view.numberOfLines = 0

view.textColor = UIColor(red: 0.122, green: 0.122, blue: 0.122, alpha: 1)
view.font = UIFont(name: "SFProText-Regular", size: 30)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

let bodyLabel:UILabel = {
var view = UILabel()
view.backgroundColor = .clear
view.font = UIFont(name: "Helvetica", size: 12)
view.textColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
view.numberOfLines = 3
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

containerView.addSubview(titleLabel)
containerView.addSubview(bodyLabel)
self.contentView.addSubview(containerView)
self.contentView.addSubview(eventImageView)

containerView.leadingAnchor.constraint(equalTo:self.contentView.leadingAnchor, constant:10).isActive = true
containerView.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo:self.contentView.bottomAnchor, constant:-10).isActive = true

eventImageView.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
eventImageView.leadingAnchor.constraint(equalTo:containerView.trailingAnchor, constant:10).isActive = true
eventImageView.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor, constant:-10).isActive = true
eventImageView.heightAnchor.constraint(equalToConstant:80).isActive = true

titleLabel.topAnchor.constraint(equalTo:containerView.topAnchor, constant:5).isActive = true
titleLabel.trailingAnchor.constraint(equalTo:containerView.trailingAnchor, constant:-10).isActive = true
titleLabel.leadingAnchor.constraint(equalTo:containerView.leadingAnchor, constant:10).isActive = true

bodyLabel.topAnchor.constraint(equalTo:titleLabel.bottomAnchor, constant:0).isActive = true
bodyLabel.bottomAnchor.constraint(equalTo:containerView.bottomAnchor, constant:-5).isActive = true
bodyLabel.trailingAnchor.constraint(equalTo:containerView.trailingAnchor, constant:-10).isActive = true
bodyLabel.leadingAnchor.constraint(equalTo:containerView.leadingAnchor, constant:10).isActive = true
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

}
Loading

0 comments on commit fc650a2

Please sign in to comment.