[+] Add family add alarm table
This commit is contained in:
@@ -0,0 +1,512 @@
|
||||
//
|
||||
// ViewController.swift
|
||||
// ProjectClock
|
||||
//
|
||||
// Created by Hykilpikonna on 1/6/21.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
Account view controller controlling the two separate view controllers
|
||||
*/
|
||||
class AccountViewController: UIViewController
|
||||
{
|
||||
@IBOutlet var vLogin: UIView!
|
||||
@IBOutlet var vManage: UIView!
|
||||
|
||||
// For instance references
|
||||
static var this: AccountViewController!
|
||||
|
||||
/**
|
||||
Called when the user switch to this tab
|
||||
*/
|
||||
override func viewDidLoad()
|
||||
{
|
||||
// Static instance reference
|
||||
AccountViewController.this = self
|
||||
|
||||
// Check if already registered/logged in
|
||||
if localStorage.string(forKey: "id") != nil { login() }
|
||||
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
/**
|
||||
Login from the account page
|
||||
*/
|
||||
func login()
|
||||
{
|
||||
vLogin.isHidden = true
|
||||
vManage.isHidden = false
|
||||
ManageVC.this.display()
|
||||
}
|
||||
|
||||
/**
|
||||
Logout
|
||||
*/
|
||||
func logout()
|
||||
{
|
||||
// Remove login info
|
||||
["id", "user", "pass", "family"].forEach { localStorage.removeObject(forKey: $0) }
|
||||
|
||||
// Switch UI
|
||||
vLogin.isHidden = false
|
||||
vManage.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
View controller for registration and login
|
||||
*/
|
||||
class LoginVC: UIViewController
|
||||
{
|
||||
@IBOutlet weak var username: UITextField!
|
||||
@IBOutlet weak var password: UITextField!
|
||||
|
||||
/**
|
||||
Send user login/registration request
|
||||
|
||||
- Parameter login: True: Login, False: Register
|
||||
*/
|
||||
func userRequest(login: Bool)
|
||||
{
|
||||
// Verify username and password
|
||||
guard let name = username.text, name ~= "[A-Za-z0-9_-]{3,16}" else
|
||||
{
|
||||
msg("Username Invalid", "Username must be 3 to 16 characters long, and must only contain a-z, 0-9, underscore, and minus signs (-).")
|
||||
return
|
||||
}
|
||||
guard let pass = password.text, pass.count >= 8 else
|
||||
{
|
||||
msg("Password Invalid", "Password must be more than or equal to 8 characters long")
|
||||
return
|
||||
}
|
||||
|
||||
// Error messages
|
||||
let errors = ["409 - [\"A0111\"]": "Account already exists, please login instead.",
|
||||
"401 -": "Incorrect username/password",
|
||||
"404 -": "Username does not exist in the database",
|
||||
"406 - [\"A0101\"]": "Username invalid."
|
||||
]
|
||||
|
||||
// Send register request
|
||||
sendReq(login ? APIs.login : APIs.register,
|
||||
title: login ? "Logging in..." : "Registering...", errors: errors,
|
||||
params: ["username": name, "password": pass.sha256])
|
||||
{
|
||||
// Store username and password
|
||||
localStorage["name"] = name
|
||||
localStorage["pass"] = pass.sha256
|
||||
localStorage["id"] = $0
|
||||
|
||||
send(APIs.familyGet)
|
||||
{
|
||||
$0.localSave()
|
||||
self.loginSuccess(login)
|
||||
}
|
||||
err: { it in print(it); self.loginSuccess(login) }
|
||||
}
|
||||
}
|
||||
|
||||
private func loginSuccess(_ login: Bool)
|
||||
{
|
||||
ui
|
||||
{
|
||||
// Send feedback
|
||||
if login { self.msg("Login success!", "Now you can use account features, yay!") }
|
||||
else { self.msg("Registration success!", "Now you have an account, yay!") }
|
||||
|
||||
// Hide registration and show account detail view
|
||||
AccountViewController.this.login()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks register
|
||||
*/
|
||||
@IBAction func register(_ sender: Any)
|
||||
{
|
||||
userRequest(login: false)
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks login
|
||||
*/
|
||||
@IBAction func login(_ sender: Any)
|
||||
{
|
||||
userRequest(login: true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Account manage view controller
|
||||
*/
|
||||
class ManageVC: UIViewController
|
||||
{
|
||||
static var this: ManageVC!
|
||||
|
||||
@IBOutlet weak var lUsername: UILabel!
|
||||
@IBOutlet weak var lJoinDate: UILabel!
|
||||
|
||||
@IBOutlet weak var lCurrentFamily: UILabel!
|
||||
|
||||
/**
|
||||
Called when the user switched to the account tab (whether the view container is hidden or not)
|
||||
*/
|
||||
override func viewDidLoad()
|
||||
{
|
||||
// Static reference
|
||||
ManageVC.this = self
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
/**
|
||||
Display account info
|
||||
*/
|
||||
func display()
|
||||
{
|
||||
lUsername.text = localStorage.string(forKey: "name")
|
||||
// TODO: Implement join date (not important)
|
||||
lJoinDate.text = localStorage.string(forKey: "id")
|
||||
|
||||
// Display family name
|
||||
if let family = Family.fromLocal()
|
||||
{
|
||||
lCurrentFamily.text = family.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the upload backup button
|
||||
*/
|
||||
@IBAction func uploadBackup(_ sender: Any)
|
||||
{
|
||||
sendReq(APIs.uploadConfig, title: "Uploading...", params: ["config": localStorage.string(forKey: "alarms")!])
|
||||
{ it in self.msg("Success!", "You're backed up.") }
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the download backup button
|
||||
*/
|
||||
@IBAction func downloadBackup(_ sender: Any)
|
||||
{
|
||||
sendReq(APIs.downloadConfig, title: "Downloading...")
|
||||
{
|
||||
// Save backup
|
||||
localStorage.setValue($0, forKey: "alarms")
|
||||
|
||||
// Update UI
|
||||
AlarmViewController.staticTable?.reloadData()
|
||||
|
||||
self.msg("Success!", "You're restored your backup.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the logout button
|
||||
*/
|
||||
@IBAction func logout(_ sender: Any)
|
||||
{
|
||||
AccountViewController.this.logout()
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the delete account button
|
||||
*/
|
||||
@IBAction func deleteAccount(_ sender: Any)
|
||||
{
|
||||
sendReq(APIs.delete, title: "Deleting...")
|
||||
{
|
||||
print("Deleted! \($0)")
|
||||
self.msg("Deleted!", "You are erased from our database, you no longer exist.")
|
||||
self.logout(sender)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Family view controller that displays family info or create/join family buttons
|
||||
*/
|
||||
class FamilyVC: UIViewController
|
||||
{
|
||||
static var this: FamilyVC!
|
||||
|
||||
// No family view - prompt to create/join a family
|
||||
@IBOutlet weak var noFamilyView: UIView!
|
||||
var createMode: Bool!
|
||||
|
||||
@IBAction func btnCreate(_ sender: Any)
|
||||
{
|
||||
createMode = true
|
||||
performSegue(withIdentifier: "family-create-join", sender: nil)
|
||||
}
|
||||
|
||||
@IBAction func btnJoin(_ sender: Any)
|
||||
{
|
||||
createMode = false
|
||||
performSegue(withIdentifier: "family-create-join", sender: nil)
|
||||
}
|
||||
|
||||
@IBSegueAction func segueCreateJoin(_ coder: NSCoder) -> FamilyCreateJoinVC?
|
||||
{
|
||||
return FamilyCreateJoinVC(coder: coder, create: createMode)
|
||||
}
|
||||
|
||||
// Family view - Display family information and controls
|
||||
@IBOutlet weak var familyView: UIView!
|
||||
@IBOutlet weak var table: UITableView!
|
||||
|
||||
/**
|
||||
Called when information is updated
|
||||
*/
|
||||
override func viewDidLoad()
|
||||
{
|
||||
FamilyVC.this = self
|
||||
|
||||
if let _ = Family.fromLocal()
|
||||
{
|
||||
// Family exists
|
||||
noFamilyView.isHidden = true
|
||||
familyView.isHidden = false
|
||||
|
||||
table.dataSource = self
|
||||
table.delegate = self
|
||||
}
|
||||
else
|
||||
{
|
||||
// Family doesn't exist
|
||||
noFamilyView.isHidden = false
|
||||
familyView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the refresh button
|
||||
*/
|
||||
@IBAction func btnRefresh(_ sender: Any)
|
||||
{
|
||||
sendReq(APIs.familyGet, title: "Updating family...")
|
||||
{
|
||||
$0.localSave()
|
||||
self.table.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the change pin button
|
||||
*/
|
||||
@IBAction func btnChangePin(_ sender: Any)
|
||||
{
|
||||
self.enterPin("Change Pin", "Enter your OLD pin:") { oldPin in
|
||||
|
||||
self.enterPin("Change Pin", "Enter your NEW pin:") { newPin in
|
||||
|
||||
guard newPin.count >= 4 else { self.msg("Pin Too Weak", "Your family pin must be 4 numbers or more."); return }
|
||||
|
||||
self.sendReq(APIs.familyChangePin, title: "Updating Pin...", params: ["oldPin": oldPin, "newPin": newPin]) { it in
|
||||
|
||||
self.msg("Update Success!", "Your family pin is updated.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks the leave or delete family button
|
||||
*/
|
||||
@IBAction func btnLeave(_ sender: UIButton)
|
||||
{
|
||||
let i = sender.tag
|
||||
let action = ["Leave", "Delete"][i]
|
||||
let title = ["Leaving...", "Deleting..."][i]
|
||||
let msg = ["You left the family.", "You deleted the family."][i]
|
||||
|
||||
enterPin()
|
||||
{
|
||||
self.sendReq(APIs.familyAction, title: title, params: ["pin": $0, "action": action]) { it in
|
||||
|
||||
// Leave or delete, clear local storage's family section
|
||||
if i == 0 || i == 1 { localStorage.removeObject(forKey: "family") }
|
||||
|
||||
self.msg("\(action) Success!", msg)
|
||||
{
|
||||
self.viewDidLoad()
|
||||
AccountViewController.this.login()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Table data source
|
||||
*/
|
||||
extension FamilyVC: UITableViewDelegate, UITableViewDataSource
|
||||
{
|
||||
/**
|
||||
Define row count
|
||||
*/
|
||||
func tableView(_ _: UITableView, numberOfRowsInSection _: Int) -> Int
|
||||
{
|
||||
guard let family = Family.fromLocal() else { return 0 }
|
||||
return family.membersList.count
|
||||
}
|
||||
|
||||
/**
|
||||
Set cell at i
|
||||
*/
|
||||
func tableView(_ view: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
||||
{
|
||||
let cell = view.dequeueReusableCell(withIdentifier: "family-member-cell", for: i)
|
||||
cell.textLabel?.text = Family.fromLocal()!.membersList[i.row]
|
||||
return cell
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks on the cell at i
|
||||
*/
|
||||
func tableView(_ view: UITableView, didSelectRowAt i: IndexPath)
|
||||
{
|
||||
print(i.row)
|
||||
view.deselectRow(at: i, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Create or join a family
|
||||
*/
|
||||
class FamilyCreateJoinVC: UIViewController
|
||||
{
|
||||
let createMode: Bool
|
||||
@IBOutlet weak var lFamilyNameOrId: UILabel!
|
||||
@IBOutlet weak var bCreateJoin: UIButton!
|
||||
@IBOutlet weak var tNameOrId: UITextField!
|
||||
@IBOutlet weak var tPin: UITextField!
|
||||
|
||||
/**
|
||||
Pass in create mode from FamilyVC
|
||||
*/
|
||||
init?(coder: NSCoder, create: Bool)
|
||||
{
|
||||
createMode = create
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
/**
|
||||
On load
|
||||
*/
|
||||
override func viewDidLoad()
|
||||
{
|
||||
// Set UI according to createMode
|
||||
lFamilyNameOrId.text = createMode ? "Family Name" : "Family ID"
|
||||
bCreateJoin.setTitle(createMode ? "Create" : "Join", for: .normal)
|
||||
tNameOrId.keyboardType = createMode ? .default : .numberPad
|
||||
|
||||
// Default name
|
||||
if createMode
|
||||
{
|
||||
tNameOrId.text = "\(localStorage.string(forKey: "name")!)'s Family"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called after successfully joining or creating a family.
|
||||
*/
|
||||
func afterFamilyChange()
|
||||
{
|
||||
self.dismiss()
|
||||
FamilyVC.this.viewDidLoad()
|
||||
AccountViewController.this.login()
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks create or join button
|
||||
*/
|
||||
@IBAction func btnCreateOrJoin(_ sender: Any)
|
||||
{
|
||||
// Check pin
|
||||
guard let pin = tPin.text, pin.count >= 4 else { msg("Pin Too Weak", "Your family pin must be 4 numbers or more."); return }
|
||||
|
||||
if createMode
|
||||
{
|
||||
guard let name = tNameOrId.text, !name.isEmpty else { msg("Name Empty", "You must enter a family name"); return }
|
||||
|
||||
// Create family
|
||||
sendReq(APIs.familyCreate, title: "Creating...", params: ["name": name, "pin": pin])
|
||||
{
|
||||
// Save
|
||||
$0.localSave()
|
||||
|
||||
// Send success message
|
||||
self.msg("Created!", "Your family ID is \($0.fid)") { self.afterFamilyChange() }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
guard let idString = tNameOrId.text, !idString.isEmpty, let id = Int(idString) else
|
||||
{ msg("ID Incorrect", "Please make sure your ID is an positive integer."); return }
|
||||
|
||||
// Join family
|
||||
sendReq(APIs.familyAction, title: "Joining...", params: ["fid": String(id), "pin": pin, "action": "join"])
|
||||
{
|
||||
// Save
|
||||
$0.localSave()
|
||||
|
||||
// Send success message
|
||||
self.msg("Joined!", "Your family ID is \($0.fid)") { self.afterFamilyChange() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
View controller for adding an alarm to a fmaily member
|
||||
*/
|
||||
class FamilyAddAlarmVC: UIViewController
|
||||
{
|
||||
@IBOutlet weak var table: UITableView!
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
table.delegate = self
|
||||
table.dataSource = self
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Table data source
|
||||
*/
|
||||
extension FamilyAddAlarmVC: UITableViewDelegate, UITableViewDataSource
|
||||
{
|
||||
/**
|
||||
Define row count
|
||||
*/
|
||||
func tableView(_ _: UITableView, numberOfRowsInSection _: Int) -> Int
|
||||
{
|
||||
return Alarms.fromLocal().list.count
|
||||
}
|
||||
|
||||
/**
|
||||
Set cell at i
|
||||
*/
|
||||
func tableView(_ view: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
||||
{
|
||||
let cell = view.dequeueReusableCell(withIdentifier: "family-alarm-cell", for: i)
|
||||
let alarm = Alarms.fromLocal().list[i.row]
|
||||
cell.textLabel?.text = String(format: "%i:%02i", alarm.hour, alarm.minute)
|
||||
return cell
|
||||
}
|
||||
|
||||
/**
|
||||
Called when the user clicks on the cell at i
|
||||
*/
|
||||
func tableView(_ view: UITableView, didSelectRowAt i: IndexPath)
|
||||
{
|
||||
view.deselectRow(at: i, animated: true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user