Skip to content

Commit

Permalink
More work on synchronization.
Browse files Browse the repository at this point in the history
  • Loading branch information
msimms committed Mar 8, 2024
1 parent ce362b4 commit fae9ef7
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 17 deletions.
1 change: 1 addition & 0 deletions Common/Notifications.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#define NOTIFICATION_NAME_ACTIVITY_STARTED "ActivityStarted" // The user has started an activity
#define NOTIFICATION_NAME_ACTIVITY_STOPPED "ActivityStopped" // The user has stopped an activity
#define NOTIFICATION_NAME_GEAR_LIST_UPDATED "GearListUpdated" // An updated gear list was returned from the (optional) server
#define NOTIFICATION_NAME_RACE_LIST_UPDATED "RaceListUpdated" // The race calendar from the (optional) server was updated
#define NOTIFICATION_NAME_PLANNED_WORKOUTS_UPDATED "PlannedWorkoutsUpdated" // The planned workouts list from the (optional) server was updated
#define NOTIFICATION_NAME_INTERVAL_SESSIONS_UPDATED "IntervalSessionsUpdated" // The interval sessions list from the (optional) server was updated
#define NOTIFICATION_NAME_INTERVAL_UPDATED "IntervalUpdated"
Expand Down
7 changes: 7 additions & 0 deletions Common/Params.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@
#define PARAM_WORKOUT_INTERVALS "intervals"
#define PARAM_WORKOUT_LAST_UPDATED_TIME "last updated time"

// Goals
#define PARAM_RACE_ID "race id"
#define PARAM_RACE_DATE "race date"
#define PARAM_RACE_IMPORTANCE "race importance"
#define PARAM_RACE_DISTANCE "race distance"
#define PARAM_RACE_NAME "race name"

