refactor: Migrate to null safety

This commit is contained in:
Krille Fear 2022-01-29 12:35:03 +01:00 committed by Christian Pauly
commit 55f0300f9f
163 changed files with 1759 additions and 1903 deletions

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:matrix/matrix.dart';
class AccountBundles {

View file

@ -26,7 +26,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_gen/gen_l10n/l10n_en.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:http/http.dart' as http;
import 'package:matrix/matrix.dart';
@ -47,27 +46,26 @@ class NoTokenException implements Exception {
}
class BackgroundPush {
static BackgroundPush _instance;
static BackgroundPush? _instance;
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Client client;
BuildContext context;
GlobalKey<VRouterState> router;
String _fcmToken;
void Function(String errorMsg, {Uri link}) onFcmError;
L10n l10n;
Store _store;
BuildContext? context;
GlobalKey<VRouterState>? router;
String? _fcmToken;
void Function(String errorMsg, {Uri? link})? onFcmError;
L10n? l10n;
Store? _store;
Store get store => _store ??= Store();
Future<void> loadLocale() async {
// inspired by _lookupL10n in .dart_tool/flutter_gen/gen_l10n/l10n.dart
l10n ??= (context != null ? L10n.of(context) : null) ??
(await L10n.delegate.load(window.locale)) ??
L10nEn();
l10n ??= (context != null ? L10n.of(context!) : null) ??
(await L10n.delegate.load(window.locale));
}
final pendingTests = <String, Completer<void>>{};
DateTime lastReceivedPush;
DateTime? lastReceivedPush;
bool upAction = false;
@ -94,12 +92,12 @@ class BackgroundPush {
factory BackgroundPush.clientOnly(Client client) {
_instance ??= BackgroundPush._(client);
return _instance;
return _instance!;
}
factory BackgroundPush(
Client _client, BuildContext _context, GlobalKey<VRouterState> router,
{final void Function(String errorMsg, {Uri link}) onFcmError}) {
Client _client, BuildContext _context, GlobalKey<VRouterState>? router,
{final void Function(String errorMsg, {Uri? link})? onFcmError}) {
final instance = BackgroundPush.clientOnly(_client);
instance.context = _context;
// ignore: prefer_initializing_formals
@ -119,15 +117,15 @@ class BackgroundPush {
setupPush();
}
final _fcmSharedIsolate = null; //FcmSharedIsolate();
final dynamic _fcmSharedIsolate = null; //FcmSharedIsolate();
StreamSubscription<LoginState> onLogin;
StreamSubscription<SyncUpdate> onRoomSync;
StreamSubscription<LoginState>? onLogin;
StreamSubscription<SyncUpdate>? onRoomSync;
Future<void> setupPusher({
String gatewayUrl,
String token,
Set<String> oldTokens,
String? gatewayUrl,
String? token,
Set<String?>? oldTokens,
bool useDeviceSpecificAppId = false,
}) async {
if (PlatformInfos.isIOS) {
@ -135,10 +133,11 @@ class BackgroundPush {
}
final clientName = PlatformInfos.clientName;
oldTokens ??= <String>{};
final pushers = await client.getPushers().catchError((e) {
Logs().w('[Push] Unable to request pushers', e);
return <Pusher>[];
});
final pushers = await (client.getPushers().catchError((e) {
Logs().w('[Push] Unable to request pushers', e);
return <Pusher>[];
})) ??
[];
var setNewPusher = false;
// Just the plain app id, we add the .data_message suffix later
var appId = AppConfig.pushNotificationsAppId;
@ -152,7 +151,7 @@ class BackgroundPush {
appId += '.data_message';
}
final thisAppId = useDeviceSpecificAppId ? deviceAppId : appId;
if (gatewayUrl != null && token != null && clientName != null) {
if (gatewayUrl != null && token != null) {
final currentPushers = pushers.where((pusher) => pusher.pushkey == token);
if (currentPushers.length == 1 &&
currentPushers.first.kind == 'http' &&
@ -179,12 +178,8 @@ class BackgroundPush {
pusher.pushkey != token &&
deviceAppId == pusher.appId) ||
oldTokens.contains(pusher.pushkey)) {
pusher.kind = null;
try {
await client.postPusher(
pusher,
append: true,
);
await client.deletePusher(pusher);
Logs().i('[Push] Removed legacy pusher for this device');
} catch (err) {
Logs().w('[Push] Failed to remove old pusher', err);
@ -195,13 +190,13 @@ class BackgroundPush {
try {
await client.postPusher(
Pusher(
pushkey: token,
pushkey: token!,
appId: thisAppId,
appDisplayName: clientName,
deviceDisplayName: client.deviceName,
deviceDisplayName: client.deviceName!,
lang: 'en',
data: PusherData(
url: Uri.parse(gatewayUrl),
url: Uri.parse(gatewayUrl!),
format: AppConfig.pushNotificationsPusherFormat,
),
kind: 'http',
@ -259,13 +254,13 @@ class BackgroundPush {
await loadLocale();
if (PlatformInfos.isAndroid) {
onFcmError?.call(
l10n.noGoogleServicesWarning,
l10n!.noGoogleServicesWarning,
link: Uri.parse(
AppConfig.enablePushTutorial,
),
);
}
onFcmError?.call(l10n.oopsPushError);
onFcmError?.call(l10n!.oopsPushError);
if (null == await store.getItem(SettingKeys.showNoGoogle)) {
await store.setItemBool(SettingKeys.showNoGoogle, false);
@ -291,19 +286,21 @@ class BackgroundPush {
);
}
Future<void> goToRoom(String roomId) async {
Future<void> goToRoom(String? roomId) async {
try {
Logs().v('[Push] Attempting to go to room $roomId...');
if (router == null) {
return;
}
await client.roomsLoading;
await client.accountDataLoading;
final isStory = client
?.getRoomById(roomId)
.getRoomById(roomId!)
?.getState(EventTypes.RoomCreate)
?.content
?.tryGet<String>('type') ==
.tryGet<String>('type') ==
ClientStoriesExtension.storiesRoomType;
router.currentState.toSegments([isStory ? 'stories' : 'rooms', roomId]);
router!.currentState!.toSegments([isStory ? 'stories' : 'rooms', roomId]);
} catch (e, s) {
Logs().e('[Push] Failed to open room', e, s);
}
@ -318,10 +315,9 @@ class BackgroundPush {
// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project
const initializationSettingsAndroid =
AndroidInitializationSettings('notifications_icon');
final initializationSettingsIOS =
IOSInitializationSettings(onDidReceiveLocalNotification: (i, a, b, c) {
return null;
});
final initializationSettingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: (i, a, b, c) async => null,
);
final initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
@ -338,9 +334,8 @@ class BackgroundPush {
Future<void> _onFcmMessage(Map<dynamic, dynamic> message) async {
Logs().v('[Push] Foreground message received');
Map<String, dynamic> data;
final data = Map<String, dynamic>.from(message['data'] ?? message);
try {
data = Map<String, dynamic>.from(message['data'] ?? message);
await _onMessage(data);
} catch (e, s) {
Logs().e('[Push] Error while processing notification', e, s);
@ -350,7 +345,7 @@ class BackgroundPush {
Future<void> _newUpEndpoint(String newEndpoint) async {
upAction = true;
if (newEndpoint?.isEmpty ?? true) {
if (newEndpoint.isEmpty) {
await _upUnregistered();
return;
}
@ -377,7 +372,7 @@ class BackgroundPush {
'[Push] No self-hosted unified push gateway present: ' + newEndpoint);
}
Logs().i('[Push] UnifiedPush using endpoint ' + endpoint);
final oldTokens = <String>{};
final oldTokens = <String?>{};
try {
final fcmToken = await _fcmSharedIsolate?.getToken();
oldTokens.add(fcmToken);
@ -408,9 +403,9 @@ class BackgroundPush {
Future<void> _onUpMessage(String message) async {
upAction = true;
Map<String, dynamic> data;
final data =
Map<String, dynamic>.from(json.decode(message)['notification']);
try {
data = Map<String, dynamic>.from(json.decode(message)['notification']);
await _onMessage(data);
} catch (e, s) {
Logs().e('[Push] Error while processing notification', e, s);
@ -428,12 +423,14 @@ class BackgroundPush {
pendingTests.remove(eventId)?.complete();
return;
}
final unread = ((data['counts'] is String
? json
.decode(data.tryGet<String>('counts', TryGet.optional) ?? '{}')
: data.tryGet<Map<String, dynamic>>('counts', TryGet.optional) ??
<String, dynamic>{}) as Map<String, dynamic>)
.tryGet<int>('unread');
// For legacy reasons the counts map could be a String encoded JSON:
final countsMap =
data.tryGetMap<String, dynamic>('counts', TryGet.silent) ??
(jsonDecode(data.tryGet<String>('counts') ?? '{}')
as Map<String, dynamic>);
final unread = countsMap.tryGet<int>('unread');
if ((roomId?.isEmpty ?? true) ||
(eventId?.isEmpty ?? true) ||
unread == 0) {
@ -478,24 +475,24 @@ class BackgroundPush {
await _showNotification(roomId, eventId);
}
Future<bool> eventExists(String roomId, String eventId) async {
Future<bool> eventExists(String roomId, String? eventId) async {
final room = client.getRoomById(roomId);
if (room == null) return false;
return (await client.database.getEventById(eventId, room)) != null;
return (await client.database!.getEventById(eventId!, room)) != null;
}
/// Workaround for the problem that local notification IDs must be int but we
/// sort by [roomId] which is a String. To make sure that we don't have duplicated
/// IDs we map the [roomId] to a number and store this number.
Map<String, int> idMap;
late Map<String, int> idMap;
Future<void> _loadIdMap() async {
idMap ??= Map<String, int>.from(json.decode(
idMap = Map<String, int>.from(json.decode(
(await store.getItem(SettingKeys.notificationCurrentIds)) ?? '{}'));
}
Future<int> mapRoomIdToInt(String roomId) async {
await _loadIdMap();
int currentInt;
int? currentInt;
try {
currentInt = idMap[roomId];
} catch (_) {
@ -504,13 +501,13 @@ class BackgroundPush {
if (currentInt != null) {
return currentInt;
}
currentInt = 0;
var nCurrentInt = 0;
while (idMap.values.contains(currentInt)) {
currentInt++;
nCurrentInt++;
}
idMap[roomId] = currentInt;
idMap[roomId] = nCurrentInt;
await store.setItem(SettingKeys.notificationCurrentIds, json.encode(idMap));
return currentInt;
return nCurrentInt;
}
bool _clearingPushLock = false;
@ -520,7 +517,7 @@ class BackgroundPush {
}
try {
_clearingPushLock = true;
Iterable<String> emptyRooms;
late Iterable<String> emptyRooms;
if (getFromServer) {
Logs().v('[Push] Got new clearing push');
var syncErrored = false;
@ -541,8 +538,7 @@ class BackgroundPush {
});
if (!syncErrored) {
emptyRooms = client.rooms
.where((r) =>
r.notificationCount == 0 || r.notificationCount == null)
.where((r) => r.notificationCount == 0)
.map((r) => r.id);
}
}
@ -561,25 +557,23 @@ class BackgroundPush {
'[Push] failed to fetch pending notifications for clearing push, falling back...',
e);
emptyRooms = client.rooms
.where((r) =>
r.notificationCount == 0 || r.notificationCount == null)
.where((r) => r.notificationCount == 0)
.map((r) => r.id);
}
}
} else {
emptyRooms = client.rooms
.where(
(r) => r.notificationCount == 0 || r.notificationCount == null)
.where((r) => r.notificationCount == 0)
.map((r) => r.id);
}
await _loadIdMap();
var changed = false;
for (final roomId in emptyRooms) {
if (idMap[roomId] != null) {
final id = idMap[roomId];
final id = idMap[roomId];
if (id != null) {
idMap.remove(roomId);
changed = true;
await _flutterLocalNotificationsPlugin?.cancel(id);
await _flutterLocalNotificationsPlugin.cancel(id);
}
}
if (changed) {
@ -598,15 +592,14 @@ class BackgroundPush {
throw 'Room not found';
}
await room.postLoad();
final event = await client.database.getEventById(eventId, room);
final event = await client.database!.getEventById(eventId, room);
final activeRoomId = router.currentState.pathParameters['roomid'];
final activeRoomId = router!.currentState!.pathParameters['roomid'];
if (((activeRoomId?.isNotEmpty ?? false) &&
activeRoomId == room.id &&
client.syncPresence == null) ||
(event != null &&
(room.notificationCount == 0 || room.notificationCount == null))) {
(event != null && (room.notificationCount == 0))) {
return;
}
@ -614,11 +607,11 @@ class BackgroundPush {
await loadLocale();
// Calculate title
final title = l10n.unreadMessages(room.notificationCount ?? 0);
final title = l10n!.unreadMessages(room.notificationCount);
// Calculate the body
final body = event.getLocalizedBody(
MatrixLocals(L10n.of(context)),
final body = event!.getLocalizedBody(
MatrixLocals(L10n.of(context!)!),
withSenderNamePrefix: !room.isDirectChat,
plaintextBody: true,
hideReply: true,
@ -629,7 +622,7 @@ class BackgroundPush {
final avatar = room.avatar == null
? null
: await DefaultCacheManager().getSingleFile(
event.room.avatar
event.room.avatar!
.getThumbnail(
client,
width: 126,
@ -638,7 +631,7 @@ class BackgroundPush {
.toString(),
);
final person = Person(
name: room.getLocalizedDisplayname(MatrixLocals(l10n)),
name: room.getLocalizedDisplayname(MatrixLocals(l10n!)),
icon: avatar == null ? null : BitmapFilePathAndroidIcon(avatar.path),
);
@ -650,12 +643,12 @@ class BackgroundPush {
messages: [
Message(
body,
event?.originServerTs ?? DateTime.now(),
event.originServerTs,
person,
)
],
),
ticker: l10n.newMessageInFluffyChat,
ticker: l10n!.newMessageInFluffyChat,
);
const iOSPlatformChannelSpecifics = IOSNotificationDetails();
final platformChannelSpecifics = NotificationDetails(
@ -664,7 +657,7 @@ class BackgroundPush {
);
await _flutterLocalNotificationsPlugin.show(
await mapRoomIdToInt(room.id),
room.getLocalizedDisplayname(MatrixLocals(l10n)),
room.getLocalizedDisplayname(MatrixLocals(l10n!)),
body,
platformChannelSpecifics,
payload: roomId,
@ -676,16 +669,15 @@ class BackgroundPush {
await setupLocalNotificationsPlugin();
await loadLocale();
final String eventId = data['event_id'];
final String roomId = data['room_id'];
final unread = ((data['counts'] is String
? json.decode(
data.tryGet<String>('counts', TryGet.optional) ?? '{}')
: data.tryGet<Map<String, dynamic>>(
'counts', TryGet.optional) ??
<String, dynamic>{}) as Map<String, dynamic>)
.tryGet<int>('unread', TryGet.optional) ??
1;
final String? eventId = data['event_id'];
final String? roomId = data['room_id'];
// For legacy reasons the counts map could be a String encoded JSON:
final countsMap = data.tryGetMap<String, dynamic>('counts') ??
(jsonDecode(data.tryGet<String>('counts') ?? '{}')
as Map<String, dynamic>);
final unread = countsMap.tryGet<int>('unread') ?? 1;
if (unread == 0 || roomId == null || eventId == null) {
await _onClearingPush();
return;
@ -698,11 +690,11 @@ class BackgroundPush {
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics,
);
final title = l10n.unreadChats(unread);
final title = l10n!.unreadChats(unread);
await _flutterLocalNotificationsPlugin.show(
await mapRoomIdToInt(roomId),
title,
l10n.openAppToReadMessages,
l10n!.openAppToReadMessages,
platformChannelSpecifics,
payload: roomId,
);
@ -712,8 +704,8 @@ class BackgroundPush {
}
AndroidNotificationDetails _getAndroidNotificationDetails(
{MessagingStyleInformation styleInformation, String ticker}) {
final color = (context != null ? Theme.of(context).primaryColor : null) ??
{MessagingStyleInformation? styleInformation, String? ticker}) {
final color = (context != null ? Theme.of(context!).primaryColor : null) ??
const Color(0xFF5625BA);
return AndroidNotificationDetails(

View file

@ -1,5 +1,3 @@
//@dart=2.12
extension BeautifyStringExtension on String {
String get beautified {
var beautifiedStr = '';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

View file

@ -9,11 +9,11 @@ import 'package:fluffychat/utils/platform_infos.dart';
// see https://github.com/mogol/flutter_secure_storage/issues/161#issuecomment-704578453
class AsyncMutex {
Completer<void> _completer;
Completer<void>? _completer;
Future<void> lock() async {
while (_completer != null) {
await _completer.future;
await _completer!.future;
}
_completer = Completer<void>();
@ -21,15 +21,15 @@ class AsyncMutex {
void unlock() {
assert(_completer != null);
final completer = _completer;
final completer = _completer!;
_completer = null;
completer.complete();
}
}
class Store {
LocalStorage storage;
final FlutterSecureStorage secureStorage;
LocalStorage? storage;
final FlutterSecureStorage? secureStorage;
static final _mutex = AsyncMutex();
Store()
@ -44,22 +44,22 @@ class Store {
? null
: await getApplicationDocumentsDirectory());
storage = LocalStorage('LocalStorage', directory?.path);
await storage.ready;
await storage!.ready;
}
}
Future<String> getItem(String key) async {
Future<String?> getItem(String key) async {
if (!PlatformInfos.isMobile) {
await _setupLocalStorage();
try {
return storage.getItem(key)?.toString();
return storage!.getItem(key)?.toString();
} catch (_) {
return null;
}
}
try {
await _mutex.lock();
return await secureStorage.read(key: key);
return await secureStorage!.read(key: key);
} catch (_) {
return null;
} finally {
@ -67,7 +67,7 @@ class Store {
}
}
Future<bool> getItemBool(String key, [bool defaultValue]) async {
Future<bool> getItemBool(String key, [bool? defaultValue]) async {
final value = await getItem(key);
if (value == null) {
return defaultValue ?? false;
@ -76,14 +76,14 @@ class Store {
return value == '1' || value.toLowerCase() == 'true';
}
Future<void> setItem(String key, String value) async {
Future<void> setItem(String key, String? value) async {
if (!PlatformInfos.isMobile) {
await _setupLocalStorage();
return await storage.setItem(key, value);
return await storage!.setItem(key, value);
}
try {
await _mutex.lock();
return await secureStorage.write(key: key, value: value);
return await secureStorage!.write(key: key, value: value);
} finally {
_mutex.unlock();
}
@ -96,11 +96,11 @@ class Store {
Future<void> deleteItem(String key) async {
if (!PlatformInfos.isMobile) {
await _setupLocalStorage();
return await storage.deleteItem(key);
return await storage!.deleteItem(key);
}
try {
await _mutex.lock();
return await secureStorage.delete(key: key);
return await secureStorage!.delete(key: key);
} finally {
_mutex.unlock();
}

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:math';
const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:io';
import 'package:flutter/material.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:matrix/matrix.dart';
extension ClientPresenceExtension on Client {

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/cupertino.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:matrix/matrix.dart';
import '../../config/app_config.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

View file

@ -14,7 +14,7 @@ import 'package:path_provider/path_provider.dart';
import '../platform_infos.dart';
class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase {
FlutterMatrixHiveStore(String name, {HiveCipher encryptionCipher})
FlutterMatrixHiveStore(String name, {HiveCipher? encryptionCipher})
: super(
name,
encryptionCipher: encryptionCipher,
@ -28,7 +28,7 @@ class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase {
if (!kIsWeb && !_hiveInitialized) {
_hiveInitialized = true;
}
HiveCipher hiverCipher;
HiveCipher? hiverCipher;
try {
// Workaround for secure storage is calling Platform.operatingSystem on web
if (kIsWeb || Platform.isLinux) throw MissingPluginException();
@ -83,12 +83,12 @@ class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase {
return (await getApplicationDocumentsDirectory()).path;
}
} catch (_) {
return (await getDownloadsDirectory()).path;
return (await getDownloadsDirectory())!.path;
}
}
@override
Future<Uint8List> getFile(Uri mxcUri) async {
Future<Uint8List?> getFile(Uri mxcUri) async {
if (!supportsFileStoring) return null;
final tempDirectory = await _getFileStoreDirectory();
final file =

View file

@ -9,8 +9,7 @@ extension IosBadgeClientExtension on Client {
if (PlatformInfos.isIOS) {
// Workaround for iOS not clearing notifications with fcm_shared_isolate
if (!rooms.any((r) =>
r.membership == Membership.invite ||
(r.notificationCount != null && r.notificationCount > 0))) {
r.membership == Membership.invite || (r.notificationCount > 0))) {
// ignore: unawaited_futures
FlutterLocalNotificationsPlugin().cancelAll();
FlutterAppBadger.removeBadge();

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:io';
import 'package:flutter/material.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:io';
import 'package:flutter/foundation.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:matrix/matrix.dart';
import 'resize_image.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:async';
import 'package:isolate/isolate.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:async';
extension StreamExtension on Stream {

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/material.dart';
extension StringColor on String {

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:async';
import 'package:adaptive_dialog/adaptive_dialog.dart';
@ -50,7 +48,8 @@ extension UiaRequestManager on MatrixState {
),
);
case AuthenticationTypes.emailIdentity:
if (currentThreepidCreds == null || currentClientSecret == null) {
final currentThreepidCreds = this.currentThreepidCreds;
if (currentThreepidCreds == null) {
return uiaRequest.cancel(
UiaException(L10n.of(widget.context)!.serverRequiresEmail),
);

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
@ -15,22 +16,22 @@ import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
import 'platform_infos.dart';
class UrlLauncher {
final String url;
final String? url;
final BuildContext context;
const UrlLauncher(this.context, this.url);
void launchUrl() {
if (url.toLowerCase().startsWith(AppConfig.deepLinkPrefix) ||
url.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) ||
{'#', '@', '!', '+', '\$'}.contains(url[0]) ||
url.toLowerCase().startsWith(AppConfig.schemePrefix)) {
if (url!.toLowerCase().startsWith(AppConfig.deepLinkPrefix) ||
url!.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) ||
{'#', '@', '!', '+', '\$'}.contains(url![0]) ||
url!.toLowerCase().startsWith(AppConfig.schemePrefix)) {
return openMatrixToUrl();
}
final uri = Uri.tryParse(url);
final uri = Uri.tryParse(url!);
if (uri == null) {
// we can't open this thing
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).cantOpenUri(url))));
SnackBar(content: Text(L10n.of(context)!.cantOpenUri(url!))));
return;
}
if (!{'https', 'http'}.contains(uri.scheme)) {
@ -38,8 +39,7 @@ class UrlLauncher {
// we need to transmute geo URIs on desktop and on iOS
if ((!PlatformInfos.isMobile || PlatformInfos.isIOS) &&
uri.scheme == 'geo' &&
uri.path != null) {
uri.scheme == 'geo') {
final latlong = uri.path
.split(';')
.first
@ -64,12 +64,12 @@ class UrlLauncher {
return;
}
}
launch(url);
launch(url!);
return;
}
if (uri.host == null) {
if (uri.host.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).cantOpenUri(url))));
SnackBar(content: Text(L10n.of(context)!.cantOpenUri(url!))));
return;
}
// okay, we have either an http or an https URI.
@ -87,7 +87,7 @@ class UrlLauncher {
void openMatrixToUrl() async {
final matrix = Matrix.of(context);
final url = this.url.replaceFirst(
final url = this.url!.replaceFirst(
AppConfig.deepLinkPrefix,
AppConfig.inviteLinkPrefix,
);
@ -96,10 +96,10 @@ class UrlLauncher {
// identifiers (room id & event id), or it might also have a query part.
// All this needs parsing.
final identityParts = url.parseIdentifierIntoParts() ??
Uri.tryParse(url)?.host?.parseIdentifierIntoParts() ??
Uri.tryParse(url)?.host.parseIdentifierIntoParts() ??
Uri.tryParse(url)
?.pathSegments
?.lastWhere((_) => true, orElse: () => null)
.lastWhereOrNull((_) => true)
?.parseIdentifierIntoParts();
if (identityParts == null) {
return; // no match, nothing to do
@ -124,13 +124,11 @@ class UrlLauncher {
if (response.error != null) {
return; // nothing to do, the alias doesn't exist
}
roomId = response.result.roomId;
servers.addAll(response.result.servers);
room = matrix.client.getRoomById(roomId);
}
if (identityParts.via != null) {
servers.addAll(identityParts.via);
roomId = response.result!.roomId;
servers.addAll(response.result!.servers!);
room = matrix.client.getRoomById(roomId!);
}
servers.addAll(identityParts.via);
if (room != null) {
// we have the room, so....just open it
if (event != null) {
@ -170,10 +168,10 @@ class UrlLauncher {
context: context,
future: () => Future.delayed(const Duration(seconds: 2)));
if (event != null) {
VRouter.of(context).toSegments(['rooms', response.result],
VRouter.of(context).toSegments(['rooms', response.result!],
queryParameters: {'event': event});
} else {
VRouter.of(context).toSegments(['rooms', response.result]);
VRouter.of(context).toSegments(['rooms', response.result!]);
}
}
}