diff --git a/lib/business_logic/chat/chat.dart b/lib/business_logic/chat/chat.dart index ef74f61..1e9ce0a 100644 --- a/lib/business_logic/chat/chat.dart +++ b/lib/business_logic/chat/chat.dart @@ -91,11 +91,23 @@ Future _getConversationModels( return ConversationModelResult([]); } - return ConversationModelResult(jsonDecode(result.body) - .map( - (e) => ConversationModel.fromJson(e, identityName), - ) - .toList()); + String? newMaxId = result.headers["link"]; + final firstOpen = newMaxId?.indexOf("<"); + final firstClose = newMaxId?.indexOf(">"); + newMaxId = newMaxId?.substring(firstOpen! + 1, firstClose); + if (newMaxId != null) { + final maxIdUri = Uri.parse(newMaxId); + newMaxId = maxIdUri.queryParameters["max_id"]; + } + + return ConversationModelResult( + jsonDecode(result.body) + .map( + (e) => ConversationModel.fromJson(e, identityName), + ) + .toList(), + maxIds: {identityName: newMaxId}, + ); } Future getAllConversationModels( @@ -104,19 +116,29 @@ Future getAllConversationModels( global.settings!.identities.forEach((key, value) { futureResults.add(_getConversationModels(key, maxIds[key])); }); + List results = []; List models = []; for (var element in futureResults) { - models.addAll((await element).models); + final r = await element; + results.add(r); + models.addAll(r.models); } models.sort(); - models = models.reversed.toList().sublist(0, global.settings!.batchSize); - Map newMaxIds = {}; - for (var element in models) { - if (newMaxIds[element.identity] == null) { - newMaxIds.addAll({element.identity: element.id}); - } else if (element.id.compareTo(newMaxIds[element.identity]!) < 0) { - newMaxIds.addAll({element.identity: element.id}); - } + models = models.reversed.toList(); + if (models.length > global.settings!.batchSize) { + models = models.sublist(0, global.settings!.batchSize); } + Map newMaxIds = {}; + for (var element in results) { + newMaxIds.addAll(element.maxIds); + } + + Set appearingIdentities = {}; + for (var i in models) { + appearingIdentities.add(i.identity); + } + + newMaxIds.removeWhere((key, value) => (appearingIdentities.contains(value))); + print(newMaxIds); return ConversationModelResult(models, maxIds: newMaxIds); } diff --git a/lib/dialogues/makepost.dart b/lib/dialogues/makepost.dart index f3970c6..e866ec1 100644 --- a/lib/dialogues/makepost.dart +++ b/lib/dialogues/makepost.dart @@ -449,7 +449,7 @@ class FileUploadDisplay extends StatelessWidget { ), ], ), - if (lookupMimeType(file.path)!.startsWith("image/")) + if (lookupMimeType(file.path)?.startsWith("image/") ?? false) Image.asset( file.path, fit: BoxFit.fitWidth, diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 395fdb7..dbe9b2f 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:localization/localization.dart'; import 'package:loris/business_logic/chat/chat.dart'; +import 'package:loris/partials/loadingbox.dart'; import 'package:loris/partials/post_text_renderer.dart'; import 'package:loris/themes/themes.dart' as themes; import 'package:loris/global.dart' as global; @@ -16,37 +17,59 @@ class _ChatState extends State { List conversations = []; // map that stores max ids for each identity Map maxIds = {}; + final scrollController = ScrollController(); + bool loading = false; + // loads more conversations and properly stores all ids Future updateConversations() async { + if (loading) return; + loading = true; final models = await getAllConversationModels(maxIds); - print(models.maxIds); setState(() { conversations.addAll(models.models); maxIds = models.maxIds; }); + loading = false; } @override void initState() { updateConversations(); + scrollController.addListener(() { + if (scrollController.position.maxScrollExtent - scrollController.offset < + MediaQuery.of(context).size.height) { + updateConversations(); + } + }); super.initState(); } + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: ListView.separated( + controller: scrollController, shrinkWrap: true, - itemBuilder: (context, index) => ConversationButton( - model: conversations[index], - ), + itemBuilder: ((context, index) { + if (index >= conversations.length) return const LoadingBox(); + return ConversationButton( + model: conversations[index], + onPressed: () {}, + ); + }), separatorBuilder: (context, index) => const Divider( height: themes.defaultSeperatorHeight, color: Colors.transparent, ), - itemCount: conversations.length), + itemCount: conversations.length + 1), ), Container( decoration: BoxDecoration( @@ -63,35 +86,55 @@ class ConversationButton extends StatelessWidget { const ConversationButton({ super.key, required this.model, + required this.onPressed, }); final ConversationModel model; + final void Function() onPressed; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(themes.defaultRadius), - color: Theme.of(context).colorScheme.surface, + border: Border.fromBorderSide(BorderSide( + color: Theme.of(context).colorScheme.secondary, width: 2)), ), margin: const EdgeInsets.fromLTRB(themes.defaultSeperatorHeight * 2, 0, themes.defaultSeperatorHeight * 2, 0), width: global.getWidth(context), constraints: global.getConstraints(context), - child: Padding( - padding: themes.defaultInsideMargins, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Wrap( - alignment: WrapAlignment.spaceBetween, + child: InkWell( + borderRadius: const BorderRadius.all(themes.defaultRadius), + onTap: onPressed, + child: Ink( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(themes.defaultRadius), + color: Theme.of(context).colorScheme.surface, + ), + child: Padding( + padding: themes.defaultInsideMargins, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - SelectableText(model.getAccountsString()), - SelectableText("${"you-are".i18n()} ${model.identity}"), + if (model.unread) + Row( + children: [ + const Icon(Icons.mark_chat_unread), + SelectableText("unread".i18n()) + ], + ), + Wrap( + alignment: WrapAlignment.spaceBetween, + children: [ + if (model.unread) SelectableText(model.getAccountsString()), + SelectableText("${"you-are".i18n()} ${model.identity}"), + ], + ), + if (model.lastStatus != null) + PostTextRenderer(input: model.lastStatus!.content), ], ), - if (model.lastStatus != null) - PostTextRenderer(input: model.lastStatus!.content), - ], + ), ), ), );