forked from cwtch.im/cwtch-ui
Merge branch 'trunk' into nima/clickable-links
This commit is contained in:
commit
4dea1e1dd4
|
@ -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'])!;
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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())),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -40,13 +40,9 @@ 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;
|
|
||||||
var last = scrollListener.itemPositions.value.last.index;
|
|
||||||
// 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).initialScrollIndex = 0;
|
||||||
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
|
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
});
|
});
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue