To receive notifications about scheduled maintenance, please subscribe to the mailing-list gitlab-operations@sympa.ethz.ch. You can subscribe to the mailing-list at https://sympa.ethz.ch

Commit 27e6a53a authored by domenicw's avatar domenicw
Browse files

Event grouping, Event signup

parent 701986ea
......@@ -29,6 +29,11 @@
B005A32E21727D6A00D0BA31 /* RSCodeDataMatrixGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F912EF2172015E00B883BA /* RSCodeDataMatrixGenerator.swift */; };
B005A32F21727D6D00D0BA31 /* RSCornersLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F912E32172015D00B883BA /* RSCornersLayer.swift */; };
B005A33021727D7200D0BA31 /* RSITF14Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F912F32172015F00B883BA /* RSITF14Generator.swift */; };
B005A3322172895F00D0BA31 /* JSONEncoder+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005A3312172895F00D0BA31 /* JSONEncoder+Extension.swift */; };
B005A3342172A74E00D0BA31 /* NumberFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005A3332172A74E00D0BA31 /* NumberFormatter+Extension.swift */; };
B005A3362172B19300D0BA31 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005A3352172B19300D0BA31 /* Date+Extension.swift */; };
B005A3382172B38500D0BA31 /* EventViewModelSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005A3372172B38500D0BA31 /* EventViewModelSection.swift */; };
B005A33A2172B3F600D0BA31 /* EventViewModelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005A3392172B3F600D0BA31 /* EventViewModelCell.swift */; };
B048377E21582D4E00AFA689 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B048377D21582D4E00AFA689 /* String+Extension.swift */; };
B050E120215169230090CB79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B050E11F215169230090CB79 /* AppDelegate.swift */; };
B050E127215169250090CB79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B050E126215169250090CB79 /* Assets.xcassets */; };
......@@ -164,6 +169,11 @@
92F912F42172015F00B883BA /* RSITFGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSITFGenerator.swift; sourceTree = "<group>"; };
92F912F52172015F00B883BA /* RSUnifiedCodeGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSUnifiedCodeGenerator.swift; sourceTree = "<group>"; };
92F912F62172015F00B883BA /* RSUPCEGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSUPCEGenerator.swift; sourceTree = "<group>"; };
B005A3312172895F00D0BA31 /* JSONEncoder+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONEncoder+Extension.swift"; sourceTree = "<group>"; };
B005A3332172A74E00D0BA31 /* NumberFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+Extension.swift"; sourceTree = "<group>"; };
B005A3352172B19300D0BA31 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
B005A3372172B38500D0BA31 /* EventViewModelSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventViewModelSection.swift; sourceTree = "<group>"; };
B005A3392172B3F600D0BA31 /* EventViewModelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventViewModelCell.swift; sourceTree = "<group>"; };
B048377D21582D4E00AFA689 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
B050E11C215169230090CB79 /* Amiv.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Amiv.app; sourceTree = BUILT_PRODUCTS_DIR; };
B050E11F215169230090CB79 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
......@@ -322,6 +332,8 @@
isa = PBXGroup;
children = (
B050E15821516E230090CB79 /* EventViewModel.swift */,
B005A3372172B38500D0BA31 /* EventViewModelSection.swift */,
B005A3392172B3F600D0BA31 /* EventViewModelCell.swift */,
);
path = Events;
sourceTree = "<group>";
......@@ -498,6 +510,9 @@
B0D3F92A21552E8E005209FF /* UIButton+Extension.swift */,
B048377D21582D4E00AFA689 /* String+Extension.swift */,
B0F5B94E217137EC005E4591 /* Local+Extension.swift */,
B005A3312172895F00D0BA31 /* JSONEncoder+Extension.swift */,
B005A3332172A74E00D0BA31 /* NumberFormatter+Extension.swift */,
B005A3352172B19300D0BA31 /* Date+Extension.swift */,
);
path = Extension;
sourceTree = "<group>";
......@@ -951,8 +966,10 @@
B005A32E21727D6A00D0BA31 /* RSCodeDataMatrixGenerator.swift in Sources */,
B005A32D21727D6600D0BA31 /* RSCode39Mod43Generator.swift in Sources */,
B0FE2F1421550C4400F3D073 /* EventsViewControllerDelegate.swift in Sources */,
B005A3362172B19300D0BA31 /* Date+Extension.swift in Sources */,
B0AF91422157D192008F3B80 /* KeychainKey.swift in Sources */,
92F9130A2172015F00B883BA /* RSUnifiedCodeGenerator.swift in Sources */,
B005A3382172B38500D0BA31 /* EventViewModelSection.swift in Sources */,
B0FE2F16215514E600F3D073 /* QuickLookDataSource.swift in Sources */,
B050E17821517EF50090CB79 /* AmivMicroAppModel.swift in Sources */,
B0AF91452157D34E008F3B80 /* JobOffersResponse.swift in Sources */,
......@@ -960,6 +977,7 @@
92F913022172015F00B883BA /* ContextMaker.m in Sources */,
92F913002172015F00B883BA /* RSExtendedCode39Generator.swift in Sources */,
B050E18C2151A54D0090CB79 /* SettingsCellModel.swift in Sources */,
B005A3342172A74E00D0BA31 /* NumberFormatter+Extension.swift in Sources */,
92F913092172015F00B883BA /* RSITFGenerator.swift in Sources */,
B0E22FDF216DD754002317D6 /* AMIVApiStudyDocuments.swift in Sources */,
B07A8A0921524384003CC2D8 /* OnboardingNavigatorDelegate.swift in Sources */,
......@@ -996,6 +1014,8 @@
B050E16B215177820090CB79 /* JobsNavigator.swift in Sources */,
B050E14C21516A590090CB79 /* AmivRootNavigator.swift in Sources */,
92F912F92172015F00B883BA /* RSCode128Generator.swift in Sources */,
B005A33A2172B3F600D0BA31 /* EventViewModelCell.swift in Sources */,
B005A3322172895F00D0BA31 /* JSONEncoder+Extension.swift in Sources */,
92F912FD2172015F00B883BA /* RSCode93Generator.swift in Sources */,
B0AF913C2157C025008F3B80 /* Router.swift in Sources */,
B0E22FE1216E93EF002317D6 /* StudyDocumentResponse.swift in Sources */,
......
//
// Date+Extension.swift
// Amiv
//
// Created by Domenic Wüthrich on 14.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
extension Date {
public var isToday: Bool {
return Calendar.current.isDateInToday(self)
}
public var string: String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
return formatter.string(from: self)
}
}
//
// JSONEncoder+Extension.swift
// Amiv
//
// Created by Domenic Wüthrich on 13.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
extension Encodable {
public func encode() throws -> Data {
return try JSONEncoder().encode(self)
}
}
//
// NumberFormatter+Extension.swift
// Amiv
//
// Created by Domenic Wüthrich on 14.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
extension NumberFormatter {
public static func priceString(for price: Double) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = Locale(identifier: "de_CH")
if let priceString = formatter.string(from: price as NSNumber) {
return priceString
} else {
return String(describing: price)
}
}
}
......@@ -13,17 +13,18 @@ public struct EventViewModel {
// MARK: - Variables
let viewTitle: String
let events: [AMIVEvent]
let sections: [EventViewModelSection]
// MARK: - Initializers
public init(viewTitle: String, events: [AMIVEvent]) {
public init(viewTitle: String, events: [(String, [AMIVEvent])]) {
self.viewTitle = viewTitle
self.events = events
self.sections = events.map({EventViewModelSection(sectionTitle: $0.0, cells: $0.1)})
}
public init(response: EventsResponse) {
self.init(viewTitle: "Events", events: response.events)
let events = response.sortEvents()
self.init(viewTitle: "Events", events: events)
}
}
......
//
// EventViewModelCell.swift
// Amiv
//
// Created by Domenic Wüthrich on 14.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
public struct EventViewModelCell {
// MARK: - Variables
public let title: String
public let subtitle: String
public let additionalInfo: String
public let event: AMIVEvent
// MARK: - Initializers
public init(event: AMIVEvent) {
self.title = event.title
self.subtitle = event.catchPhrase
self.additionalInfo = String(describing: event.spots - event.signupCount)
self.event = event
}
}
//
// EventViewModelSection.swift
// Amiv
//
// Created by Domenic Wüthrich on 14.10.18.
// Copyright © 2018 Amiv an der ETH. All rights reserved.
//
import Foundation
public struct EventViewModelSection {
// MARK: - Variables
public let sectionTitle: String
public let cells: [EventViewModelCell]
// MARK: - Initializers
public init(sectionTitle: String, cells: [AMIVEvent]) {
self.sectionTitle = sectionTitle
self.cells = cells.map({EventViewModelCell(event: $0)})
}
}
......@@ -30,17 +30,6 @@ public class EventsNavigator: Navigator {
events.delegate = self
self.refreshData(events)
self.networkManager.getEventSignups { (events, error) in
guard let event = events?.first else {
return
}
self.networkManager.signupTo(event, { (success, error) in
debugPrint(success)
debugPrint(error)
})
}
}
// MARK: - Navigation
......@@ -95,9 +84,14 @@ extension EventsNavigator: EventsViewControllerDelegate {
extension EventsNavigator: GenericInfoViewControllerDelegate {
public func buttonTapped(_ viewController: GenericInfoViewController, action: GenericInfoViewControllerAction) {
debugPrint("Info View button tapped")
guard case .signup(let eventID) = action else {
return
}
// TODO: - Sign up for event
self.networkManager.signupTo(eventID) { (signup, error) in
debugPrint(error)
debugPrint(signup)
}
}
}
......@@ -11,8 +11,8 @@ import Foundation
public enum AMIVApiEvents {
case events
case eventSignups
case signup(_ eventSignup: EventSignup)
case allEventSignups
case signup(_ event: String, user: String)
case media(_ path: String)
}
......@@ -23,7 +23,7 @@ extension AMIVApiEvents: EndPointType {
switch self {
case .events:
return "/events"
case .eventSignups, .signup:
case .allEventSignups, .signup:
return "/eventsignups"
case .media(let path):
return path
......@@ -32,7 +32,7 @@ extension AMIVApiEvents: EndPointType {
public var httpMethod: HTTPMethod {
switch self {
case .events, .media, .eventSignups:
case .events, .media, .allEventSignups:
return .get
case .signup:
return .post
......@@ -41,16 +41,17 @@ extension AMIVApiEvents: EndPointType {
public var task: HTTPTask {
switch self {
case .events, .media, .eventSignups:
case .events, .media, .allEventSignups:
return .request
case .signup(let eventSignup):
return .requestJson(json: eventSignup)
case let .signup(event, user):
let parameters = ["event": event, "user": user]
return .requestParameters(bodyParameters: parameters, urlParameter: nil)
}
}
public var headers: HTTPHeaders? {
switch self {
case .events, .media, .eventSignups, .signup:
case .events, .media, .allEventSignups, .signup:
return nil
}
}
......@@ -59,7 +60,7 @@ extension AMIVApiEvents: EndPointType {
switch self {
case .events, .media:
return false
case .eventSignups, .signup:
case .allEventSignups, .signup:
return true
}
}
......
......@@ -100,8 +100,8 @@ extension NetworkManager where EndPoint == AMIVApiEvents {
}
}
public func getEventSignups(_ completion: @escaping (_ signups: [EventSignup]?, _ error: String?) -> Void) {
router.request(.eventSignups) { (data, response, error) in
public func getAllEventSignups(_ completion: @escaping (_ signups: [EventSignup]?, _ error: String?) -> Void) {
router.request(.allEventSignups) { (data, response, error) in
guard error == nil else {
completion(nil, error?.localizedDescription)
return
......@@ -130,14 +130,14 @@ extension NetworkManager where EndPoint == AMIVApiEvents {
}
}
public func signupTo(_ event: EventSignup, _ completion: @escaping (_ success: Bool, _ error: String?) -> Void) {
router.request(.signup(event)) { (data, response, error) in
public func signupTo(_ event: String, _ completion: @escaping (_ signup: EventSignup?, _ error: String?) -> Void) {
guard let user = User.loadLocal()?.id else {
completion(nil, "Missing user id")
return
}
router.request(.signup(event, user: user)) { (data, response, error) in
guard error == nil else {
completion(false, error?.localizedDescription)
return
}
guard let response = response as? HTTPURLResponse else {
completion(nil, error?.localizedDescription)
return
}
......@@ -145,12 +145,26 @@ extension NetworkManager where EndPoint == AMIVApiEvents {
debugPrint(String(data: data, encoding: .utf8))
}
guard let response = response as? HTTPURLResponse else {
debugPrint("fail")
return
}
let result = self.handleNetworkRequest(response)
switch result {
case .success:
completion(true, nil)
guard let data = data else {
completion(nil, NetworkResponse.noData.rawValue)
return
}
do {
let json = try JSONDecoder().decode(EventSignup.self, from: data)
completion(json, nil)
} catch {
completion(nil, NetworkResponse.unableToDecode.rawValue)
}
case .failure(let error):
completion(false, error)
completion(nil, error)
}
}
}
......
......@@ -82,3 +82,20 @@ extension AMIVEvent: Decodable {
}
}
extension AMIVEvent {
public func getInfoText() -> String {
var text = self.description
text += "\n\n"
let shortInfo = [("Start Time", self.startTime.string), ("End Time", self.endTime.string), ("Price", NumberFormatter.priceString(for: Double(self.price))), ("Total Spots", String(describing: self.spots)), ("Available Spots", String(describing: self.spots - self.signupCount))]
for (title, info) in shortInfo {
text += "\(title)\n\t\(info)\n"
}
return text
}
}
......@@ -45,4 +45,16 @@ extension EventSignup: Codable {
}
// TODO: - Implement encodable
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: EventSignupCodingKeys.self)
try container.encode(self.eventID, forKey: .eventID)
try container.encode(self.userID, forKey: .userID)
try container.encode(self.email, forKey: .email)
try container.encode(self.isAccepted, forKey: .isAccepted)
try container.encode(self.isConfirmed, forKey: .isConfirmed)
try container.encode(self.signupID, forKey: .signupID)
try container.encode(etag, forKey: .etag)
}
}
......@@ -27,3 +27,29 @@ extension EventsResponse: Decodable {
}
}
extension EventsResponse {
public func sortEvents() -> [(String, [AMIVEvent])] {
var past: [AMIVEvent] = []
var today: [AMIVEvent] = []
var upcoming: [AMIVEvent] = []
var current: [AMIVEvent] = []
let now = Date()
self.events.forEach { (event) in
if event.startTime <= now && event.endTime >= now {
current.append(event)
} else if event.startTime.isToday {
today.append(event)
} else if event.startTime >= now {
upcoming.append(event)
} else if event.endTime <= now {
past.append(event)
}
}
let events: [(String, [AMIVEvent])] = [("Right Now", current), ("Today", today), ("Upcoming", upcoming), ("Past Events", past)]
return events.filter({!($0.1.isEmpty)})
}
}
......@@ -13,7 +13,7 @@ public enum HTTPMethod: String {
case post = "POST"
case get = "GET"
case put = "PUT"
case path = "PATCH"
case patch = "PATCH"
case delete = "DELETE"
}
......@@ -15,6 +15,6 @@ public enum HTTPTask {
case request
case requestParameters(bodyParameters: Parameters?, urlParameter: Parameters?)
case requestParametersAndHeaders(bodyParameters: Parameters?, urlParameter: Parameters?, additionalHeaders: HTTPHeaders?)
case requestJson(json: EventSignup)
case requestJson(json: Encodable)
}
......@@ -74,12 +74,13 @@ public class Router<EndPoint: EndPointType>: NetworkRouter {
self.addAdditionalHeaders(additionalHeaders, request: &request)
try self.configureParameters(bodyParameters: bodyParameters, urlParameters: urlParameters, request: &request)
case .requestJson(let json):
let jsonData = try JSONEncoder().encode(json)
let jsonData = try json.encode()
request.httpBody = jsonData
if request.value(forHTTPHeaderField: "Content-Type") == nil {
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
}
self.addAdditionalHeaders(route.headers, request: &request)
return request
} catch let error {
......
......@@ -77,11 +77,11 @@ public class EventsViewController: UITableViewController {
extension EventsViewController {
public override func numberOfSections(in tableView: UITableView) -> Int {
return 1
return self.model.sections.count
}
public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.model.events.count
return self.model.sections[section].cells.count
}
public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
......@@ -92,14 +92,14 @@ extension EventsViewController {
return cell
}()
let event = self.model.events[indexPath.row]
cell.setupView(title: event.title, subtitle: event.catchPhrase, additionalInfo: String(describing: event.signupCount))
let event = self.model.sections[indexPath.section].cells[indexPath.row]
cell.setupView(title: event.title, subtitle: event.subtitle, additionalInfo: event.additionalInfo)
return cell
}
public override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Events"
return self.model.sections[section].sectionTitle
}
}
......@@ -109,7 +109,7 @@ extension EventsViewController {
extension EventsViewController {
public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate?.didSelectEvent(self, event: self.model.events[indexPath.row])
self.delegate?.didSelectEvent(self, event: self.model.sections[indexPath.section].cells[indexPath.row].event)
tableView.deselectRow(at: indexPath, animated: true)
}
......
......@@ -43,9 +43,14 @@ public struct GenericInfoViewControllerModel {
public init(event: AMIVEvent, image: UIImage?) {
self.title = event.title
self.image = image
self.text = event.description
self.titleButton = "Signup"
self.action = .signup(event.id)
self.text = event.getInfoText()
if event.startTime >= Date() {
self.titleButton = "Signup"
self.action = .signup(event.id)
} else {
self.titleButton = nil
self.action = .default
}
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment