Merge branch 'trunk' into nima/clickable-links
continuous-integration/drone/pr Build is running Details

This commit is contained in:
Sarah Jamie Lewis 2021-11-11 00:22:53 +00:00
commit 4dea1e1dd4
14 changed files with 83 additions and 21 deletions

View File

@ -133,6 +133,8 @@ class CwtchNotifier {
notificationManager.notify("New Message From Peer!"); notificationManager.notify("New Message From Peer!");
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["RemotePeer"]) { if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["RemotePeer"]) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++; profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++;
} else {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.newMarker++;
} }
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.totalMessages++; profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.totalMessages++;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now()); profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
@ -181,6 +183,8 @@ class CwtchNotifier {
//if not currently open //if not currently open
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) { if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++;
} else {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.newMarker++;
} }
var timestampSent = DateTime.tryParse(data['TimestampSent'])!; var timestampSent = DateTime.tryParse(data['TimestampSent'])!;

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "de", "@@locale": "de",
"@@last_modified": "2021-11-10T18:47:30+01:00", "@@last_modified": "2021-11-11T01:02:08+01:00",
"newMessagesLabel": "New Messages",
"localeRU": "Russian", "localeRU": "Russian",
"copyServerKeys": "Copy keys", "copyServerKeys": "Copy keys",
"verfiyResumeButton": "Verify\/resume", "verfiyResumeButton": "Verify\/resume",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "en", "@@locale": "en",
"@@last_modified": "2021-11-10T18:47:30+01:00", "@@last_modified": "2021-11-11T01:02:08+01:00",
"newMessagesLabel": "New Messages",
"localeRU": "Russian", "localeRU": "Russian",
"copyServerKeys": "Copy keys", "copyServerKeys": "Copy keys",
"verfiyResumeButton": "Verify\/resume", "verfiyResumeButton": "Verify\/resume",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "es", "@@locale": "es",
"@@last_modified": "2021-11-10T18:47:30+01:00", "@@last_modified": "2021-11-11T01:02:08+01:00",
"newMessagesLabel": "New Messages",
"localeRU": "Russian", "localeRU": "Russian",
"copyServerKeys": "Copy keys", "copyServerKeys": "Copy keys",
"verfiyResumeButton": "Verify\/resume", "verfiyResumeButton": "Verify\/resume",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"@@last_modified": "2021-11-10T18:47:30+01:00", "@@last_modified": "2021-11-11T01:02:08+01:00",
"newMessagesLabel": "New Messages",
"localeRU": "Russian", "localeRU": "Russian",
"copyServerKeys": "Copier les clés", "copyServerKeys": "Copier les clés",
"verfiyResumeButton": "Vérifier\/reprendre", "verfiyResumeButton": "Vérifier\/reprendre",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "it", "@@locale": "it",
"@@last_modified": "2021-11-10T18:47:30+01:00", "@@last_modified": "2021-11-11T01:02:08+01:00",
"newMessagesLabel": "New Messages",
"localeRU": "Russian", "localeRU": "Russian",
"copyServerKeys": "Copy keys", "copyServerKeys": "Copy keys",
"verfiyResumeButton": "Verify\/resume", "verfiyResumeButton": "Verify\/resume",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "pl", "@@locale": "pl",
"@@last_modified": "2021-11-10T18:47:30+01:00", "@@last_modified": "2021-11-11T01:02:08+01:00",
"newMessagesLabel": "New Messages",
"localeRU": "Russian", "localeRU": "Russian",
"copyServerKeys": "Kopiuj klucze", "copyServerKeys": "Kopiuj klucze",
"verfiyResumeButton": "Zweryfikuj\/wznów", "verfiyResumeButton": "Zweryfikuj\/wznów",

View File

@ -1,6 +1,7 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"@@last_modified": "2021-11-10T18:47:30+01:00", "@@last_modified": "2021-11-11T01:02:08+01:00",
"newMessagesLabel": "New Messages",
"localeRU": "Russian", "localeRU": "Russian",
"copyServerKeys": "Copy keys", "copyServerKeys": "Copy keys",
"verfiyResumeButton": "Verify\/resume", "verfiyResumeButton": "Verify\/resume",

