Add scroll to position of notification in post screen

codemagic-setup
Hank Grabowski 2023-01-15 09:39:05 -06:00
rodzic 3dc6751281
commit a1ac7a009f
9 zmienionych plików z 138 dodań i 44 usunięć

Wyświetl plik

@ -25,12 +25,13 @@ class NotificationControl extends StatelessWidget {
final manager = getIt<TimelineManager>();
final existingPostData = manager.getPostTreeEntryBy(notification.iid);
if (existingPostData.isSuccess) {
context.push('/post/view/${existingPostData.value.id}');
context
.push('/post/view/${existingPostData.value.id}/${notification.iid}');
return;
}
final loadedPost = await manager.refreshStatusChain(notification.iid);
if (loadedPost.isSuccess) {
context.push('/post/view/${loadedPost.value.id}');
context.push('/post/view/${loadedPost.value.id}/${notification.iid}');
return;
}
buildSnackbar(

Wyświetl plik

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../../models/entry_tree_item.dart';
import '../../models/flattened_tree_item.dart';
import '../../models/timeline_entry.dart';
import '../../services/timeline_manager.dart';
import '../../utils/entry_tree_item_flattening.dart';
@ -10,14 +12,19 @@ import 'flattened_tree_entry_control.dart';
class PostControl extends StatefulWidget {
final EntryTreeItem originalItem;
final String scrollToId;
final bool openRemote;
final bool showStatusOpenButton;
final bool isRoot;
const PostControl(
{super.key,
required this.originalItem,
required this.openRemote,
required this.showStatusOpenButton});
const PostControl({
super.key,
required this.originalItem,
required this.scrollToId,
required this.openRemote,
required this.showStatusOpenButton,
required this.isRoot,
});
@override
State<PostControl> createState() => _PostControlState();
@ -26,6 +33,10 @@ class PostControl extends StatefulWidget {
class _PostControlState extends State<PostControl> {
static final _logger = Logger('$PostControl');
final ItemScrollController itemScrollController = ItemScrollController();
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
var showContent = true;
var showComments = false;
@ -41,6 +52,7 @@ class _PostControlState extends State<PostControl> {
@override
void initState() {
showContent = entry.spoilerText.isEmpty;
showComments = widget.scrollToId != item.id;
}
@override
@ -48,6 +60,19 @@ class _PostControlState extends State<PostControl> {
final manager = context.watch<TimelineManager>();
_logger.finest('Building ${item.entry.toShortString()}');
final items = widget.originalItem.flatten(topLevelOnly: !showComments);
if (widget.isRoot) {
return buildListView(context, items, manager);
} else {
return buildColumnView(context, items, manager);
}
}
Widget buildColumnView(
BuildContext context,
List<FlattenedTreeItem> items,
TimelineManager manager,
) {
final widgets = <Widget>[];
for (var i = 0; i < items.length; i++) {
final itemWidget = FlattenedTreeEntryControl(
@ -76,4 +101,61 @@ class _PostControlState extends State<PostControl> {
}
return Column(children: widgets);
}
Widget buildListView(
BuildContext context,
List<FlattenedTreeItem> items,
TimelineManager manager,
) {
final int count;
final int offset;
if (hasComments && showComments) {
count = items.length + 1;
offset = 1;
final scrollToIndex =
items.indexWhere((e) => e.timelineEntry.id == widget.scrollToId);
Future.delayed(
const Duration(seconds: 1),
() async => itemScrollController.jumpTo(index: scrollToIndex),
);
} else if (hasComments) {
count = 2;
offset = 0;
} else {
count = 1;
offset = 0;
}
return ScrollablePositionedList.builder(
itemCount: count,
itemScrollController: itemScrollController,
itemPositionsListener: itemPositionsListener,
itemBuilder: (context, index) {
if (index == 0) {
return FlattenedTreeEntryControl(
originalItem: items.first,
openRemote: widget.openRemote,
showStatusOpenButton: widget.showStatusOpenButton,
);
}
if (index == 1 && hasComments) {
return TextButton(
onPressed: () async {
setState(() {
showComments = !showComments;
});
if (showComments) {
await manager.refreshStatusChain(entry.id);
}
},
child:
Text(showComments ? 'Hide Comments' : 'Load & Show Comments'),
);
}
return FlattenedTreeEntryControl(
originalItem: items[index - offset],
openRemote: widget.openRemote,
showStatusOpenButton: widget.showStatusOpenButton,
);
});
}
}

Wyświetl plik

@ -45,8 +45,10 @@ class TimelinePanel extends StatelessWidget {
'Building item: $itemIndex: ${item.entry.toShortString()}');
return PostControl(
originalItem: item,
scrollToId: item.id,
openRemote: false,
showStatusOpenButton: true,
isRoot: false,
);
},
separatorBuilder: (context, index) => Divider(),

Wyświetl plik

@ -137,9 +137,11 @@ final appRouter = GoRouter(
EditorScreen(id: state.params['id'] ?? 'Not Found'),
),
GoRoute(
path: 'view/:id',
builder: (context, state) =>
PostScreen(id: state.params['id'] ?? 'Not Found'),
path: 'view/:id/:goto_id',
builder: (context, state) => PostScreen(
id: state.params['id'] ?? 'Not Found',
goToId: state.params['goto_id'] ?? 'Not Found',
),
),
]),
GoRoute(

Wyświetl plik

@ -7,7 +7,13 @@ import '../services/timeline_manager.dart';
class PostScreen extends StatelessWidget {
final String id;
const PostScreen({super.key, required this.id});
final String goToId;
const PostScreen({
super.key,
required this.id,
required this.goToId,
});
@override
Widget build(BuildContext context) {
@ -17,13 +23,12 @@ class PostScreen extends StatelessWidget {
onRefresh: () async {
await manager.refreshStatusChain(id);
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: PostControl(
originalItem: post,
openRemote: true,
showStatusOpenButton: true,
),
child: PostControl(
originalItem: post,
scrollToId: goToId,
openRemote: true,
showStatusOpenButton: true,
isRoot: true,
),
),
onError: (error) => Text('Error getting post: $error'));

Wyświetl plik

@ -8,7 +8,7 @@ import Foundation
import desktop_window
import flutter_secure_storage_macos
import path_provider_macos
import shared_preferences_macos
import shared_preferences_foundation
import sqflite
import url_launcher_macos

Wyświetl plik

@ -9,7 +9,8 @@ PODS:
- FMDB/standard (2.7.5)
- path_provider_macos (0.0.1):
- FlutterMacOS
- shared_preferences_macos (0.0.1):
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite (0.0.2):
- FlutterMacOS
@ -22,7 +23,7 @@ DEPENDENCIES:
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
@ -39,8 +40,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
shared_preferences_macos:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos
sqflite:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
url_launcher_macos:
@ -51,8 +52,8 @@ SPEC CHECKSUMS:
flutter_secure_storage_macos: 75c8cadfdba05ca007c0fa4ea0c16e5cf85e521b
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
path_provider_macos: 05fb0ef0cedf3e5bd179b9e41a638682b37133ea
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3

Wyświetl plik

@ -147,7 +147,7 @@ packages:
name: file_picker
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.4"
version: "5.2.5"
flutter:
dependency: "direct main"
description: flutter
@ -260,7 +260,7 @@ packages:
name: flutter_widget_from_html_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.0+2"
version: "0.9.1"
functional_listener:
dependency: transitive
description:
@ -323,7 +323,7 @@ packages:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.2"
version: "3.3.0"
image_picker:
dependency: "direct main"
description:
@ -351,7 +351,7 @@ packages:
name: image_picker_ios
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.6+4"
version: "0.8.6+5"
image_picker_platform_interface:
dependency: transitive
description:
@ -484,7 +484,7 @@ packages:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
version: "2.0.7"
path_provider_platform_interface:
dependency: transitive
description:
@ -562,13 +562,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.27.7"
scrollable_positioned_list:
dependency: "direct main"
description:
name: scrollable_positioned_list
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.15"
version: "2.0.16"
shared_preferences_android:
dependency: transitive
description:
@ -576,10 +583,10 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.14"
shared_preferences_ios:
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_ios
name: shared_preferences_foundation
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
@ -590,13 +597,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
shared_preferences_platform_interface:
dependency: transitive
description:
@ -636,14 +636,14 @@ packages:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.2"
version: "2.2.3"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0+2"
version: "2.4.1"
stack_trace:
dependency: transitive
description:
@ -678,7 +678,7 @@ packages:
name: synchronized
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0+3"
version: "3.0.1"
term_glyph:
dependency: transitive
description:
@ -825,7 +825,7 @@ packages:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0+2"
version: "0.2.0+3"
xml:
dependency: transitive
description:

Wyświetl plik

@ -37,6 +37,7 @@ dependencies:
flutter_file_dialog: ^2.3.2
multi_trigger_autocomplete: ^0.1.1
video_player: ^2.4.10
scrollable_positioned_list: ^0.3.5
dev_dependencies:
flutter_test: