Commit de99c76a authored by Wilko Manger's avatar Wilko Manger

Add ability accept invittes

parent 16915948
......@@ -154,7 +154,7 @@ extension on List<Chat> {
c.room.highlightedUnreadNotificationCount,
totalNotificationCount: c.room.totalUnreadNotificationCount,
latestMessageTime: c.latestMessageForSorting?.event?.time,
isInvite: c.room.myMembership == Membership.invited,
isInvite: c.room.me.isInvited,
),
),
),
......
......@@ -47,7 +47,7 @@ class Chat {
bool get isChannel =>
room.joinRule == JoinRule.public || room.joinRule == JoinRule.knock;
bool get isInvite => room.myMembership == Membership.invited;
bool get isInvite => room.me.isInvited;
Chat({
@required this.room,
......@@ -73,7 +73,7 @@ class Chat {
extension RoomToChat on Room {
Chat toChat({@required UserId myId}) {
// We should always have at least 30 items in the timeline, so don't load
final latestEvent = timeline.firstWhere(
var latestEvent = timeline.firstWhere(
(event) => !ignoredEvents.contains(event.runtimeType),
orElse: () => null,
);
......@@ -89,6 +89,11 @@ extension RoomToChat on Room {
orElse: () => null,
);
// For invited-to chats, the latest event is the invite event
if (me.isInvited) {
latestEvent = members.get(myId).event;
}
// If there is no non-MemberChangeEvent in the last
// 10 messages, just settle for the most recent one (which ever
// type it is).
......
......@@ -36,6 +36,7 @@ import 'bloc.dart';
import 'widgets/bubble/message.dart';
import 'widgets/bubble/state.dart';
import 'widgets/date_header.dart';
import 'widgets/input/invite/widget.dart';
import 'widgets/input/widget.dart';
class ChatPage extends StatefulWidget {
......@@ -108,6 +109,7 @@ class _ChatPageState extends State<ChatPage> {
listener: _onStateChange,
builder: (context, state) {
final chat = state.chat;
final isInvite = chat.isInvite;
Widget avatar = Container();
final avatarUrl = chat.avatarUrl;
......@@ -145,6 +147,7 @@ class _ChatPageState extends State<ChatPage> {
child: Scaffold(
backgroundColor: context.pattleTheme.data.chat.backgroundColor,
appBar: _InkWellAppbar(
onTap: !isInvite ? () => _goToChatSettings(context, state) : null,
appBar: AppBar(
leading: SizedBox(
width: kToolbarHeight,
......@@ -161,7 +164,6 @@ class _ChatPageState extends State<ChatPage> {
],
),
),
onTap: () => _goToChatSettings(context, state),
),
body: Column(
children: <Widget>[
......@@ -170,7 +172,9 @@ class _ChatPageState extends State<ChatPage> {
child: Column(
children: <Widget>[
Expanded(
child: _MessageList(chat: chat),
child: !isInvite
? _MessageList(chat: chat)
: _InviteSplash(chat: chat),
),
ConstrainedBox(
constraints: BoxConstraints.loose(
......@@ -178,10 +182,12 @@ class _ChatPageState extends State<ChatPage> {
MediaQuery.of(context).size.height / 3,
),
),
child: Input.withBloc(
roomId: chat.room.id,
canSendMessages: chat.room.myMembership is Joined,
),
child: !isInvite
? Input.withBloc(
roomId: chat.room.id,
canSendMessages: chat.room.me.hasJoined,
)
: InviteInput.withBloc(roomId: chat.room.id),
),
],
),
......@@ -364,3 +370,42 @@ class _InkWellAppbar extends StatelessWidget implements PreferredSize {
@override
Size get preferredSize => appBar.preferredSize;
}
class _InviteSplash extends StatelessWidget {
final Chat chat;
const _InviteSplash({Key key, @required this.chat}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Align(
alignment: Alignment.center,
child: Container(
width: 164,
height: 164,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryColor,
),
child: Icon(
Icons.mail,
size: 96,
color: Colors.white,
),
),
),
),
SizedBox(height: 16),
StateBubble.withContent(
// Latest message should be the invite event
message: chat.latestMessage,
),
SizedBox(height: 12),
],
);
}
}
......@@ -53,7 +53,7 @@ class ChatSettingsBloc extends Bloc<ChatSettingsEvent, ChatSettingsState> {
time: DateTime.now(),
),
content: MemberChange(
membership: _room.myMembership,
membership: _room.me.membership,
displayName: _matrix.user.name,
avatarUrl: _matrix.user.avatarUrl,
),
......
// Copyright (C) 2020 Wilko Manger
//
// This file is part of Pattle.
//
// Pattle is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pattle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:matrix_sdk/matrix_sdk.dart' hide Joined;
import '../../../../../../matrix.dart';
import 'event.dart';
import 'state.dart';
export 'state.dart';
export 'event.dart';
class InviteInputBloc extends Bloc<InviteInputEvent, InviteInputState> {
// Doesn't need the most up to date room instance.
final Room _room;
InviteInputBloc(
Matrix matrix,
RoomId roomId,
) : _room = matrix.chats[roomId].room;
@override
InviteInputState get initialState => NotAccepted();
@override
Stream<InviteInputState> mapEventToState(InviteInputEvent event) async* {
if (event is AcceptInvite) {
yield Accepting();
await _room.join();
yield Accepted();
}
if (event is RejectInvite) {
yield Rejecting();
/*_room.leave();*/
yield Rejected();
}
}
}
// Copyright (C) 2020 Wilko Manger
//
// This file is part of Pattle.
//
// Pattle is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pattle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:equatable/equatable.dart';
abstract class InviteInputEvent extends Equatable {
@override
List<Object> get props => [];
}
class AcceptInvite extends InviteInputEvent {}
class RejectInvite extends InviteInputEvent {}
// Copyright (C) 2020 Wilko Manger
//
// This file is part of Pattle.
//
// Pattle is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pattle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:equatable/equatable.dart';
abstract class InviteInputState extends Equatable {
@override
List<Object> get props => [];
}
class NotAccepted extends InviteInputState {}
class Accepting extends InviteInputState {}
class Accepted extends InviteInputState {}
class Rejecting extends InviteInputState {}
class Rejected extends InviteInputState {}
// Copyright (C) 2020 Wilko Manger
//
// This file is part of Pattle.
//
// Pattle is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pattle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import '../../../../../../matrix.dart';
import 'bloc.dart';
class InviteInput extends StatelessWidget {
final RoomId roomId;
const InviteInput._({
Key key,
@required this.roomId,
}) : super(key: key);
static Widget withBloc({@required RoomId roomId}) {
return BlocProvider<InviteInputBloc>(
create: (c) => InviteInputBloc(Matrix.of(c), roomId),
child: InviteInput._(roomId: roomId),
);
}
void _accept(BuildContext context) {
context.bloc<InviteInputBloc>().add(AcceptInvite());
}
void _reject(BuildContext context) {
context.bloc<InviteInputBloc>().add(RejectInvite());
}
static const _linearProgressIndicatorHeight = 6.0;
@override
Widget build(BuildContext context) {
return BlocBuilder<InviteInputBloc, InviteInputState>(
builder: (context, state) {
// TODO: Use Material outside of widget when Input doesn't need specific
// changes to it anymore
final isLoading = state is Accepting || state is Rejecting;
return Material(
elevation: 8,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
switchInCurve: Curves.decelerate,
switchOutCurve: Curves.decelerate,
child: isLoading
? LinearProgressIndicator()
: SizedBox(
height: _linearProgressIndicatorHeight,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
FlatButton.icon(
onPressed: !isLoading ? () => _reject(context) : null,
icon: Icon(Icons.clear),
label: Text('Reject'.toUpperCase()),
),
FlatButton.icon(
onPressed: !isLoading ? () => _accept(context) : null,
icon: Icon(Icons.check),
label: Text('Accept'.toUpperCase()),
),
],
),
// For symmetry
SizedBox(height: _linearProgressIndicatorHeight),
],
),
);
},
);
}
}
......@@ -82,15 +82,24 @@ class ChatListState extends State<ChatList> {
indent: 64,
),
itemCount: widget.chats.length,
itemBuilder: (context, index) {
return _buildChat(widget.chats[index]);
},
itemBuilder: (context, index) => _ChatTile(chat: widget.chats[index]),
),
),
);
}
}
class _ChatTile extends StatelessWidget {
final Chat chat;
Widget _buildChat(Chat chat) {
const _ChatTile({Key key, @required this.chat}) : super(key: key);
void _onTap(BuildContext context) {
Navigator.pushNamed(context, Routes.chats, arguments: chat.room.id);
}
@override
Widget build(BuildContext context) {
final time = formatAsListItem(context, chat.latestMessage?.event?.time);
return ListTile(
......@@ -113,9 +122,7 @@ class ChatListState extends State<ChatList> {
],
),
dense: false,
onTap: () {
Navigator.pushNamed(context, Routes.chats, arguments: chat.room.id);
},
onTap: () => _onTap(context),
leading: ChatAvatar(chat: chat),
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
subtitle: Subtitle.withContent(chat),
......
......@@ -101,6 +101,7 @@ class Subtitle extends StatelessWidget {
child: child,
),
),
if (_InviteIcon.necessary(context)) _InviteIcon(),
if (_NotificationCount.necessary(context)) _NotificationCount(),
],
);
......@@ -134,6 +135,21 @@ class Sender extends StatelessWidget {
}
}
class _InviteIcon extends StatelessWidget {
static bool necessary(BuildContext context) {
return Subtitle.of(context).chat.room.me.isInvited;
}
@override
Widget build(BuildContext context) {
return Icon(
Icons.mail,
color: Theme.of(context).primaryColor,
size: 20,
);
}
}
class _NotificationCount extends StatelessWidget {
static bool necessary(BuildContext context) {
return Subtitle.of(context).chat.room.totalUnreadNotificationCount > 0;
......
......@@ -322,10 +322,12 @@ packages:
matrix_sdk:
dependency: "direct main"
description:
name: matrix_sdk
url: "https://pub.dartlang.org"
source: hosted
version: "0.31.0"
path: "."
ref: "41d2b6fe414c105db27be2dc67798ca17121eb9a"
resolved-ref: "41d2b6fe414c105db27be2dc67798ca17121eb9a"
url: "https://git.pattle.im/pattle/library/matrix-dart-sdk.git"
source: git
version: "0.0.0"
mdi:
dependency: "direct main"
description:
......@@ -360,7 +362,7 @@ packages:
name: moor_ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
version: "0.6.0"
nested:
dependency: transitive
description:
......
......@@ -10,7 +10,10 @@ dependencies:
flutter_localizations:
sdk: flutter
matrix_sdk: ^0.31.0
matrix_sdk:
git:
url: https://git.pattle.im/pattle/library/matrix-dart-sdk.git
ref: 41d2b6fe414c105db27be2dc67798ca17121eb9a
async: ^2.3.0
......
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