This commit is contained in:
Inex Code 2020-10-18 01:32:21 +00:00
commit 2a3cb73e32
16 changed files with 212 additions and 149 deletions

View file

@ -1,12 +1,18 @@
# Version 0.20.0 - 2020-??-??
### Features
- Added translations: Arabic
- Add ability to enable / disable emotes globally
- Add ability to manage emote packs with different state keys
- Add swipe to reply - Thanks @inexcode
- Initial support for compiling to desktop
- Initial snap metadata - Thanks @RAOF_47
### Changes
- Re-scale images in a separate isolate to prevent the UI from freezing
- URLs without https:// now linkify
### Fixes
- Fix amoled / theme settings not always saving properly
- Show device name in account information correctly
- Fix tapping on aliases / room pills not always working
# Version 0.19.0 - 2020-09-21
### Features

View file

@ -3,7 +3,6 @@ import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../utils/platform_infos.dart';
import '../utils/string_color.dart';
import 'matrix.dart';
@ -28,31 +27,49 @@ class Avatar extends StatelessWidget {
Matrix.of(context).client,
width: size * MediaQuery.of(context).devicePixelRatio,
height: size * MediaQuery.of(context).devicePixelRatio,
method: ThumbnailMethod.scale,
);
final src = thumbnail;
var fallbackLetters = '@';
if ((name?.length ?? 0) >= 2) {
fallbackLetters = name.substring(0, 2);
fallbackLetters = String.fromCharCodes(name.runes, 0, 2);
} else if ((name?.length ?? 0) == 1) {
fallbackLetters = name;
}
final textWidget = Center(
child: Text(
fallbackLetters,
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
);
final noPic = mxContent == null || mxContent.toString().isEmpty;
return InkWell(
onTap: onTap,
child: CircleAvatar(
radius: size / 2,
backgroundImage: !noPic
? PlatformInfos.isBetaDesktop
? NetworkImage(src)
: CachedNetworkImageProvider(src)
: null,
backgroundColor: noPic
? name?.lightColor ?? Theme.of(context).secondaryHeaderColor
: Theme.of(context).secondaryHeaderColor,
child: noPic
? Text(fallbackLetters, style: TextStyle(color: Colors.white))
: null,
child: ClipRRect(
borderRadius: BorderRadius.circular(size / 2),
child: Container(
width: size,
height: size,
color: noPic
? name?.lightColor ?? Theme.of(context).secondaryHeaderColor
: Theme.of(context).secondaryHeaderColor,
child: noPic
? textWidget
: CachedNetworkImage(
imageUrl: src,
fit: BoxFit.cover,
width: size,
height: size,
placeholder: (c, s) => Stack(
children: [
Center(child: CircularProgressIndicator(strokeWidth: 2)),
textWidget,
],
),
),
),
),
);
}

View file

@ -49,17 +49,11 @@ class ContentBanner extends StatelessWidget {
opacity: 0.75,
child: !loading
? mxContent != null
? PlatformInfos.isBetaDesktop
? Image.network(
src,
height: 300,
fit: BoxFit.cover,
)
: CachedNetworkImage(
imageUrl: src,
height: 300,
fit: BoxFit.cover,
)
? CachedNetworkImage(
imageUrl: src,
height: 300,
fit: BoxFit.cover,
)
: Icon(defaultIcon, size: 300)
: Icon(defaultIcon, size: 300),
),

View file

@ -128,17 +128,11 @@ class _ImageBubbleState extends State<ImageBubble> {
width: 800,
height: 800,
method: ThumbnailMethod.scale);
renderWidget = PlatformInfos.isBetaDesktop
? Image.network(
src,
fit: widget.fit,
)
: CachedNetworkImage(
imageUrl: src,
placeholder: (context, url) =>
generatePlaceholderWidget(),
fit: widget.fit,
);
renderWidget = CachedNetworkImage(
imageUrl: src,
placeholder: (context, url) => generatePlaceholderWidget(),
fit: widget.fit,
);
} else {
renderWidget = generatePlaceholderWidget();
}

View file