View File

@ -507,6 +507,8 @@ class ContactInfoState extends ChangeNotifier {
late int _totalMessages = 0; late int _totalMessages = 0;
late DateTime _lastMessageTime; late DateTime _lastMessageTime;
late Map<String, GlobalKey<MessageRowState>> keys; late Map<String, GlobalKey<MessageRowState>> keys;
int _newMarker = 0;
DateTime _newMarkerClearAt = DateTime.now();
// todo: a nicer way to model contacts, groups and other "entities" // todo: a nicer way to model contacts, groups and other "entities"
late bool _isGroup; late bool _isGroup;
@ -587,10 +589,36 @@ class ContactInfoState extends ChangeNotifier {
int get unreadMessages => this._unreadMessages; int get unreadMessages => this._unreadMessages;
set unreadMessages(int newVal) { set unreadMessages(int newVal) {
// don't reset newMarker position when unreadMessages is being cleared
if (newVal > 0) {
this._newMarker = newVal;
} else {
this._newMarkerClearAt = DateTime.now().add(const Duration(minutes:2));
}
this._unreadMessages = newVal; this._unreadMessages = newVal;
notifyListeners(); notifyListeners();
} }
int get newMarker {
if (DateTime.now().isAfter(this._newMarkerClearAt)) {
// perform heresy
this._newMarker = 0;
// no need to notifyListeners() because presumably this getter is
// being called from a renderer anyway
}
return this._newMarker;
}
// what's a getter that sometimes sets without a setter
// that sometimes doesn't set
set newMarker(int newVal) {
// only unreadMessages++ can set newMarker = 1;
// avoids drawing a marker when the convo is already open
if (newVal > 1) {
this._newMarker = newVal;
notifyListeners();
}
}
int get totalMessages => this._totalMessages; int get totalMessages => this._totalMessages;
set totalMessages(int newVal) { set totalMessages(int newVal) {
this._totalMessages = newVal; this._totalMessages = newVal;

View File

@ -4,7 +4,6 @@ import 'package:cwtch/views/torstatusview.dart';
import 'package:cwtch/widgets/contactrow.dart'; import 'package:cwtch/widgets/contactrow.dart';
import 'package:cwtch/widgets/profileimage.dart'; import 'package:cwtch/widgets/profileimage.dart';
import 'package:cwtch/widgets/textfield.dart'; import 'package:cwtch/widgets/textfield.dart';
import 'package:cwtch/widgets/tor_icon.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../main.dart'; import '../main.dart';

View File

@ -35,7 +35,7 @@ class _DoubleColumnViewState extends State<DoubleColumnView> {
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)), ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
ChangeNotifierProvider.value( ChangeNotifierProvider.value(
value: flwtch.selectedConversation != null ? Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation!)! : ContactInfoState("", "")), value: flwtch.selectedConversation != null ? Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation!)! : ContactInfoState("", "")),
], child: Container(child: MessageView())), ], child: Container(key: Key(flwtch.selectedConversation??"never_this"), child: MessageView())),
), ),
], ],
); );

View File

