Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
44c584c
update pubspec
pastordee May 1, 2025
311a2ef
Added Optional Index
pastordee May 1, 2025
d7cec0f
Update parse_live_list.dart
pastordee May 1, 2025
8768bb3
Parse Live List PageView
pastordee May 1, 2025
2b22a2e
use Last
pastordee May 1, 2025
9d58cc1
Update parse_live_page_view.dart
pastordee May 1, 2025
bb500f2
Update pubspec.yaml
pastordee May 1, 2025
edddb2e
Added Pagination to ParseLiveListWidget and ParseLiveGridWidget
pastordee May 1, 2025
0c03f6f
Update parse_live_list.dart
pastordee May 1, 2025
44b601a
Update parse_live_list.dart
pastordee May 1, 2025
e573e8d
Update parse_live_list.dart
pastordee May 1, 2025
6e9256a
useAnimations
pastordee May 1, 2025
6572007
added cached
pastordee May 1, 2025
e61f294
Update parse_server_sdk_flutter.dart
pastordee May 1, 2025
f355032
Update parse_cached_live_list.dart
pastordee May 1, 2025
eec1a7b
Update parse_live_grid.dart
pastordee May 1, 2025
e4b73d2
Update parse_live_grid.dart
pastordee May 1, 2025
5162d46
Adding CachedParseLiveList to ParseLiveListPageView
pastordee May 1, 2025
f170d94
ok
pastordee May 1, 2025
97e3193
Update pubspec.yaml
pastordee May 1, 2025
81ce90d
ok
pastordee May 1, 2025
45fc594
LazyLoading in CachedParseLiveList
pastordee May 1, 2025
e014aaf
CachedParseLiveList
pastordee May 1, 2025
2410adb
ok
pastordee May 1, 2025
827351f
Fixing Lazy Loading
pastordee May 1, 2025
af6c282
offline mode
pastordee May 2, 2025
6635518
added ParseObjectOffline
pastordee May 2, 2025
e5ad704
ParseObjectOffline
pastordee May 2, 2025
c60801e
Update parse_live_list.dart
pastordee May 2, 2025
ed8ef30
Update parse_offline_object.dart
pastordee May 2, 2025
4039201
Update pubspec.yaml
pastordee May 2, 2025
e1693e9
Update pubspec.yaml
pastordee May 2, 2025
83e88c9
Update parse_offline_object.dart
pastordee May 2, 2025
c6dec80
ok
pastordee May 2, 2025
7705f0c
Update pubspec.yaml
pastordee May 2, 2025
857bfa9
Update core_store_memory.dart
pastordee May 2, 2025
e4cbaaf
Update core_store_memory.dart
pastordee May 2, 2025
bcbb4b1
Update pubspec.yaml
pastordee May 2, 2025
bef3058
Update pubspec.yaml
pastordee May 2, 2025
34aa3fd
ok
pastordee May 2, 2025
2631004
offlineMode
pastordee May 2, 2025
2571ad3
Use the factory in _loadFromCache:
pastordee May 2, 2025
4b4055c
ConnectivityHandlerMixin
pastordee May 3, 2025
7257b4c
Update parse_live_list.dart
pastordee May 3, 2025
1686c81
Update parse_live_grid.dart
pastordee May 3, 2025
a771fe7
proactivelyCacheNextPage
pastordee May 3, 2025
3719f0a
ParseLiveSliverListWidget
pastordee May 30, 2025
f0c5edc
Update parse_live_sliver_list.dart
pastordee May 30, 2025
db74d47
ParseLiveSliverGridWidget
pastordee May 30, 2025
5c1059b
Parse Dashboard Analytics Integration
pastordee Aug 6, 2025
a7d111c
ok
pastordee Aug 17, 2025
0fd55d0
Refactor analytics integration and remove clean files
pastordee Aug 17, 2025
3406e46
Update pubspec.yaml
pastordee Aug 17, 2025
d58166f
Update parse_offline_object.dart
pastordee Aug 17, 2025
c27d40f
Update dependencies and improve live list handling
pastordee Aug 17, 2025
aac8ab1
Remove trailing whitespace in pubspec.yaml files
pastordee Aug 17, 2025
b92b33c
Add offline extension tests and update Dart SDK version
pastordee Aug 17, 2025
69f12da
Update pubspec.yaml
pastordee Aug 17, 2025
c1ca15e
Fix meta dependency compatibility and use path dependency for local d…
pastordee Aug 17, 2025
de1b270
Update Parse dependencies in pubspec.yaml files
pastordee Aug 17, 2025
4b25476
Update Parse SDK source and remove connectivity_plus plugin
pastordee Aug 17, 2025
a7f080d
Merge remote-tracking branch 'upstream/master'
pastordee Nov 29, 2025
436a1dc
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Nov 29, 2025
ef4b8ed
Merge branch 'pc_server'
pastordee Nov 29, 2025
1c3b57b
Update dependencies and clean up pubspec files
pastordee Nov 29, 2025
287f94f
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Nov 29, 2025
b16529b
Replace parse_server_sdk_flutter with parse_server_sdk imports
pastordee Nov 29, 2025
afa779d
Merge branch 'pc_server'
pastordee Nov 29, 2025
182c05b
Switch to parse_server_sdk_flutter and update dependencies
pastordee Nov 29, 2025
e266199
Switch to local parse_server_sdk_flutter package
pastordee Nov 29, 2025
24ee249
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Nov 29, 2025
e5d912a
Merge branch 'pc_server'
pastordee Nov 29, 2025
cbd4c04
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Nov 30, 2025
15919de
Merge branch 'pc_server'
pastordee Nov 30, 2025
117c560
Update pubspec.yaml
pastordee Nov 30, 2025
73aef09
Update pubspec.yaml
pastordee Nov 30, 2025
e154603
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Nov 30, 2025
c07d849
Merge branch 'pc_server'
pastordee Nov 30, 2025
508318f
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Dec 4, 2025
af2dc38
Remove ethernet connectivity support
pastordee Dec 4, 2025
d5ca699
Merge branch 'pc_server'
pastordee Dec 4, 2025
bcbdc2c
Update pubspec.yaml
pastordee Dec 4, 2025
4459d53
Update pubspec.yaml
pastordee Dec 4, 2025
3b84021
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Dec 4, 2025
5887abf
Merge branch 'pc_server'
pastordee Dec 4, 2025
730d651
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee Dec 6, 2025
b9f5513
Merge branch 'pc_server'
pastordee Dec 6, 2025
3bbcd99
Add offline mode tests and update documentation
pastordee Dec 6, 2025
c2ff6d8
Merge branch 'pc_server'
pastordee Dec 6, 2025
62e80b7
Expose public state for sliver widgets and improve docs
pastordee Dec 6, 2025
609289a
fix: resolve linting issues in example app
pastordee Dec 6, 2025
da21fc8
fix: resolve linting issues in example app
pastordee Dec 6, 2025
5a14b1b
fix: resolve linting issues in example app
pastordee Dec 6, 2025
3550eb0
style: format dart files
pastordee Dec 6, 2025
c1abf93
fix: use local dart package override and remove sdk prefix from Parse…
pastordee Dec 6, 2025
17a4262
style: run dart format on flutter package
pastordee Dec 6, 2025
1df6083
chore: remove accidentally added screenshot
pastordee Dec 6, 2025
38a89c5
fix: add curly braces to if statements for lint compliance
pastordee Dec 6, 2025
09bcd4b
test: add unit tests for offline mode, analytics, connectivity mixin,…
pastordee Dec 6, 2025
57342bd
test: Add tests for improved coverage
pastordee Dec 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions PR_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Pre-Pull Request Checklist for Parse SDK Flutter