@ -150,17 +150,11 @@ class InputBar extends StatelessWidget {
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
PlatformInfos.isBetaDesktop
? Image.network(
url,
width: size,
height: size,
)
: CachedNetworkImage(
imageUrl: url,
width: size,
height: size,
),
CachedNetworkImage(
imageUrl: url,
width: size,
height: size,
),
SizedBox(width: 6),
Text(suggestion['name']),
Expanded(

View file

@ -1,4 +1,5 @@
import 'package:bot_toast/bot_toast.dart';
import 'package:circular_check_box/circular_check_box.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -12,6 +13,7 @@ import '../avatar.dart';
import '../dialogs/send_file_dialog.dart';
import '../dialogs/simple_dialogs.dart';
import '../matrix.dart';
import '../mouse_over_builder.dart';
import '../theme_switcher.dart';
class ChatListItem extends StatelessWidget {
@ -127,7 +129,20 @@ class ChatListItem extends StatelessWidget {
color: chatListItemColor(context, activeChat, selected),
child: ListTile(
onLongPress: onLongPress,
leading: Avatar(room.avatar, room.displayname),
leading: MouseOverBuilder(
builder: (context, hover) =>
onLongPress != null && (hover || selected)
? Container(
width: Avatar.defaultSize,
height: Avatar.defaultSize,
alignment: Alignment.center,
child: CircularCheckBox(
value: selected,
onChanged: (_) => onLongPress(),
),
)
: Avatar(room.avatar, room.displayname),
),
title: Row(
children: <Widget>[
Expanded(

View file

@ -5,6 +5,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../../utils/date_time_extension.dart';
import '../../utils/event_extension.dart';
import '../../utils/string_color.dart';
import '../adaptive_page_layout.dart';
import '../avatar.dart';
import '../dialogs/simple_dialogs.dart';
import '../matrix.dart';
@ -86,6 +87,8 @@ class Message extends StatelessWidget {
color: color,
borderRadius: BorderRadius.circular(radius),
),
constraints:
BoxConstraints(maxWidth: AdaptivePageLayout.defaultMinWidth),
child: Stack(
children: <Widget>[
Column(

View file

@ -8,6 +8,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:universal_html/prefer_universal/html.dart' as html;
import 'package:url_launcher/url_launcher.dart';
/*import 'package:fluffychat/views/chat.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:dbus/dbus.dart';
import 'package:desktop_notifications/desktop_notifications.dart';*/
import '../utils/app_route.dart';
import '../utils/beautify_string_extension.dart';
@ -179,9 +183,10 @@ class MatrixState extends State<Matrix> {
bool webHasFocus = true;
void _showWebNotification(EventUpdate eventUpdate) async {
if (webHasFocus && activeRoomId == eventUpdate.roomID) return;
final room = client.getRoomById(eventUpdate.roomID);
void _showLocalNotification(EventUpdate eventUpdate) async {
final roomId = eventUpdate.roomID;
if (webHasFocus && activeRoomId == roomId) return;
final room = client.getRoomById(roomId);
if (room.notificationCount == 0) return;
final event = Event.fromJson(eventUpdate.content, room);
final body = event.getLocalizedBody(
@ -189,20 +194,41 @@ class MatrixState extends State<Matrix> {
withSenderNamePrefix:
!room.isDirectChat || room.lastEvent.senderId == client.userID,
);
html.AudioElement()
..src = 'assets/assets/sounds/notification.wav'
..autoplay = true
..load();
html.Notification(
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
body: body,
icon: event.sender.avatarUrl?.getThumbnail(client,
width: 64, height: 64, method: ThumbnailMethod.crop) ??
room.avatar?.getThumbnail(client,
width: 64, height: 64, method: ThumbnailMethod.crop),
);
final icon = event.sender.avatarUrl?.getThumbnail(client,
width: 64, height: 64, method: ThumbnailMethod.crop) ??
room.avatar?.getThumbnail(client,
width: 64, height: 64, method: ThumbnailMethod.crop);
if (kIsWeb) {
html.AudioElement()
..src = 'assets/assets/sounds/notification.wav'
..autoplay = true
..load();
html.Notification(
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
body: body,
icon: icon,
);
} else if (Platform.isLinux) {
/*var sessionBus = DBusClient.session();
var client = NotificationClient(sessionBus);
_linuxNotificationIds[roomId] = await client.notify(
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
body: body,
replacesID: _linuxNotificationIds[roomId] ?? -1,
appName: AppConfig.applicationName,
actionCallback: (_) => Navigator.of(context).pushAndRemoveUntil(
AppRoute.defaultRoute(
context,
ChatView(roomId),
),
(r) => r.isFirst),
);
await sessionBus.close();*/
}
}
//final Map<String, int> _linuxNotificationIds = {};
@override
void initState() {
store = widget.store ?? Store();
@ -299,7 +325,8 @@ class MatrixState extends State<Matrix> {
if (kIsWeb) {
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);
onBlurSub = html.window.onBlur.listen((_) => webHasFocus = false);
}
if (kIsWeb || Platform.isLinux) {
client.onSync.stream.first.then((s) {
html.Notification.requestPermission();
onNotification ??= client.onEvent.stream
@ -308,7 +335,7 @@ class MatrixState extends State<Matrix> {
[EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted]
.contains(e.eventType) &&
e.content['sender'] != client.userID)
.listen(_showWebNotification);
.listen(_showLocalNotification);
});
}
super.initState();

View file

@ -93,15 +93,10 @@ class _Reaction extends StatelessWidget {
content = Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
PlatformInfos.isBetaDesktop
? Image.network(
src,
height: fontSize,
)
: CachedNetworkImage(
imageUrl: src,
height: fontSize,
),
CachedNetworkImage(
imageUrl: src,
height: fontSize,
),
Container(width: 4),
Text(count.toString(),
style: TextStyle(

View file

@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
class MouseOverBuilder extends StatefulWidget {
final Function(BuildContext, bool) builder;
const MouseOverBuilder({Key key, this.builder}) : super(key: key);
@override
_MouseOverBuilderState createState() => _MouseOverBuilderState();
}
class _MouseOverBuilderState extends State<MouseOverBuilder> {
bool _hover = false;
void _toggleHover(bool hover) {
if (_hover != hover) {
setState(() => _hover = hover);
}
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => _toggleHover(true),
onExit: (_) => _toggleHover(false),
child: widget.builder != null ? widget.builder(context, _hover) : null,
);
}
}

View file

@ -683,14 +683,11 @@ class _ChatState extends State<_Chat> {
body: Stack(
children: <Widget>[
if (Matrix.of(context).wallpaper != null)
Opacity(
opacity: 0.66,
child: Image.file(
Matrix.of(context).wallpaper,
height: double.infinity,
width: double.infinity,
fit: BoxFit.cover,
),
Image.file(
Matrix.of(context).wallpaper,
height: double.infinity,
width: double.infinity,
fit: BoxFit.cover,
),
Column(
children: <Widget>[
@ -935,8 +932,7 @@ class _ChatState extends State<_Chat> {
room.canSendDefaultMessages && room.membership == Membership.join
? Container(
decoration: BoxDecoration(
color:
Theme.of(context).backgroundColor.withOpacity(0.8),
color: Theme.of(context).backgroundColor,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,

View file

@ -420,37 +420,34 @@ class _ChatListState extends State<ChatList> {
),
),
),
floatingActionButton:
(AdaptivePageLayout.columnMode(context) ||
selectMode != SelectMode.normal)
? null
: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
heroTag: null,
child: Icon(
Icons.edit,
color: Theme.of(context).primaryColor,
),
elevation: 1,
backgroundColor:
Theme.of(context).secondaryHeaderColor,
onPressed: () => _setStatus(context),
),
SizedBox(height: 16.0),
FloatingActionButton(
child: Icon(Icons.add),
backgroundColor:
Theme.of(context).primaryColor,
onPressed: () => Navigator.of(context)
.pushAndRemoveUntil(
AppRoute.defaultRoute(
context, NewPrivateChatView()),
(r) => r.isFirst),
),
],
floatingActionButton: AdaptivePageLayout.columnMode(context)
? null
: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
heroTag: null,
child: Icon(
Icons.edit,
color: Theme.of(context).primaryColor,
),
elevation: 1,
backgroundColor:
Theme.of(context).secondaryHeaderColor,
onPressed: () => _setStatus(context),
),
SizedBox(height: 16.0),
FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Theme.of(context).primaryColor,
onPressed: () => Navigator.of(context)
.pushAndRemoveUntil(
AppRoute.defaultRoute(
context, NewPrivateChatView()),
(r) => r.isFirst),
),
],
),
body: Column(
children: [
ConnectionStatusHeader(),
@ -532,11 +529,7 @@ class _ChatListState extends State<ChatList> {
(BuildContext context, int i) {
if (i == 0) {
final displayPresences =
Matrix.of(context)
.userStatuses
.isNotEmpty &&
selectMode ==
SelectMode.normal;
selectMode != SelectMode.share;
final displayShareStatus =
selectMode ==
SelectMode.share &&

View file

@ -416,19 +416,12 @@ class _EmoteImage extends StatelessWidget {
height: size * devicePixelRatio,
method: ThumbnailMethod.scale,
);
return PlatformInfos.isBetaDesktop
? Image.network(
url,
fit: BoxFit.contain,
width: size,
height: size,
)
: CachedNetworkImage(
imageUrl: url,
fit: BoxFit.contain,
width: size,
height: size,
);
return CachedNetworkImage(
imageUrl: url,
fit: BoxFit.contain,
width: size,
height: size,
);
}
}

View file

@ -19,7 +19,7 @@ static void my_application_activate(GApplication* application) {
gtk_header_bar_set_title(header_bar, "fluffychat");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
gtk_window_set_default_size(window, 1280, 720);
gtk_window_set_default_size(window, 802, 520);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();

View file

@ -77,7 +77,7 @@ packages:
name: cached_network_image
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.2+1"
version: "2.3.3"
canonical_json:
dependency: transitive
description:
@ -99,6 +99,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
circular_check_box:
dependency: "direct main"
description:
name: circular_check_box
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
cli_util:
dependency: transitive
description:
@ -188,7 +195,7 @@ packages:
description:
path: "."
ref: yiffed
resolved-ref: c1791edd4d9788d353c618cf1717a88d6c3b547b
resolved-ref: d9bc92d6543e51845ef384ee7650fcb3428d0718
url: "https://github.com/innereq/famedlysdk-fork.git"
source: git
version: "0.0.1"
@ -233,7 +240,7 @@ packages:
name: firebase
url: "https://pub.dartlang.org"
source: hosted
version: "7.3.1"
version: "7.3.2"
firebase_core:
dependency: transitive
description:
@ -280,7 +287,7 @@ packages:
name: flutter_cache_manager
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.2"
version: "2.0.0"
flutter_keyboard_visibility:
dependency: transitive
description:
@ -301,7 +308,7 @@ packages:
name: flutter_local_notifications
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0+1"
version: "2.0.1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
@ -428,7 +435,7 @@ packages:
name: image_picker
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.7+11"
version: "0.6.7+12"
image_picker_platform_interface:
dependency: transitive
description:
@ -477,7 +484,7 @@ packages:
name: localstorage
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2+5"
version: "3.0.3+6"
logging:
dependency: transitive
description:
@ -512,7 +519,7 @@ packages:
name: matrix_link_text
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
version: "0.2.0"
meta:
dependency: transitive
description:
@ -626,7 +633,7 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.18"
version: "1.6.21"
path_provider_linux:
dependency: transitive
description:
@ -759,7 +766,7 @@ packages:
name: share
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.5+2"
version: "0.6.5+3"
shelf:
dependency: transitive
description:
@ -834,7 +841,7 @@ packages:
name: sqlite3
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.6"
version: "0.1.7"
sqlite3_flutter_libs:
dependency: "direct main"
description:
@ -946,7 +953,7 @@ packages:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "5.7.2"
version: "5.7.5"
url_launcher_linux:
dependency: transitive
description:

View file

@ -34,10 +34,10 @@ dependencies:
file_picker_cross: ^4.2.2
image_picker: ^0.6.7+11
url_launcher: ^5.7.2
cached_network_image: ^2.3.2+1
cached_network_image: ^2.3.3
firebase_messaging: ^7.0.2
flutter_local_notifications: ^2.0.0+1
matrix_link_text: ^0.1.5
matrix_link_text: ^0.2.0
path_provider: ^1.5.1
webview_flutter: ^0.3.19+9
share: ^0.6.3+5
@ -60,6 +60,7 @@ dependencies:
flutter_olm: ^1.0.1
intl: ^0.16.1
intl_translation: ^0.17.9
circular_check_box: ^1.0.4
flutter_localizations:
sdk: flutter
sqflite: ^1.1.7 # Still used to obtain the database location