// Interval Session
#define PARAM_INTERVAL_ID "id"
#define PARAM_INTERVAL_NAME "name"
Expand Down
1 change: 1 addition & 0 deletions Common/Urls.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define REMOTE_API_CREATE_GEAR_URL "api/1.0/create_gear"
#define REMOTE_API_UPDATE_GEAR_URL "api/1.0/update_gear"
#define REMOTE_API_DELETE_GEAR_URL "api/1.0/delete_gear"
#define REMOTE_API_LIST_RACES_URL "api/1.0/list_races"
#define REMOTE_API_LIST_PLANNED_WORKOUTS_URL "api/1.0/list_planned_workouts"
#define REMOTE_API_LIST_INTERVAL_WORKOUTS_URL "api/1.0/list_interval_workouts"
#define REMOTE_API_LIST_PACE_PLANS_URL "api/1.0/list_pace_plans"
Expand Down
56 changes: 51 additions & 5 deletions IOS/Controller/CommonApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class CommonApp : ObservableObject {
NotificationCenter.default.addObserver(self, selector: #selector(self.requestUserSettingsResponse), name: Notification.Name(rawValue: NOTIFICATION_NAME_REQUEST_USER_SETTINGS_RESULT), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.downloadedActivityReceived), name: Notification.Name(rawValue: NOTIFICATION_NAME_DOWNLOADED_ACTIVITY), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.gearListUpdated), name: Notification.Name(rawValue: NOTIFICATION_NAME_GEAR_LIST_UPDATED), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.raceListUpdated), name: Notification.Name(rawValue: NOTIFICATION_NAME_RACE_LIST_UPDATED), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.plannedWorkoutsUpdated), name: Notification.Name(rawValue: NOTIFICATION_NAME_PLANNED_WORKOUTS_UPDATED), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.intervalSessionsUpdated), name: Notification.Name(rawValue: NOTIFICATION_NAME_INTERVAL_SESSIONS_UPDATED), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.pacePlansUpdated), name: Notification.Name(rawValue: NOTIFICATION_NAME_PACE_PLANS_UPDATED), object: nil)
Expand Down Expand Up @@ -291,11 +292,11 @@ class CommonApp : ObservableObject {

// Parse the URL.
let components = URLComponents(url: requestUrl, resolvingAgainstBaseURL: false)!

if let queryItems = components.queryItems {
var activityId: String?
var exportFormat: String?

// Grab the activity ID and file format out of the URL parameters.
for queryItem in queryItems {
if queryItem.name == PARAM_ACTIVITY_ID {
Expand All @@ -305,22 +306,47 @@ class CommonApp : ObservableObject {
exportFormat = queryItem.value!
}
}

if activityId != nil && exportFormat != nil {
let directory = NSTemporaryDirectory()
let fileName = NSUUID().uuidString + "." + exportFormat!
let fullUrl = NSURL.fileURL(withPathComponents: [directory, fileName])

if fullUrl != nil {
try responseData.write(to: fullUrl!)

if ImportActivityFromFile(fullUrl?.absoluteString, "", activityId) == false {

if ImportActivityFromFile(fullUrl?.absoluteString, "", activityId) {
var startTime: time_t = 0
var endTime: time_t = 0

LoadHistoricalActivity(activityId)
if GetHistoricalActivityStartAndEndTime(activityId, &startTime, &endTime) {
let lastSynchedActivityTime = Preferences.lastServerImportTime()

if startTime > lastSynchedActivityTime {
Preferences.setLastServerImportTime(value: startTime)
}
}
}
else {
NSLog("Import failed!")
}
try FileManager.default.removeItem(at: fullUrl!)
}
else {
NSLog("Cannot find the downloaded file!")
}
}
else {
NSLog("Activity ID and Export Format not provided!")
}
}
else {
NSLog("Cannot parse downloaded file URL!")
}
}
else {
NSLog("Response URL or Response Data not provided!")
}
}
}
Expand Down Expand Up @@ -349,6 +375,26 @@ class CommonApp : ObservableObject {
}
}

@objc func raceListUpdated(notification: NSNotification) {
do {
if let data = notification.object as? Dictionary<String, AnyObject> {
if let responseData = data[KEY_NAME_RESPONSE_DATA] as? Data {
let workoutsVM: WorkoutsVM = WorkoutsVM()
let raceList = try JSONSerialization.jsonObject(with: responseData, options: []) as! [Any]

for race in raceList {
if let raceDict = race as? Dictionary<String, AnyObject> {
workoutsVM.importRaceCalendar(dict: raceDict)
}
}
}
}
}
catch {
NSLog(error.localizedDescription)
}
}

@objc func plannedWorkoutsUpdated(notification: NSNotification) {
do {
if let data = notification.object as? Dictionary<String, AnyObject> {
Expand Down
22 changes: 14 additions & 8 deletions IOS/Model/ApiClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ class ApiClient : ObservableObject {
let notification = Notification(name: Notification.Name(rawValue: NOTIFICATION_NAME_GEAR_LIST_UPDATED), object: downloadedData)
NotificationCenter.default.post(notification)
}
else if url.contains(REMOTE_API_LIST_RACES_URL) {
let notification = Notification(name: Notification.Name(rawValue: NOTIFICATION_NAME_RACE_LIST_UPDATED), object: downloadedData)
NotificationCenter.default.post(notification)
}
else if url.contains(REMOTE_API_LIST_PLANNED_WORKOUTS_URL) {
let notification = Notification(name: Notification.Name(rawValue: NOTIFICATION_NAME_PLANNED_WORKOUTS_UPDATED), object: downloadedData)
NotificationCenter.default.post(notification)
Expand Down Expand Up @@ -292,6 +296,11 @@ class ApiClient : ObservableObject {
return self.makeRequest(url: urlStr, method: "DELETE", data: deleteDict)
}

func listRaces() -> Bool {
let urlStr = self.buildApiUrlStr(request: REMOTE_API_LIST_RACES_URL)
return self.makeRequest(url: urlStr, method: "GET", data: [:])
}

func listPlannedWorkouts() -> Bool {
let urlStr = self.buildApiUrlStr(request: REMOTE_API_LIST_PLANNED_WORKOUTS_URL)
return self.makeRequest(url: urlStr, method: "GET", data: [:])
Expand Down Expand Up @@ -634,12 +643,14 @@ class ApiClient : ObservableObject {
if now - lastServerSync > 60 {
let deviceId = Preferences.uuid()
if deviceId != nil {

// Associate this device with the user.
result = self.claimDevice(deviceId: deviceId!)

// Get all the things.
#if !os(watchOS)
result = result && self.listGear()
result = result && self.listRaces()
result = result && self.listPlannedWorkouts()
result = result && self.requestUserSettings(settings: [WORKOUT_INPUT_GOAL_TYPE])
#endif
Expand All @@ -653,14 +664,9 @@ class ApiClient : ObservableObject {
result = result && self.sendPacePlansToServer()
#endif

// Ask for the server's activity list from the last week.
let ONE_WEEK = 60 * 60 * 24 * 7
if lastServerSync > ONE_WEEK {
result = result && self.requestUpdatesSince(timestamp: Date(timeIntervalSince1970: TimeInterval(lastServerSync - ONE_WEEK)))
}
else {
result = result && self.requestUpdatesSince(timestamp: Date(timeIntervalSince1970: 0))
}
// Ask for the server's activity list, from the time of the last activity received.
let lastServerImport = Preferences.lastServerImportTime()
result = result && self.requestUpdatesSince(timestamp: Date(timeIntervalSince1970: TimeInterval(lastServerImport)))

Preferences.setLastServerSyncTime(value: now)
}
Expand Down
15 changes: 13 additions & 2 deletions IOS/Model/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ let PREF_NAME_WORKOUTS_CAN_INCLUDE_RUNNING = "Workouts Can Include Runn
let PREF_NAME_POOL_LENGTH = "Pool Length"
let PREF_NAME_POOL_LENGTH_UNITS = "Pool Length Units"
let PREF_NAME_LAST_SERVER_SYNC_TIME = "Last Server Sync Time"
let PREF_NAME_LAST_SERVER_IMPORT_TIME = "Last Server Import Time"
let PREF_NAME_MOST_RECENT_ACTIVITY_DESCRIPTION = "Most Recent Activity Description"

let PREF_NAME_METRIC = "units_metric"
Expand Down Expand Up @@ -560,7 +561,12 @@ class Preferences {
let mydefaults: UserDefaults = UserDefaults.standard
return mydefaults.integer(forKey: PREF_NAME_LAST_SERVER_SYNC_TIME)
}


static func lastServerImportTime() -> time_t {
let mydefaults: UserDefaults = UserDefaults.standard
return mydefaults.integer(forKey: PREF_NAME_LAST_SERVER_IMPORT_TIME)
}

//
// Set methods
//
Expand Down Expand Up @@ -844,7 +850,12 @@ class Preferences {
let mydefaults: UserDefaults = UserDefaults.standard
mydefaults.set(value, forKey: PREF_NAME_LAST_SERVER_SYNC_TIME)
}


static func setLastServerImportTime(value: time_t) {
let mydefaults: UserDefaults = UserDefaults.standard
mydefaults.set(value, forKey: PREF_NAME_LAST_SERVER_IMPORT_TIME)
}

//
// Methods for managing the list of accessories
//
Expand Down
23 changes: 21 additions & 2 deletions View Models/WorkoutsVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,32 @@ class WorkoutsVM : ObservableObject {
func importWorkoutFromDict(dict: Dictionary<String, Any>) throws {
if let workoutId = dict[PARAM_WORKOUT_ID] as? String,
let workoutTypeStr = dict[PARAM_WORKOUT_WORKOUT_TYPE] as? String,
let activityType = dict[PARAM_WORKOUT_ACTIVITY_TYPE] as? String,
let activityTypeStr = dict[PARAM_WORKOUT_ACTIVITY_TYPE] as? String,
let scheduledTime = dict[PARAM_WORKOUT_SCHEDULED_TIME] as? time_t {

let estimatedIntensityScore = dict[PARAM_WORKOUT_ESTIMATED_INTENSITY] as? Double ?? 0.0
let workoutType = WorkoutTypeStrToEnum(workoutTypeStr)

CreateWorkout(workoutId, workoutType, activityType, estimatedIntensityScore, scheduledTime)
CreateWorkout(workoutId, workoutType, activityTypeStr, estimatedIntensityScore, scheduledTime)
}
}

func importRaceCalendar(dict: Dictionary<String, Any>) {
if let raceDate = dict[PARAM_RACE_DATE] as? time_t,
let raceDistance = dict[PARAM_RACE_DISTANCE] as? String {

// Ignore anything that has already passed.
let now = Date()
if raceDate > time_t(now.timeIntervalSince1970) {
let goal = WorkoutsVM.workoutGoalStringToEnum(goalStr: raceDistance)
let currentGoal = Preferences.workoutGoal()
let currentGoalDate = Preferences.workoutGoalDate()

if currentGoal == GOAL_FITNESS || raceDate < currentGoalDate {
Preferences.setWorkoutGoal(value: goal)
Preferences.setWorkoutGoalDate(value: raceDate)
}
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions iPhone App/HistoryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SwiftUI

struct HistoryView: View {
@ObservedObject private var historyVM = HistoryVM()
@State var displayedDates : Set<Date> = []

let dateFormatter: DateFormatter = {
let df = DateFormatter()
Expand All @@ -17,6 +18,9 @@ struct HistoryView: View {

private func loadHistory() {
DispatchQueue.global(qos: .userInitiated).async {
if let updatesSince = self.displayedDates.min() {
let _ = ApiClient.shared.requestUpdatesSince(timestamp: updatesSince)
}
self.historyVM.buildHistoricalActivitiesList(createAllObjects: false)
}
}
Expand Down Expand Up @@ -62,6 +66,10 @@ struct HistoryView: View {
}
.onAppear() {
item.requestMetadata()
self.displayedDates.insert(item.startTime)
}
.onDisappear {
self.displayedDates.remove(item.startTime)
}
}
}
Expand Down

0 comments on commit fae9ef7

Please sign in to comment.