Commit 002c36e9 authored by Wilko Manger's avatar Wilko Manger

Restructure chat ordering and incorporate invites

parent 8b2658ed
......@@ -167,7 +167,7 @@ class App extends StatelessWidget {
),
],
child: Provider<Matrix>(
create: (_) => Matrix(_authBloc),
create: (_) => Matrix(_authBloc, _chatOrderBloc),
child: PattleTheme(
data: _settingsBloc.state.themeBrightness == Brightness.dark
? pattleDarkTheme
......
......@@ -18,6 +18,7 @@
import 'dart:convert';
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:shared_preferences/shared_preferences.dart';
......@@ -44,14 +45,8 @@ class ChatOrderBloc extends Bloc<ChatOrderEvent, ChatOrderState> {
@override
Stream<ChatOrderState> mapEventToState(ChatOrderEvent event) async* {
if (event is UpdateChatOrder) {
Map<RoomId, DateTime> set(List<Chat> chats, String key) {
var map = Map<RoomId, DateTime>.fromIterable(
chats,
key: (chat) => chat.room.id,
value: (chat) =>
chat.latestMessageForSorting?.event?.time ??
DateTime.fromMillisecondsSinceEpoch(0),
);
Map<RoomId, SortData> set(List<Chat> chats, String key) {
var map = chats.toSortData();
final current = key == _personalKey ? state.personal : state.public;
......@@ -66,7 +61,7 @@ class ChatOrderBloc extends Bloc<ChatOrderEvent, ChatOrderState> {
map.map(
(key, value) => MapEntry(
key.toString(),
value.millisecondsSinceEpoch,
value.toJson(),
),
),
),
......@@ -83,7 +78,7 @@ class ChatOrderBloc extends Bloc<ChatOrderEvent, ChatOrderState> {
}
ChatOrderState _getFromPreferences() {
Map<RoomId, DateTime> get(String key) {
Map<RoomId, SortData> get(String key) {
final encoded = _preferences.getString(key);
if (encoded == null) {
......@@ -95,7 +90,7 @@ class ChatOrderBloc extends Bloc<ChatOrderEvent, ChatOrderState> {
return decoded.map(
(key, value) => MapEntry(
RoomId(key),
DateTime.fromMillisecondsSinceEpoch(value),
SortData.fromJson(value),
),
);
}
......@@ -107,13 +102,118 @@ class ChatOrderBloc extends Bloc<ChatOrderEvent, ChatOrderState> {
}
}
extension on Map<RoomId, DateTime> {
Map<RoomId, DateTime> get sorted {
@immutable
class SortData {
final int highlightedNotificationCount;
final int totalNotificationCount;
final DateTime latestMessageTime;
final bool isInvite;
SortData({
@required this.highlightedNotificationCount,
@required this.totalNotificationCount,
@required this.latestMessageTime,
@required this.isInvite,
});
static const _highlightedNotificationCountKey =
'highlighted_notification_count';
static const _totalNotificationCountKey = 'total_notification_count';
static const _latestMessageTimeKey = 'latest_message_time';
static const _isInviteKey = 'is_invite';
factory SortData.fromJson(Map<String, dynamic> json) {
return SortData(
highlightedNotificationCount: json[_highlightedNotificationCountKey],
totalNotificationCount: json[_totalNotificationCountKey],
latestMessageTime: json[_latestMessageTimeKey] != null
? DateTime.fromMillisecondsSinceEpoch(
json[_latestMessageTimeKey],
)
: null,
isInvite: json[_isInviteKey],
);
}
Map<String, dynamic> toJson() => {
_highlightedNotificationCountKey: highlightedNotificationCount,
_totalNotificationCountKey: totalNotificationCount,
_latestMessageTimeKey: latestMessageTime?.millisecondsSinceEpoch,
_isInviteKey: isInvite,
};
}
extension on List<Chat> {
Map<RoomId, SortData> toSortData() {
return Map.fromEntries(
entries.toList()
..sort(
(a, b) => -a.value.compareTo(b.value),
map(
(c) => MapEntry(
c.room.id,
SortData(
highlightedNotificationCount:
c.room.highlightedUnreadNotificationCount,
totalNotificationCount: c.room.totalUnreadNotificationCount,
latestMessageTime: c.latestMessageForSorting?.event?.time,
isInvite: c.room.myMembership == Membership.invited,
),
),
),
);
}
}
extension on Map<RoomId, SortData> {
Map<RoomId, SortData> get sorted {
final entries = this.entries.toList();
entries.sort((a, b) {
final aSortData = a.value;
final bSortData = b.value;
if (!aSortData.isInvite && !bSortData.isInvite) {
final aHighlightedNotificationCount =
aSortData.highlightedNotificationCount;
final bHighlightedNotificationCount =
bSortData.highlightedNotificationCount;
final aTotalNotificationCount = aSortData.totalNotificationCount;
final bTotalNotificationCount = bSortData.totalNotificationCount;
if (aHighlightedNotificationCount > 0 &&
bHighlightedNotificationCount <= 0) {
return -1;
} else if (aHighlightedNotificationCount <= 0 &&
bHighlightedNotificationCount > 0) {
return 1;
} else if (aTotalNotificationCount > 0 &&
bTotalNotificationCount <= 0) {
return -1;
} else if (aTotalNotificationCount <= 0 &&
bTotalNotificationCount > 0) {
return 1;
}
final aTime = aSortData.latestMessageTime;
final bTime = bSortData.latestMessageTime;
if (aTime != null && bTime != null) {
return -aTime.compareTo(bTime);
} else if (aTime != null && bTime == null) {
return -1;
} else if (aTime == null && bTime != null) {
return 1;
} else {
return 0;
}
} else if (aSortData.isInvite && !bSortData.isInvite) {
return -1;
} else if (!aSortData.isInvite && bSortData.isInvite) {
return 1;
} else {
return 0;
}
});
return Map.fromEntries(entries);
}
}
......@@ -19,9 +19,11 @@ import 'package:meta/meta.dart';
import 'package:equatable/equatable.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'bloc.dart';
class ChatOrderState extends Equatable {
final Map<RoomId, DateTime> personal;
final Map<RoomId, DateTime> public;
final Map<RoomId, SortData> personal;
final Map<RoomId, SortData> public;
ChatOrderState({@required this.personal, @required this.public});
......
......@@ -21,10 +21,12 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:moor/moor.dart';
import 'package:path/path.dart' as path;
import 'chat_order/bloc.dart';
import 'auth/bloc.dart';
import 'models/chat.dart';
......@@ -43,6 +45,9 @@ class Matrix {
// Used for listening to auth state changes
final AuthBloc _authBloc;
// Notified when chats changed.
final ChatOrderBloc _chatOrderBloc;
MyUser _user;
MyUser get user => _user;
......@@ -55,7 +60,7 @@ class Matrix {
var _chats = <RoomId, Chat>{};
Map<RoomId, Chat> get chats => _chats;
Matrix(this._authBloc) {
Matrix(this._authBloc, this._chatOrderBloc) {
_authBloc.listen(_processAuthState);
}
......@@ -107,6 +112,13 @@ class Matrix {
void _processUpdate(Update update) {
_processUser(update.user);
_chatOrderBloc.add(
UpdateChatOrder(
personal: _chats.values.notChannels.toList(),
public: _chats.values.channels.toList(),
),
);
_chatUpdatesController.add(
ChatsUpdate(
chats: _chats,
......@@ -121,6 +133,8 @@ class Matrix {
final _chatUpdatesController = StreamController<ChatsUpdate>.broadcast();
Stream<ChatsUpdate> get updates => _chatUpdatesController.stream;
Stream<ChatUpdate> updatesFor(RoomId roomId) => _chatUpdatesController.stream
.map(
(update) => ChatUpdate(
......
......@@ -47,6 +47,8 @@ class Chat {
bool get isChannel =>
room.joinRule == JoinRule.public || room.joinRule == JoinRule.knock;
bool get isInvite => room.myMembership == Membership.invited;
Chat({
@required this.room,
this.latestMessage,
......@@ -121,3 +123,9 @@ extension RoomToChat on Room {
);
}
}
extension Channels on Iterable<Chat> {
Iterable<Chat> get channels => where((c) => c.isChannel);
Iterable<Chat> get notChannels => where((c) => !c.isChannel);
}
......@@ -21,8 +21,6 @@ import 'package:bloc/bloc.dart';
import '../../../chat_order/bloc.dart';
import '../../../models/chat.dart';
import '../../../matrix.dart';
import 'event.dart';
......@@ -39,59 +37,22 @@ class ChatsBloc extends Bloc<ChatsEvent, ChatsState> {
ChatsBloc(this._matrix, this._chatOrderBloc) {
_matrix.userAvaible.then((_) {
add(RefreshChats());
_subscription = _matrix.user.updates.listen((update) {
_subscription = _chatOrderBloc.listen((update) {
add(RefreshChats());
});
});
}
Future<List<Chat>> _getChats() async {
final chats = _matrix.chats.values.toList();
chats.sort((a, b) {
if (a.room.highlightedUnreadNotificationCount > 0 &&
b.room.highlightedUnreadNotificationCount <= 0) {
return 1;
} else if (a.room.highlightedUnreadNotificationCount <= 0 &&
b.room.highlightedUnreadNotificationCount > 0) {
return -1;
} else if (a.room.totalUnreadNotificationCount > 0 &&
b.room.totalUnreadNotificationCount <= 0) {
return 1;
} else if (a.room.totalUnreadNotificationCount <= 0 &&
b.room.totalUnreadNotificationCount > 0) {
return -1;
} else if (a.latestMessageForSorting != null &&
b.latestMessageForSorting != null) {
return a.latestMessageForSorting.event.time.compareTo(
b.latestMessageForSorting.event.time,
);
} else if (a.latestMessageForSorting != null &&
b.latestMessageForSorting == null) {
return 1;
} else if (a.latestMessageForSorting == null &&
b.latestMessageForSorting != null) {
return -1;
} else {
return 0;
}
});
return chats.reversed.toList();
}
Future<ChatsState> _loadChats() async {
final chats = await _getChats();
final personalChats = chats.where((chat) => !chat.isChannel).toList();
final publicChats = chats.where((chat) => chat.isChannel).toList();
_chatOrderBloc.add(
UpdateChatOrder(
personal: personalChats,
public: publicChats,
),
);
final personalChats = _chatOrderBloc.state.personal.keys
.map((id) => _matrix.chats[id])
.where((c) => c != null)
.toList();
final publicChats = _chatOrderBloc.state.public.keys
.map((id) => _matrix.chats[id])
.where((c) => c != null)
.toList();
return ChatsLoaded(personal: personalChats, public: publicChats);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment