Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jzecm wal local sync 958 #1010

Merged
merged 6 commits into from
Oct 9, 2024
Merged

Jzecm wal local sync 958 #1010

merged 6 commits into from
Oct 9, 2024

Conversation

beastoin
Copy link
Collaborator

@beastoin beastoin commented Oct 9, 2024

Summary by Entelligence.AI

  • New Feature: Introduced an "Experimental" section in settings with a toggle for the "Local Sync" feature, allowing users to enable or disable local synchronization of memories.
  • New Feature: Added internet connection status monitoring to enhance the app's responsiveness to network changes.
  • Refactor: Updated the Write-Ahead Logging (WAL) support check and added a method to set WAL support status, improving the app's data integrity mechanisms.
  • New Feature: Implemented a utility function to convert seconds into HH:MM:SS format, enhancing the display of time-related information.

Copy link

github-actions bot commented Oct 9, 2024

Image description Entelligence.AI

Walkthrough

This update introduces local synchronization capabilities, internet connection status monitoring, and a new time format conversion utility. It also includes UI enhancements for managing the local sync feature and modifies the WebSocket service pool's start method.

Changes

Files Summary
app/lib/backend/preferences.dart,
app/lib/providers/developer_mode_provider.dart,
app/lib/main.dart,
app/lib/pages/memories/page.dart,
app/lib/pages/memories/widgets/local_sync.dart,
app/lib/pages/settings/developer.dart
Introduced local synchronization feature with UI management in settings. Added a new provider for developer mode and updated shared preferences to handle local sync status.
app/lib/providers/capture_provider.dart,
app/lib/services/sockets.dart
Added internet connection status monitoring and updated the WebSocket support check. The start method in the SocketServicePool class was modified to an empty implementation.
app/lib/utils/other/string_utils.dart Added a new function to convert seconds to HH:MM:SS format.

"In the land of code, where logic is king, 🤴

A new sync feature, joy it will bring. 🎉

With connection checks and time well dressed, ⏰

This update surely is one of the best! 👍"


Uplevel your code reviews with Entelligence.AI Pro

Entelligence.AI Pro

If you like this project, please support us by purchasing the Pro version. The Pro version has advanced context, superior noise reduction and several proprietary improvements compared to the open source version. Moreover, Entelligence.AI Pro is free for open source projects.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

Commits Files that changed from the base of the PR and between 74cda7e and f19b141 commits.
Files selected (9)
  • app/lib/backend/preferences.dart (1)
  • app/lib/main.dart (2)
  • app/lib/pages/memories/page.dart (3)
  • app/lib/pages/memories/widgets/local_sync.dart (1)
  • app/lib/pages/settings/developer.dart (1)
  • app/lib/providers/capture_provider.dart (8)
  • app/lib/providers/developer_mode_provider.dart (3)
  • app/lib/services/sockets.dart (1)
  • app/lib/utils/other/string_utils.dart (1)
Files skipped from review due to trivial changes (3)
  • app/lib/pages/settings/developer.dart
  • app/lib/services/sockets.dart
  • app/lib/utils/other/string_utils.dart
Review comments generated (22)
  • Review: 17
  • LGTM: 5

Tips

Chat with Image description Entelligence.AI Bot (@Entelligence.AI)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @Entelligence.AI in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @Entelligence.AI: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +19 to +26
bool localSyncEnabled = false;

void initialize() {
gcpCredentialsController.text = SharedPreferencesUtil().gcpCredentials;
gcpBucketNameController.text = SharedPreferencesUtil().gcpBucketName;
webhookOnMemoryCreated.text = SharedPreferencesUtil().webhookOnMemoryCreated;
webhookOnTranscriptReceived.text = SharedPreferencesUtil().webhookOnTranscriptReceived;
localSyncEnabled = SharedPreferencesUtil().localSyncEnabled;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The initialize() method is not asynchronous, but it's calling SharedPreferencesUtil() which might involve disk I/O operations. This could potentially block the main thread and degrade the app performance. Consider making this method asynchronous and use await with SharedPreferencesUtil() calls.

-   void initialize() {
+   Future<void> initialize() async {
     gcpCredentialsController.text = SharedPreferencesUtil().gcpCredentials;
     gcpBucketNameController.text = SharedPreferencesUtil().gcpBucketName;
     webhookOnMemoryCreated.text = SharedPreferencesUtil().webhookOnMemoryCreated;
     webhookOnTranscriptReceived.text = SharedPreferencesUtil().webhookOnTranscriptReceived;
     localSyncEnabled = await SharedPreferencesUtil().localSyncEnabled;
     notifyListeners();
   }

Comment on lines +75 to +78
void onLocalSyncEnabledChanged(var value) {
localSyncEnabled = value;
notifyListeners();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The onLocalSyncEnabledChanged method does not validate the input value before assigning it to localSyncEnabled. It's a good practice to validate inputs in setter methods to prevent potential issues. If value is expected to be a boolean, consider adding a type check.

-  void onLocalSyncEnabledChanged(var value) {
+  void onLocalSyncEnabledChanged(bool value) {
     if (value is bool) {
       localSyncEnabled = value;
       notifyListeners();
     } else {
       throw ArgumentError('Expected a boolean value');
     }
   }

Comment on lines 400 to 403
set wals(List<Wal> wals) {
final List<String> value = wals.map((e) => jsonEncode(e.toJson())).toList();
saveStringList('v3/wals', value);
saveStringList('wals', value);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The setter for wals is missing null safety checks. If a null list is passed, it will cause a NullPointerException at the map function.

- set wals(List<Wal> wals) {
-   final List<String> value = wals.map((e) => jsonEncode(e.toJson())).toList();
-   saveStringList('wals', value);
- }
+ set wals(List<Wal> wals) {
+   if (wals != null) {
+     final List<String> value = wals.map((e) => jsonEncode(e.toJson())).toList();
+     saveStringList('wals', value);
+   } else {
+     saveStringList('wals', null);
+   }
+ }

Comment on lines +406 to 408
final List<String> value = getStringList('wals') ?? [];
return Wal.fromJsonList(value.map((e) => jsonDecode(e)).toList());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

Similar to the setter, the getter for wals should also have null safety checks. If getStringList('wals') returns null, it will cause a NullPointerException at the map function.

- List<Wal> get wals {
-   final List<String> value = getStringList('wals') ?? [];
-   return Wal.fromJsonList(value.map((e) => jsonDecode(e)).toList());
- }
+ List<Wal> get wals {
+   final List<String> value = getStringList('wals');
+   if (value != null) {
+     return Wal.fromJsonList(value.map((e) => jsonDecode(e)).toList());
+   } else {
+     return null;
+   }
+ }

@@ -67,7 +68,7 @@ class _MemoriesPageState extends State<MemoriesPage> with AutomaticKeepAliveClie

@override
Widget build(BuildContext context) {
print('building memories page');
debugPrint('building memories page');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The use of debugPrint instead of print is a good practice in Flutter, especially for logging during development. It allows you to turn off these logs in production by simply setting debugPrint = null;. However, it's important to note that this change doesn't affect the functionality of the code.

-    print('building memories page');
+    debugPrint('building memories page');

@@ -295,6 +311,7 @@ class CaptureProvider extends ChangeNotifier
_bleBytesStream?.cancel();
_socket?.unsubscribe(this);
_keepAliveTimer?.cancel();
_internetStatusListener?.cancel();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The cancellation of _internetStatusListener has been added to the dispose() method. This is a good practice as it prevents potential memory leaks.

Comment on lines +488 to +497
void onInternetSatusChanged(InternetStatus status) {
debugPrint("[SocketService] Internet connection changed $status");
_internetStatus = status;
notifyListeners();
}

void setIsWalSupported(bool value) {
_isWalSupported = value;
notifyListeners();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

New methods onInternetSatusChanged and setIsWalSupported have been added. These methods update the respective variables and notify listeners of the changes. This is a good practice as it ensures that any changes to these variables are propagated throughout the application. However, there's a typo in onInternetSatusChanged - it should be onInternetStatusChanged. Here's the corrected code:

- void onInternetSatusChanged(InternetStatus status) {
+ void onInternetStatusChanged(InternetStatus status) {
    debugPrint("[SocketService] Internet connection changed $status");
    _internetStatus = status;
    notifyListeners();
  }

Comment on lines +15 to +18
const LocalSyncWidget({super.key});

@override
State<LocalSyncWidget> createState() => _LocalSyncWidgetState();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The super.key in the constructor of LocalSyncWidget is not defined. It should be replaced with Key? key and passed to the super class.

-  const LocalSyncWidget({super.key});
+  const LocalSyncWidget({Key? key}) : super(key: key);

Comment on lines +32 to +36
@override
void dispose() {
_missSecondsInEstTimer?.cancel();
super.dispose();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

Good practice to cancel timers in the dispose method to prevent memory leaks.

Comment on lines 38 to 119
@override
Widget build(BuildContext context) {
return Consumer3<MemoryProvider, CaptureProvider, DeveloperModeProvider>(
builder: (context, provider, captureProvider, devModeProvider, child) {
if (provider.missingWalsInSeconds > 120) {
_status = LocalSyncStatus.flush;
_missSeconds = max(_missSeconds, provider.missingWalsInSeconds); // est. good for ux
} else if (!captureProvider.isWalSupported) {
_status = LocalSyncStatus.disabled;
_missSecondsInEstTimer?.cancel();
} else if ((!captureProvider.transcriptServiceReady && captureProvider.recordingDeviceServiceReady) ||
provider.missingWalsInSeconds > 0) {
var previousStatus = _status;
_status = LocalSyncStatus.inProgress;

// Change state to in progress
if (previousStatus != LocalSyncStatus.inProgress) {
_missSecondsInEstTimer?.cancel();
_missSeconds = provider.missingWalsInSeconds;
_missSecondsInEstTimer = Timer.periodic(const Duration(seconds: 1), (t) {
setState(() {
_missSeconds++;
});
});
}
}

// in progress
if (_status == LocalSyncStatus.inProgress) {
return Container(
decoration: BoxDecoration(
color: Colors.grey.shade900,
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
padding: const EdgeInsets.all(16),
child: Text(
'${convertToHHMMSS(_missSeconds)} of conversation locally',
style: const TextStyle(color: Colors.white, fontSize: 16),
textAlign: TextAlign.center,
),
);
}

// ready to sync
if (_status == LocalSyncStatus.flush) {
return GestureDetector(
onTap: () {
routeToPage(context, const SyncPage());
},
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade900,
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
const Icon(Icons.download_rounded),
const SizedBox(width: 16),
Text(
'${secondsToHumanReadable(_missSeconds)} available. Sync now?',
style: const TextStyle(color: Colors.white, fontSize: 16),
),
],
),
),
],
),
),
);
}

return const SizedBox.shrink();
});
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The logic inside the build method seems complex and hard to follow. Consider breaking it down into smaller, more manageable functions. This will improve readability and maintainability. For example, you could create separate methods for each LocalSyncStatus case.

Also, there's no handling for when _status is null. You should add a default case to handle this scenario.

-  Widget build(BuildContext context) {
-    return Consumer3<MemoryProvider, CaptureProvider, DeveloperModeProvider>(
-        builder: (context, provider, captureProvider, devModeProvider, child) {
-      if (provider.missingWalsInSeconds > 120) {
-        _status = LocalSyncStatus.flush;
-        _missSeconds = max(_missSeconds, provider.missingWalsInSeconds); // est. good for ux
-      } else if (!captureProvider.isWalSupported) {
-        _status = LocalSyncStatus.disabled;
-        _missSecondsInEstTimer?.cancel();
-      } else if ((!captureProvider.transcriptServiceReady && captureProvider.recordingDeviceServiceReady) ||
-          provider.missingWalsInSeconds > 0) {
-        var previousStatus = _status;
-        _status = LocalSyncStatus.inProgress;
-
-        // Change state to in progress
-        if (previousStatus != LocalSyncStatus.inProgress) {
-          _missSecondsInEstTimer?.cancel();
-          _missSeconds = provider.missingWalsInSeconds;
-          _missSecondsInEstTimer = Timer.periodic(const Duration(seconds: 1), (t) {
-            setState(() {
-              _missSeconds++;
-            });
-          });
-        }
-      }
-
-      // in progress
-      if (_status == LocalSyncStatus.inProgress) {
-        return Container(
-          decoration: BoxDecoration(
-            color: Colors.grey.shade900,
-            borderRadius: const BorderRadius.all(Radius.circular(12)),
-          ),
-          margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
-          padding: const EdgeInsets.all(16),
-          child: Text(
-            '${convertToHHMMSS(_missSeconds)} of conversation locally',
-            style: const TextStyle(color: Colors.white, fontSize: 16),
-            textAlign: TextAlign.center,
-          ),
-        );
-      }
-
-      // ready to sync
-      if (_status == LocalSyncStatus.flush) {
-        return GestureDetector(
-          onTap: () {
-            routeToPage(context, const SyncPage());
-          },
-          child: Container(
-            decoration: BoxDecoration(
-              color: Colors.grey.shade900,
-              borderRadius: const BorderRadius.all(Radius.circular(12)),
-            ),
-            margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
-            padding: const EdgeInsets.all(16),
-            child: Row(
-              mainAxisAlignment: MainAxisAlignment.spaceBetween,
-              children: [
-                Expanded(
-                  child: Row(
-                    children: [
-                      const Icon(Icons.download_rounded),
-                      const SizedBox(width: 16),
-                      Text(
-                        '${secondsToHumanReadable(_missSeconds)} available. Sync now?',
-                        style: const TextStyle(color: Colors.white, fontSize: 16),
-                      ),
-                    ],
-                  ),
-                ),
-              ],
-            ),
-          ),
-        );
-      }
-
-      return const SizedBox.shrink();
-    });
-  }
+  Widget build(BuildContext context) {
+    return Consumer3<MemoryProvider, CaptureProvider, DeveloperModeProvider>(
+        builder: (context, provider, captureProvider, devModeProvider, child) {
+      _updateStatus(provider, captureProvider);
+
+      switch (_status) {
+        case LocalSyncStatus.inProgress:
+          return _buildInProgressWidget();
+        case LocalSyncStatus.flush:
+          return _buildFlushWidget(context);
+        case LocalSyncStatus.disabled:
+        default:
+          return const SizedBox.shrink();
+      }
+    });
+  }

@beastoin beastoin merged commit 574d2b7 into main Oct 9, 2024
1 check passed
@beastoin beastoin deleted the jzecm_wal_local_sync_958 branch October 9, 2024 04:50
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

Commits Files that changed from the base of the PR and between f19b141 and 2ae9037 commits.
Files selected (1)
  • app/lib/pages/memories/widgets/local_sync.dart (1)
Review comments generated (3)
  • Review: 1
  • LGTM: 2

Tips

Chat with Image description Entelligence.AI Bot (@Entelligence.AI)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @Entelligence.AI in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @Entelligence.AI: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +40 to +62
return Consumer2<MemoryProvider, CaptureProvider>(builder: (context, provider, captureProvider, child) {
if (provider.missingWalsInSeconds > 120) {
_status = LocalSyncStatus.flush;
_missSeconds = max(_missSeconds, provider.missingWalsInSeconds); // est. good for ux
} else if (!captureProvider.isWalSupported) {
_status = LocalSyncStatus.disabled;
_missSecondsInEstTimer?.cancel();
} else if ((!captureProvider.transcriptServiceReady && captureProvider.recordingDeviceServiceReady) ||
provider.missingWalsInSeconds > 0) {
var previousStatus = _status;
_status = LocalSyncStatus.inProgress;

// Change state to in progress
if (previousStatus != LocalSyncStatus.inProgress) {
_missSecondsInEstTimer?.cancel();
_missSeconds = provider.missingWalsInSeconds;
_missSecondsInEstTimer = Timer.periodic(const Duration(seconds: 1), (t) {
setState(() {
_missSeconds++;
});
});
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description Entelligence.AI

The logic for setting _status and _missSeconds seems to be a bit convoluted. It might be more maintainable if you encapsulate this logic into separate methods, each responsible for determining a specific status. This would make the code easier to read and test.

-      if (provider.missingWalsInSeconds > 120) {
-        _status = LocalSyncStatus.flush;
-        _missSeconds = max(_missSeconds, provider.missingWalsInSeconds); // est. good for ux
-      } else if (!captureProvider.isWalSupported) {
-        _status = LocalSyncStatus.disabled;
-        _missSecondsInEstTimer?.cancel();
-      } else if ((!captureProvider.transcriptServiceReady && captureProvider.recordingDeviceServiceReady) ||
-          provider.missingWalsInSeconds > 0) {
-        var previousStatus = _status;
-        _status = LocalSyncStatus.inProgress;
-
-        // Change state to in progress
-        if (previousStatus != LocalSyncStatus.inProgress) {
-          _missSecondsInEstTimer?.cancel();
-          _missSeconds = provider.missingWalsInSeconds;
-          _missSecondsInEstTimer = Timer.periodic(const Duration(seconds: 1), (t) {
-            setState(() {
-              _missSeconds++;
-            });
-          });
-        }
-      }

+      _updateStatus(provider, captureProvider);

And then define _updateStatus method like:

void _updateStatus(MemoryProvider provider, CaptureProvider captureProvider) {
  if (provider.missingWalsInSeconds > 120) {
    _status = LocalSyncStatus.flush;
    _missSeconds = max(_missSeconds, provider.missingWalsInSeconds); // est. good for ux
  } else if (!captureProvider.isWalSupported) {
    _status = LocalSyncStatus.disabled;
    _missSecondsInEstTimer?.cancel();
  } else if ((!captureProvider.transcriptServiceReady && captureProvider.recordingDeviceServiceReady) ||
      provider.missingWalsInSeconds > 0) {
    var previousStatus = _status;
    _status = LocalSyncStatus.inProgress;

    // Change state to in progress
    if (previousStatus != LocalSyncStatus.inProgress) {
      _missSecondsInEstTimer?.cancel();
      _missSeconds = provider.missingWalsInSeconds;
      _startMissSecondsTimer();
    }
  }
}

void _startMissSecondsTimer() {
  _missSecondsInEstTimer = Timer.periodic(const Duration(seconds: 1), (t) {
    setState(() {
      _missSeconds++;
    });
  });
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant