Skip to content
This repository has been archived by the owner on Feb 4, 2023. It is now read-only.

Commit

Permalink
Merge pull request #224 from krawieck/fix/deduplication-infinite-scroll
Browse files Browse the repository at this point in the history
  • Loading branch information
krawieck authored Apr 28, 2021
2 parents af8e887 + 79d77f6 commit 772cb5e
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 81 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Show avatars setting toggle
- Show scores setting toggle
- Default sort type setting
- Default listing type for the home tab setting
- Import Lemmy settings: long press an account in account settings then choose the import option
- Editing posts
Expand All @@ -12,6 +13,7 @@
### Fixed

- Fixed bug where creating post would crash after uploading a picture
- Added deduplication in infinite scrolls

## v0.4.2 - 2021-04-12

Expand Down
17 changes: 17 additions & 0 deletions lib/pages/add_account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart' as ul;

import '../hooks/delayed_loading.dart';
import '../hooks/stores.dart';
import '../l10n/l10n.dart';
import '../stores/config_store.dart';
import '../widgets/fullscreenable_image.dart';
import '../widgets/radio_picker.dart';
import 'add_instance.dart';
Expand Down Expand Up @@ -40,12 +42,27 @@ class AddAccountPage extends HookWidget {

handleOnAdd() async {
try {
final isFirstAccount = accountsStore.hasNoAccount;

loading.start();
await accountsStore.addAccount(
selectedInstance.value,
usernameController.text,
passwordController.text,
);

// if first account try to import settings
if (isFirstAccount) {
try {
await context.read<ConfigStore>().importLemmyUserSettings(
accountsStore
.userDataFor(
selectedInstance.value, usernameController.text)!
.jwt);
// ignore: avoid_catches_without_on_clauses, empty_catches
} catch (e) {}
}

Navigator.of(context).pop();
} on Exception catch (err) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
Expand Down
1 change: 1 addition & 0 deletions lib/pages/communities_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class CommunitiesListPage extends StatelessWidget {
)
],
),
uniqueProp: (item) => item.community.actorId,
),
);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/pages/inbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class InboxPage extends HookWidget {
canBeMarkedAsRead: true,
hideOnRead: unreadOnly.value,
),
uniqueProp: (item) => item.comment.apId,
),
SortableInfiniteList<PersonMentionView>(
noItems: const Text('no mentions'),
Expand All @@ -137,6 +138,7 @@ class InboxPage extends HookWidget {
umv,
hideOnRead: unreadOnly.value,
),
uniqueProp: (item) => item.personMention.id,
),
InfiniteScroll<PrivateMessageView>(
noItems: const Padding(
Expand All @@ -156,6 +158,7 @@ class InboxPage extends HookWidget {
privateMessageView: mv,
hideOnRead: unreadOnly.value,
),
uniqueProp: (item) => item.privateMessage.apId,
),
],
),
Expand Down
12 changes: 11 additions & 1 deletion lib/pages/manage_account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class _ManageAccount extends HookWidget {
useTextEditingController(text: user.person.matrixUserId);
final avatar = useRef(user.person.avatar);
final banner = useRef(user.person.banner);
final showNsfw = useState(user.localUser.showNsfw);
final sendNotificationsToEmail =
useState(user.localUser.sendNotificationsToEmail);
final newPasswordController = useTextEditingController();
Expand All @@ -134,7 +135,7 @@ class _ManageAccount extends HookWidget {

try {
await LemmyApiV3(user.instanceHost).run(SaveUserSettings(
showNsfw: user.localUser.showNsfw,
showNsfw: showNsfw.value,
theme: user.localUser.theme,
defaultSortType: user.localUser.defaultSortType,
defaultListingType: user.localUser.defaultListingType,
Expand Down Expand Up @@ -318,6 +319,15 @@ class _ManageAccount extends HookWidget {
obscureText: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: showNsfw.value,
onChanged: (checked) {
showNsfw.value = checked;
},
title: Text(L10n.of(context)!.show_nsfw),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: sendNotificationsToEmail.value,
onChanged: (checked) {
Expand Down
14 changes: 14 additions & 0 deletions lib/pages/search_results.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ class _SearchResultsList extends HookWidget {
throw UnimplementedError();
}
},
uniqueProp: (item) {
switch (type) {
case SearchType.comments:
return (item as CommentView).comment.apId;
case SearchType.communities:
return (item as CommunityView).community.actorId;
case SearchType.posts:
return (item as PostView).post.apId;
case SearchType.users:
return (item as PersonViewSafe).person.actorId;
default:
return item;
}
},
);
}
}
7 changes: 0 additions & 7 deletions lib/pages/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,6 @@ class GeneralConfigPage extends HookWidget {
),
),
),
SwitchListTile.adaptive(
title: Text(L10n.of(context)!.show_nsfw),
value: configStore.showNsfw,
onChanged: (checked) {
configStore.showNsfw = checked;
},
),
],
),
);
Expand Down
44 changes: 19 additions & 25 deletions lib/stores/config_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,6 @@ class ConfigStore extends ChangeNotifier {
save();
}

late bool _showNsfw;
@JsonKey(defaultValue: false)
bool get showNsfw => _showNsfw;
set showNsfw(bool showNsfw) {
_showNsfw = showNsfw;
notifyListeners();
save();
}

late bool _showScores;
@JsonKey(defaultValue: true)
bool get showScores => _showScores;
Expand Down Expand Up @@ -95,24 +86,27 @@ class ConfigStore extends ChangeNotifier {
/// Copies over settings from lemmy to [ConfigStore]
void copyLemmyUserSettings(LocalUserSettings localUserSettings) {
// themes from lemmy-ui that are dark mode
// const darkModeLemmyUiThemes = {
// 'solar',
// 'cyborg',
// 'darkly',
// 'vaporwave-dark',
// // TODO: is it dark theme?
// 'i386',
// };
const darkModeLemmyUiThemes = {
'solar',
'cyborg',
'darkly',
'vaporwave-dark',
'i386',
};

_showAvatars = localUserSettings.showAvatars;
_showNsfw = localUserSettings.showNsfw;
// TODO: should these also be imported? If so, how?
// _theme = darkModeLemmyUiThemes.contains(localUserSettings.theme)
// ? ThemeMode.dark
// : ThemeMode.light;
// _locale = L10n.supportedLocales.contains(Locale(localUserSettings.lang))
// ? Locale(localUserSettings.lang)
// : _locale;
_theme = () {
if (localUserSettings.theme == 'browser') return ThemeMode.system;

if (darkModeLemmyUiThemes.contains(localUserSettings.theme)) {
return ThemeMode.dark;
}

return ThemeMode.light;
}();
_locale = L10n.supportedLocales.contains(Locale(localUserSettings.lang))
? Locale(localUserSettings.lang)
: _locale;
// TODO: add when it is released
// _showScores = localUserSettings.showScores;
_defaultSortType = localUserSettings.defaultSortType;
Expand Down
2 changes: 0 additions & 2 deletions lib/stores/config_store.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 25 additions & 4 deletions lib/widgets/infinite_scroll.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand Down Expand Up @@ -43,6 +45,10 @@ class InfiniteScroll<T> extends HookWidget {
/// Widget that will be displayed if there are no items
final Widget noItems;

/// Maps an item to its unique property that will allow to detect possible
/// duplicates thus perfoming deduplication
final Object Function(T item)? uniqueProp;

const InfiniteScroll({
this.batchSize = 10,
this.leading = const SizedBox.shrink(),
Expand All @@ -53,31 +59,39 @@ class InfiniteScroll<T> extends HookWidget {
required this.fetcher,
this.controller,
this.noItems = const SizedBox.shrink(),
this.uniqueProp,
}) : assert(batchSize > 0);

@override
Widget build(BuildContext context) {
final data = useState<List<T>>([]);
// holds unique props of the data
final dataSet = useRef(HashSet<Object>());
final hasMore = useRef(true);
final page = useRef(1);
final isFetching = useRef(false);

final uniquePropFunc = uniqueProp ?? (e) => e as Object;

useEffect(() {
if (controller != null) {
controller?.clear = () {
data.value = [];
hasMore.current = true;
page.current = 1;
dataSet.current.clear();
};
}

return null;
}, []);

final page = data.value.length ~/ batchSize + 1;

return RefreshIndicator(
onRefresh: () async {
data.value = [];
hasMore.current = true;
page.current = 1;
dataSet.current.clear();

await HapticFeedback.mediumImpact();
await Future.delayed(const Duration(seconds: 1));
Expand Down Expand Up @@ -107,13 +121,20 @@ class InfiniteScroll<T> extends HookWidget {
// if it's already fetching more, skip
if (!isFetching.current) {
isFetching.current = true;
fetcher(page, batchSize).then((newData) {
fetcher(page.current, batchSize).then((incoming) {
// if got less than the batchSize, mark the list as done
if (newData.length < batchSize) {
if (incoming.length < batchSize) {
hasMore.current = false;
}

final newData = incoming.where(
(e) => !dataSet.current.contains(uniquePropFunc(e)),
);

// append new data
data.value = [...data.value, ...newData];
dataSet.current.addAll(newData.map(uniquePropFunc));
page.current += 1;
}).whenComplete(() => isFetching.current = false);
}

Expand Down
Loading

0 comments on commit 772cb5e

Please sign in to comment.