## ✅ Offline Mode Verification

### Core Offline Functionality
- [x] **Single Object Caching**: `saveToLocalCache()` works correctly
- [x] **Load Single Object**: `loadFromLocalCache()` retrieves cached objects
- [x] **Batch Save**: `saveAllToLocalCache()` efficiently saves multiple objects
- [x] **Load All Objects**: `loadAllFromLocalCache()` retrieves all cached objects
- [x] **Object Existence Check**: `existsInLocalCache()` correctly identifies cached objects
- [x] **Update in Cache**: `updateInLocalCache()` modifies cached objects
- [x] **Get Object IDs**: `getAllObjectIdsInLocalCache()` retrieves all IDs
- [x] **Remove from Cache**: `removeFromLocalCache()` removes objects correctly
- [x] **Clear Cache**: `clearLocalCacheForClass()` clears all objects of a class
- [x] **Sync with Server**: `syncLocalCacheWithServer()` syncs data to server

### Widget Offline Support
- [x] **ParseLiveListWidget**: `offlineMode` parameter enables local caching
- [x] **ParseLiveSliverListWidget**: `offlineMode` parameter enables local caching
- [x] **ParseLiveSliverGridWidget**: `offlineMode` parameter enables local caching
- [x] **ParseLiveListPageView**: `offlineMode` parameter enables local caching

### Offline Features
- [x] **Cache Configuration**: `cacheSize` parameter controls memory usage
- [x] **Lazy Loading**: `lazyLoading` parameter loads data on-demand
- [x] **Preloaded Columns**: `preloadedColumns` parameter specifies initial fields
- [x] **Connectivity Detection**: Automatic detection of online/offline status via mixin
- [x] **Fallback to Cache**: Uses cached data when offline

## ✅ Code Quality

### Static Analysis
- [x] `dart analyze` - No issues in dart package
- [x] `flutter analyze` - No issues in flutter package
- [x] Linting fixes applied (unnecessary brace in string interpolation)
- [x] Removed unnecessary import

### Tests
- [x] All 17 flutter package tests pass
- [x] All 167 dart package tests pass

## ✅ New Widgets Documentation

### README Updates
- [x] Added "Features" section with Live Queries and Offline Support
- [x] Added "Usage" section with examples for all 4 live widgets
- [x] Added "Offline Mode" section with API documentation
- [x] Added Table of Contents with proper anchors
- [x] Comprehensive offline caching method examples
- [x] Configuration parameter documentation
- [x] GlobalKey pattern for controlling sliver widgets

### Documented Widgets
- [x] **ParseLiveListWidget**: Traditional ListView widget example
- [x] **ParseLiveSliverListWidget**: Sliver-based list widget example with GlobalKey
- [x] **ParseLiveSliverGridWidget**: Sliver-based grid widget example with GlobalKey
- [x] **ParseLiveListPageView**: PageView widget example

### Public API Exposed
- [x] `ParseLiveSliverListWidgetState` - Public state class for list control
- [x] `ParseLiveSliverGridWidgetState` - Public state class for grid control
- [x] `refreshData()` - Public method to refresh widget data
- [x] `loadMoreData()` - Public method to load more data when paginated
- [x] `hasMoreData` - Public getter for pagination status
- [x] `loadMoreStatus` - Public getter for load more status

## ✅ File Status

### New Files (Flutter Package)
- [x] `lib/src/utils/parse_live_sliver_list.dart` - Sliver list widget
- [x] `lib/src/utils/parse_live_sliver_grid.dart` - Sliver grid widget
- [x] `lib/src/utils/parse_live_page_view.dart` - PageView widget
- [x] `lib/src/utils/parse_cached_live_list.dart` - LRU cache implementation
- [x] `lib/src/mixins/connectivity_handler_mixin.dart` - Connectivity handling mixin

### New Files (Dart Package)
- [x] `lib/src/objects/parse_offline_object.dart` - Offline extension methods

### Modified Files
- [x] `packages/flutter/README.md` - Updated with comprehensive documentation
- [x] `packages/flutter/lib/parse_server_sdk_flutter.dart` - Exports new files

## 📋 Ready for Pull Request

This implementation is ready for submission with the following features:

### New Capabilities
1. **Three New Live Query Widgets**: ParseLiveSliverList, ParseLiveSliverGrid, ParseLivePageView
2. **Comprehensive Offline Support**: Full caching system with LRU memory management
3. **Connectivity Aware**: Automatic fallback to cached data when offline
4. **Performance Optimized**: Batch operations and lazy loading support
5. **Well Documented**: Complete README with examples for all features