@ -40,15 +40,11 @@ class _MessageViewState extends State<MessageView> {
@override @override
void initState() { void initState() {
scrollListener.itemPositions.addListener(() { scrollListener.itemPositions.addListener(() {
if (scrollListener.itemPositions.value.length == 0) { if (scrollListener.itemPositions.value.length != 0 &&
return; Provider.of<AppState>(context, listen: false).unreadMessagesBelow == true &&
} scrollListener.itemPositions.value.any((element) => element.index == 0)) {
var first = scrollListener.itemPositions.value.first.index; Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
var last = scrollListener.itemPositions.value.last.index; Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
// sometimes these go hi->lo and sometimes they go lo->hi because [who tf knows]
if ((first == 0 || last == 0) && Provider.of<AppState>(context, listen: false).unreadMessagesBelow == true) {
Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
} }
}); });
super.initState(); super.initState();
@ -59,7 +55,7 @@ class _MessageViewState extends State<MessageView> {
var appState = Provider.of<AppState>(context, listen: false); var appState = Provider.of<AppState>(context, listen: false);
// using "8" because "# of messages that fit on one screen" isnt trivial to calculate at this point // using "8" because "# of messages that fit on one screen" isnt trivial to calculate at this point
if (appState.initialScrollIndex > 8 && appState.unreadMessagesBelow == false) { if (appState.initialScrollIndex > 4 && appState.unreadMessagesBelow == false) {
WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((timeStamp) { WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((timeStamp) {
appState.unreadMessagesBelow = true; appState.unreadMessagesBelow = true;
}); });
@ -111,6 +107,8 @@ class _MessageViewState extends State<MessageView> {
? FloatingActionButton( ? FloatingActionButton(
child: Icon(Icons.arrow_downward), child: Icon(Icons.arrow_downward),
onPressed: () { onPressed: () {
Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
scrollController.scrollTo(index: 0, duration: Duration(milliseconds: 600)); scrollController.scrollTo(index: 0, duration: Duration(milliseconds: 600));
}) })
: null, : null,
@ -216,6 +214,7 @@ class _MessageViewState extends State<MessageView> {
focusNode.requestFocus(); focusNode.requestFocus();
Future.delayed(const Duration(milliseconds: 80), () { Future.delayed(const Duration(milliseconds: 80), () {
Provider.of<ContactInfoState>(context, listen: false).totalMessages++; Provider.of<ContactInfoState>(context, listen: false).totalMessages++;
Provider.of<ContactInfoState>(context, listen: false).newMarker++;
// Resort the contact list... // Resort the contact list...
Provider.of<ProfileInfoState>(context, listen: false).contactList.updateLastMessageTime(Provider.of<ContactInfoState>(context, listen: false).onion, DateTime.now()); Provider.of<ProfileInfoState>(context, listen: false).contactList.updateLastMessageTime(Provider.of<ContactInfoState>(context, listen: false).onion, DateTime.now());
}); });

View File

@ -71,7 +71,7 @@ class _MessageListState extends State<MessageList> {
? ScrollablePositionedList.builder( ? ScrollablePositionedList.builder(
itemPositionsListener: widget.scrollListener, itemPositionsListener: widget.scrollListener,
itemScrollController: widget.scrollController, itemScrollController: widget.scrollController,
initialScrollIndex: Provider.of<AppState>(outerContext, listen: false).initialScrollIndex, initialScrollIndex: initi > 4 ? initi - 4 : 0,
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages, itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction... reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
itemBuilder: (itemBuilderContext, index) { itemBuilder: (itemBuilderContext, index) {

View File

@ -159,7 +159,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
]; ];
} }
var size = MediaQuery.of(context).size; var size = MediaQuery.of(context).size;
return MouseRegion( var mr = MouseRegion(
// For desktop... // For desktop...
onHover: (event) { onHover: (event) {
setState(() { setState(() {
@ -197,6 +197,31 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: widgetRow, children: widgetRow,
))))); )))));
var mark = Provider.of<ContactInfoState>(context).newMarker;
if (mark > 0 && mark == Provider.of<ContactInfoState>(context).totalMessages - Provider.of<MessageMetadata>(context).messageIndex) {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [Align(alignment:Alignment.center ,child:_bubbleNew()), mr]);
} else {
return mr;
}
}
Widget _bubbleNew() {
return Container(
decoration: BoxDecoration(
color: Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
border: Border.all(
color: Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
width: 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
child: Padding(
padding: EdgeInsets.all(9.0),
child: Text(AppLocalizations.of(context)!.newMessagesLabel)));
} }
void _runAnimation(Offset pixelsPerSecond, Size size) { void _runAnimation(Offset pixelsPerSecond, Size size) {