### Breaking Changes
- None

### Deprecations
- None

### Migration Required
- No breaking changes, fully backward compatible

## 🚀 Deployment Notes

For users adopting this version:

1. **Optional Offline Mode**: Set `offlineMode: true` on live widgets to enable caching
2. **No Required Changes**: Existing code continues to work without modification
3. **New Widgets**: Can be used alongside existing ParseLiveList
4. **Manual Caching**: Advanced users can use ParseObjectOffline extension methods directly

---

**Status**: ✅ READY FOR PULL REQUEST
**Version**: 10.2.0+
**Breaking Changes**: None
**New Dependencies**: None
2 changes: 2 additions & 0 deletions packages/dart/lib/parse_server_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ part 'src/utils/parse_login_helpers.dart';
part 'src/utils/parse_utils.dart';
part 'src/utils/valuable.dart';

part 'src/objects/parse_offline_object.dart';

class Parse {
bool _hasBeenInitialized = false;

Expand Down
215 changes: 215 additions & 0 deletions packages/dart/lib/src/objects/parse_offline_object.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
part of '../../parse_server_sdk.dart';

extension ParseObjectOffline on ParseObject {
/// Load a single object by objectId from local storage.
static Future<ParseObject?> loadFromLocalCache(
String className,
String objectId,
) async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$className';
final List<String> cached = await _getStringListAsStrings(
coreStore,
cacheKey,
);
for (final s in cached) {
final jsonObj = json.decode(s);
if (jsonObj['objectId'] == objectId) {
print('Loaded object $objectId from local cache for $className');
return ParseObject(className).fromJson(jsonObj);
}
}
return null;
}

/// Save this object to local storage (CoreStore) for offline access.
Future<void> saveToLocalCache() async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$parseClassName';
final List<String> cached = await _getStringListAsStrings(
coreStore,
cacheKey,
);
// Remove any existing object with the same objectId
cached.removeWhere((s) {
final jsonObj = json.decode(s);
return jsonObj['objectId'] == objectId;
});
cached.add(json.encode(toJson(full: true)));
await coreStore.setStringList(cacheKey, cached);
print(
'Saved object ${objectId ?? "(no objectId)"} to local cache for $parseClassName',
);
}

/// Save a list of objects to local storage efficiently.
static Future<void> saveAllToLocalCache(
String className,
List<ParseObject> objectsToSave,
) async {
if (objectsToSave.isEmpty) return;

final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$className';
final List<String> cachedStrings = await _getStringListAsStrings(
coreStore,
cacheKey,
);

// Use a Map for efficient lookup and update of existing objects
final Map<String, String> objectMap = {};
for (final s in cachedStrings) {
try {
final jsonObj = json.decode(s);
final objectId = jsonObj['objectId'] as String?;
if (objectId != null) {
objectMap[objectId] = s; // Store the original JSON string
}
} catch (e) {
print('Error decoding cached object string during batch save: $e');
}
}

int added = 0;
int updated = 0;

// Update the map with the new objects
for (final obj in objectsToSave) {
final objectId = obj.objectId;
if (objectId != null) {
if (objectMap.containsKey(objectId)) {
updated++;
} else {
added++;
}
// Encode the new object data and replace/add it in the map
objectMap[objectId] = json.encode(obj.toJson(full: true));
} else {
print(
'Skipping object without objectId during batch save for $className',
);
}
}

// Convert the map values back to a list and save
final List<String> updatedCachedStrings = objectMap.values.toList();
await coreStore.setStringList(cacheKey, updatedCachedStrings);
print(
'Batch saved to local cache for $className. Added: $added, Updated: $updated, Total: ${updatedCachedStrings.length}',
);
}

/// Remove this object from local storage (CoreStore).
Future<void> removeFromLocalCache() async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$parseClassName';
final List<String> cached = await _getStringListAsStrings(
coreStore,
cacheKey,
);
cached.removeWhere((s) {
final jsonObj = json.decode(s);
return jsonObj['objectId'] == objectId;
});
await coreStore.setStringList(cacheKey, cached);
print(
'Removed object ${objectId ?? "(no objectId)"} from local cache for $parseClassName',
);
}

/// Load all objects of this class from local storage.
static Future<List<ParseObject>> loadAllFromLocalCache(
String className,
) async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$className';
final List<String> cached = await _getStringListAsStrings(
coreStore,
cacheKey,
);
print('Loaded ${cached.length} objects from local cache for $className');
return cached.map<ParseObject>((s) {
final jsonObj = json.decode(s);
return ParseObject(className).fromJson(jsonObj);
}).toList();
}

Future<void> updateInLocalCache(Map<String, dynamic> updates) async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$parseClassName';
final List<String> cached = await _getStringListAsStrings(
coreStore,
cacheKey,
);
for (int i = 0; i < cached.length; i++) {
final jsonObj = json.decode(cached[i]);
if (jsonObj['objectId'] == objectId) {
jsonObj.addAll(updates);
cached[i] = json.encode(jsonObj);
break;
}
}
await coreStore.setStringList(cacheKey, cached);
print(
'Updated object ${objectId ?? "(no objectId)"} in local cache for $parseClassName',
);
}

static Future<void> clearLocalCacheForClass(String className) async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$className';
await coreStore.setStringList(cacheKey, []);
print('Cleared local cache for $className');
}

static Future<bool> existsInLocalCache(
String className,
String objectId,
) async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$className';
final List<String> cached = await _getStringListAsStrings(
coreStore,
cacheKey,
);
for (final s in cached) {
final jsonObj = json.decode(s);
if (jsonObj['objectId'] == objectId) {
print('Object $objectId exists in local cache for $className');
return true;
}
}
print('Object $objectId does not exist in local cache for $className');
return false;
}

static Future<List<String>> getAllObjectIdsInLocalCache(
String className,
) async {
final CoreStore coreStore = ParseCoreData().getStore();
final String cacheKey = 'offline_cache_$className';
final List<String> cached = await _getStringListAsStrings(
coreStore,
cacheKey,
);
print('Fetched all objectIds from local cache for $className');
return cached.map((s) => json.decode(s)['objectId'] as String).toList();
}

static Future<void> syncLocalCacheWithServer(String className) async {
final objects = await loadAllFromLocalCache(className);
for (final obj in objects) {
await obj.save();
}
print('Synced local cache with server for $className');
}

static Future<List<String>> _getStringListAsStrings(
CoreStore coreStore,
String cacheKey,
) async {
final rawList = await coreStore.getStringList(cacheKey);
if (rawList == null) return [];
return List<String>.from(rawList.map((e) => e.toString()));
}
}
11 changes: 10 additions & 1 deletion packages/dart/lib/src/storage/core_store_memory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,18 @@ class CoreStoreMemoryImp implements CoreStore {

@override
Future<List<String>?> getStringList(String key) async {
return _data[key];
final value = _data[key];
if (value == null) return null;
if (value is List<String>) return value;
if (value is Iterable) return value.map((e) => e.toString()).toList();
return null;
}

// @override
// Future<List<String>?> getStringList(String key) async {
// return _data[key];
// }

@override
Future<dynamic> remove(String key) async {
return _data.remove(key);
Expand Down
13 changes: 11 additions & 2 deletions packages/dart/lib/src/storage/core_store_sem_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,19 @@ class CoreStoreSembastImp implements CoreStore {

@override
Future<List<String>?> getStringList(String key) async {
final List<String>? storedItem = await get(key);
return storedItem;
final value = await get(key);
if (value == null) return null;
if (value is List<String>) return value;
if (value is Iterable) return value.map((e) => e.toString()).toList();
return null;
}

// @override
// Future<List<String>?> getStringList(String key) async {
// final List<String>? storedItem = await get(key);
// return storedItem;
// }

@override
Future<void> remove(String key) {
return _store.record(key).delete(_database);
Expand Down
Loading