chore: update formatting to use 120 char line length

This commit is contained in:
Garrett Watson 2023-11-13 12:14:17 -05:00
parent 0cf8bde724
commit 96d95f528d
12 changed files with 171 additions and 388 deletions

View file

@ -18,8 +18,7 @@ class AudioCacheClient {
final file = File("${directory.path}/$fileName.ogg");
if (!await file.exists()) {
await file.create(recursive: true);
final response =
await http.get(Uri.parse(Aux.resdbToHttp(clip.assetUri)));
final response = await http.get(Uri.parse(Aux.resdbToHttp(clip.assetUri)));
ApiClient.checkResponseCode(response);
await file.writeAsBytes(response.bodyBytes);
}
@ -31,8 +30,7 @@ class AudioCacheClient {
}
if (!wavFileExists) {
await wavFile.create(recursive: true);
await FFmpegKit.executeAsync(
"-y -acodec libvorbis -i ${file.path} -acodec pcm_s16le ${wavFile.path}");
await FFmpegKit.executeAsync("-y -acodec libvorbis -i ${file.path} -acodec pcm_s16le ${wavFile.path}");
}
return wavFile;
}

View file

@ -1,8 +1,7 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
as fln;
import 'package:flutter_local_notifications/flutter_local_notifications.dart' as fln;
import 'package:recon/auxiliary.dart';
import 'package:recon/models/message.dart';
import 'package:recon/models/session.dart';
@ -12,8 +11,7 @@ class NotificationChannel {
final String name;
final String description;
const NotificationChannel(
{required this.name, required this.id, required this.description});
const NotificationChannel({required this.name, required this.id, required this.description});
}
class NotificationClient {
@ -23,18 +21,15 @@ class NotificationClient {
description: "Messages received from your friends",
);
final fln.FlutterLocalNotificationsPlugin _notifier =
fln.FlutterLocalNotificationsPlugin()
..initialize(const fln.InitializationSettings(
android: fln.AndroidInitializationSettings("ic_notification"),
iOS: fln.DarwinInitializationSettings(),
macOS: fln.DarwinInitializationSettings(),
linux:
fln.LinuxInitializationSettings(defaultActionName: "Open ReCon"),
));
final fln.FlutterLocalNotificationsPlugin _notifier = fln.FlutterLocalNotificationsPlugin()
..initialize(const fln.InitializationSettings(
android: fln.AndroidInitializationSettings("ic_notification"),
iOS: fln.DarwinInitializationSettings(),
macOS: fln.DarwinInitializationSettings(),
linux: fln.LinuxInitializationSettings(defaultActionName: "Open ReCon"),
));
Future<void> showUnreadMessagesNotification(
Iterable<Message> messages) async {
Future<void> showUnreadMessagesNotification(Iterable<Message> messages) async {
if (messages.isEmpty) return;
final bySender = groupBy(messages, (p0) => p0.senderId);
@ -73,8 +68,7 @@ class NotificationClient {
break;
case MessageType.sessionInvite:
try {
final session =
Session.fromMap(jsonDecode(message.content));
final session = Session.fromMap(jsonDecode(message.content));
content = "Session Invite to ${session.name}";
} catch (e) {
content = "Session Invite";

View file

@ -38,23 +38,19 @@ void main() async {
),
);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge,
overlays: [SystemUiOverlay.top]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge, overlays: [SystemUiOverlay.top]);
await Hive.initFlutter();
final dateFormat = DateFormat.Hms();
Logger.root.onRecord.listen((event) => log(
"${dateFormat.format(event.time)}: ${event.message}",
name: event.loggerName,
time: event.time));
Logger.root.onRecord.listen(
(event) => log("${dateFormat.format(event.time)}: ${event.message}", name: event.loggerName, time: event.time));
final settingsClient = SettingsClient();
await settingsClient.loadSettings();
final newSettings = settingsClient.currentSettings.copyWith(
machineId: settingsClient.currentSettings.machineId.valueOrDefault);
await settingsClient
.changeSettings(newSettings); // Save generated machineId to disk
final newSettings =
settingsClient.currentSettings.copyWith(machineId: settingsClient.currentSettings.machineId.valueOrDefault);
await settingsClient.changeSettings(newSettings); // Save generated machineId to disk
AuthenticationData cachedAuth = AuthenticationData.unauthenticated();
try {
@ -63,15 +59,11 @@ void main() async {
// Ignore
}
runApp(
ReCon(settingsClient: settingsClient, cachedAuthentication: cachedAuth));
runApp(ReCon(settingsClient: settingsClient, cachedAuthentication: cachedAuth));
}
class ReCon extends StatefulWidget {
const ReCon(
{required this.settingsClient,
required this.cachedAuthentication,
super.key});
const ReCon({required this.settingsClient, required this.cachedAuthentication, super.key});
final SettingsClient settingsClient;
final AuthenticationData cachedAuthentication;
@ -81,8 +73,7 @@ class ReCon extends StatefulWidget {
}
class _ReConState extends State<ReCon> {
final Typography _typography =
Typography.material2021(platform: defaultTargetPlatform);
final Typography _typography = Typography.material2021(platform: defaultTargetPlatform);
final ReceivePort _port = ReceivePort();
late AuthenticationData _authData = widget.cachedAuthentication;
bool _checkedForUpdate = false;
@ -105,8 +96,7 @@ class _ReConState extends State<ReCon> {
}
try {
lastDismissedSem = SemVer.fromString(
settings.currentSettings.lastDismissedVersion.valueOrDefault);
lastDismissedSem = SemVer.fromString(settings.currentSettings.lastDismissedVersion.valueOrDefault);
} catch (_) {
lastDismissedSem = SemVer.zero();
}
@ -121,9 +111,7 @@ class _ReConState extends State<ReCon> {
return;
}
if (remoteSem > currentSem &&
navigator.overlay?.context != null &&
context.mounted) {
if (remoteSem > currentSem && navigator.overlay?.context != null && context.mounted) {
showDialog(
context: navigator.overlay!.context,
builder: (context) {
@ -141,8 +129,7 @@ class _ReConState extends State<ReCon> {
void initState() {
super.initState();
IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
_port.listen((dynamic data) {
// Not useful yet? idk...
// String id = data[0];
@ -176,26 +163,21 @@ class _ReConState extends State<ReCon> {
Phoenix.rebirth(context);
},
child: DynamicColorBuilder(
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) =>
MaterialApp(
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) => MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ReCon',
theme: ThemeData(
useMaterial3: true,
textTheme: _typography.black,
colorScheme: lightDynamic ??
ColorScheme.fromSeed(
seedColor: Colors.purple, brightness: Brightness.light),
colorScheme:
lightDynamic ?? ColorScheme.fromSeed(seedColor: Colors.purple, brightness: Brightness.light),
),
darkTheme: ThemeData(
useMaterial3: true,
textTheme: _typography.white,
colorScheme: darkDynamic ??
ColorScheme.fromSeed(
seedColor: Colors.purple, brightness: Brightness.dark),
colorScheme: darkDynamic ?? ColorScheme.fromSeed(seedColor: Colors.purple, brightness: Brightness.dark),
),
themeMode: ThemeMode.values[widget
.settingsClient.currentSettings.themeMode.valueOrDefault],
themeMode: ThemeMode.values[widget.settingsClient.currentSettings.themeMode.valueOrDefault],
home: Builder(
// Builder is necessary here since we need a context which has access to the ClientHolder
builder: (context) {
@ -208,8 +190,7 @@ class _ReConState extends State<ReCon> {
create: (context) => MessagingClient(
apiClient: clientHolder.apiClient,
settingsClient: clientHolder.settingsClient,
notificationClient:
clientHolder.notificationClient,
notificationClient: clientHolder.notificationClient,
),
),
ChangeNotifierProvider(
@ -226,15 +207,13 @@ class _ReConState extends State<ReCon> {
],
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor:
Theme.of(context).colorScheme.surfaceVariant,
statusBarColor: Theme.of(context).colorScheme.surfaceVariant,
),
child: const Home(),
),
)
: LoginScreen(
onLoginSuccessful:
(AuthenticationData authData) async {
onLoginSuccessful: (AuthenticationData authData) async {
if (authData.isAuthenticated) {
setState(() {
_authData = authData;

View file

@ -28,8 +28,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
final id = event.task.taskId;
final status = event is TaskStatusUpdate ? event.status : null;
final progress = event is TaskProgressUpdate ? event.progress : null;
final SendPort? send =
IsolateNameServer.lookupPortByName('downloader_send_port');
final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port');
send?.send([id, status, progress]);
}
@ -74,9 +73,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
style: TextStyle(
color: iClient.sortReverse == false
? Theme.of(context).colorScheme.primary
: Theme.of(context)
.colorScheme
.onSurface,
: Theme.of(context).colorScheme.onSurface,
),
),
],
@ -89,9 +86,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
Icon(Icons.arrow_downward,
color: iClient.sortReverse == true
? Theme.of(context).colorScheme.primary
: Theme.of(context)
.colorScheme
.onSurface),
: Theme.of(context).colorScheme.onSurface),
const SizedBox(
width: 8,
),
@ -100,9 +95,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
style: TextStyle(
color: iClient.sortReverse == true
? Theme.of(context).colorScheme.primary
: Theme.of(context)
.colorScheme
.onSurface,
: Theme.of(context).colorScheme.onSurface,
),
),
],
@ -128,27 +121,18 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
Icon(
e.icon,
color: iClient.sortMode == e
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface,
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
const SizedBox(
width: 8,
),
Text(
toBeginningOfSentenceCase(e.name) ??
e.name,
toBeginningOfSentenceCase(e.name) ?? e.name,
style: TextStyle(
color: iClient.sortMode == e
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface,
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
)
],
@ -173,8 +157,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
actions: [
if (iClient.selectedRecordCount == 1 &&
(iClient.selectedRecords.firstOrNull?.isLink == true ||
iClient.selectedRecords.firstOrNull?.isItem ==
true))
iClient.selectedRecords.firstOrNull?.isItem == true))
IconButton(
onPressed: () {
Share.share(iClient.selectedRecords.first.assetUri);
@ -186,12 +169,8 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
onPressed: () async {
final selectedRecords = iClient.selectedRecords;
final assetUris = selectedRecords
.map((record) => record.assetUri)
.toList();
final thumbUris = selectedRecords
.map((record) => record.thumbnailUri)
.toList();
final assetUris = selectedRecords.map((record) => record.assetUri).toList();
final thumbUris = selectedRecords.map((record) => record.thumbnailUri).toList();
final selectedUris = await showDialog<List<String>>(
context: context,
@ -232,8 +211,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
);
if (selectedUris == null) return;
final directory = await FilePicker.platform
.getDirectoryPath(dialogTitle: "Download to...");
final directory = await FilePicker.platform.getDirectoryPath(dialogTitle: "Download to...");
if (directory == null) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@ -248,17 +226,14 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content:
Text("Selected directory is invalid"),
content: Text("Selected directory is invalid"),
),
);
}
return;
}
for (var record in selectedRecords) {
final uri = selectedUris == thumbUris
? record.thumbnailUri
: record.assetUri;
final uri = selectedUris == thumbUris ? record.thumbnailUri : record.assetUri;
final filename =
"${record.id.split("-")[1]}-${record.formattedName.toString()}${extension(uri)}";
final downloadTask = DownloadTask(
@ -268,22 +243,18 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
filename: filename,
updates: Updates.statusAndProgress,
);
await FileDownloader()
.enqueue(downloadTask)
.then((b) {
await FileDownloader().enqueue(downloadTask).then((b) {
if (context.mounted) {
if (b) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Downloaded ${record.formattedName.toString()}"),
content: Text("Downloaded ${record.formattedName.toString()}"),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Failed to download ${record.formattedName.toString()}"),
content: Text("Failed to download ${record.formattedName.toString()}"),
),
);
}
@ -317,10 +288,8 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
title: Text(iClient.selectedRecordCount == 1
? "Really delete this Record?"
: "Really delete ${iClient.selectedRecordCount} Records?"),
content: const Text(
"This action cannot be undone!"),
actionsAlignment:
MainAxisAlignment.spaceBetween,
content: const Text("This action cannot be undone!"),
actionsAlignment: MainAxisAlignment.spaceBetween,
actions: [
TextButton(
onPressed: loading
@ -336,8 +305,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
if (loading)
const SizedBox.square(
dimension: 16,
child: CircularProgressIndicator(
strokeWidth: 2),
child: CircularProgressIndicator(strokeWidth: 2),
),
const SizedBox(
width: 4,
@ -350,16 +318,12 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
loading = true;
});
try {
await iClient
.deleteSelectedRecords();
await iClient.deleteSelectedRecords();
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(
context)
.showSnackBar(
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Failed to delete one or more records: $e"),
content: Text("Failed to delete one or more records: $e"),
),
);
}
@ -368,16 +332,12 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
});
}
if (context.mounted) {
Navigator.of(context)
.pop(true);
Navigator.of(context).pop(true);
}
iClient
.reloadCurrentDirectory();
iClient.reloadCurrentDirectory();
},
style: TextButton.styleFrom(
foregroundColor: Theme.of(context)
.colorScheme
.error,
foregroundColor: Theme.of(context).colorScheme.error,
),
child: const Text("Delete"),
),

View file

@ -83,10 +83,8 @@ class _LoginScreenState extends State<LoginScreen> {
_error = "Please enter your 2FA-Code";
_totpFocusNode.requestFocus();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 400),
curve: Curves.easeOutCirc);
_scrollController.animateTo(_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 400), curve: Curves.easeOutCirc);
});
} else {
_error = "The given 2FA code is not valid.";
@ -115,16 +113,14 @@ class _LoginScreenState extends State<LoginScreen> {
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"This app needs to ask your permission to send background notifications."),
title: const Text("This app needs to ask your permission to send background notifications."),
content: const Text("Are you okay with that?"),
actions: [
TextButton(
onPressed: () async {
Navigator.of(context).pop();
await settingsClient.changeSettings(settingsClient
.currentSettings
.copyWith(notificationsDenied: true));
await settingsClient
.changeSettings(settingsClient.currentSettings.copyWith(notificationsDenied: true));
},
child: const Text("No"),
),
@ -133,31 +129,21 @@ class _LoginScreenState extends State<LoginScreen> {
Navigator.of(context).pop();
final requestResult = switch (Platform.operatingSystem) {
"android" => await notificationManager
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission(),
"fuschia" =>
null, // "fuschia" is not supported by flutter_local_notifications
"fuschia" => null, // "fuschia" is not supported by flutter_local_notifications
"ios" => await notificationManager
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true, badge: true, sound: true),
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(alert: true, badge: true, sound: true),
"linux" => null, // don't want to deal with this right now
"macos" => await notificationManager
.resolvePlatformSpecificImplementation<
MacOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true, badge: true, sound: true),
"windows" =>
null, // also don't want to deal with this right now
.resolvePlatformSpecificImplementation<MacOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(alert: true, badge: true, sound: true),
"windows" => null, // also don't want to deal with this right now
_ => null,
};
await settingsClient.changeSettings(
settingsClient.currentSettings.copyWith(
notificationsDenied: requestResult == null
? false
: !requestResult));
await settingsClient.changeSettings(settingsClient.currentSettings
.copyWith(notificationsDenied: requestResult == null ? false : !requestResult));
},
child: const Text("Yes"),
)
@ -183,8 +169,7 @@ class _LoginScreenState extends State<LoginScreen> {
Padding(
padding: const EdgeInsets.symmetric(vertical: 64),
child: Center(
child: Text("Sign In",
style: Theme.of(context).textTheme.headlineMedium),
child: Text("Sign In", style: Theme.of(context).textTheme.headlineMedium),
),
),
Padding(
@ -193,8 +178,7 @@ class _LoginScreenState extends State<LoginScreen> {
controller: _usernameController,
onEditingComplete: () => _passwordFocusNode.requestFocus(),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 20, horizontal: 24),
contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 24),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32),
),
@ -210,26 +194,22 @@ class _LoginScreenState extends State<LoginScreen> {
onEditingComplete: submit,
obscureText: true,
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 20, horizontal: 24),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32)),
contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 24),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32)),
labelText: 'Password',
),
),
),
if (_needsTotp)
Padding(
padding:
const EdgeInsets.symmetric(vertical: 16, horizontal: 64),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 64),
child: TextField(
controller: _totpController,
focusNode: _totpFocusNode,
onEditingComplete: submit,
obscureText: false,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
vertical: 20, horizontal: 24),
contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 24),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32),
),
@ -252,13 +232,8 @@ class _LoginScreenState extends State<LoginScreen> {
opacity: _errorOpacity,
duration: const Duration(milliseconds: 200),
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 16, horizontal: 64),
child: Text(_error,
style: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(color: Colors.red)),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 64),
child: Text(_error, style: Theme.of(context).textTheme.labelMedium?.copyWith(color: Colors.red)),
),
),
)

View file

@ -9,8 +9,7 @@ import 'package:recon/models/message.dart';
import 'package:recon/widgets/messages/message_state_indicator.dart';
class MessageAudioPlayer extends StatefulWidget {
const MessageAudioPlayer(
{required this.message, this.foregroundColor, super.key});
const MessageAudioPlayer({required this.message, this.foregroundColor, super.key});
final Message message;
final Color? foregroundColor;
@ -19,8 +18,7 @@ class MessageAudioPlayer extends StatefulWidget {
State<MessageAudioPlayer> createState() => _MessageAudioPlayerState();
}
class _MessageAudioPlayerState extends State<MessageAudioPlayer>
with WidgetsBindingObserver {
class _MessageAudioPlayerState extends State<MessageAudioPlayer> with WidgetsBindingObserver {
final AudioPlayer _audioPlayer = AudioPlayer();
Future? _audioFileFuture;
double _sliderValue = 0;
@ -43,8 +41,7 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
super.didChangeDependencies();
final audioCache = Provider.of<AudioCacheClient>(context);
_audioFileFuture = audioCache
.cachedNetworkAudioFile(
AudioClipContent.fromMap(jsonDecode(widget.message.content)))
.cachedNetworkAudioFile(AudioClipContent.fromMap(jsonDecode(widget.message.content)))
.then((value) => _audioPlayer.setFilePath(value.path))
.whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off));
}
@ -55,8 +52,7 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
if (oldWidget.message.id == widget.message.id) return;
final audioCache = Provider.of<AudioCacheClient>(context);
_audioFileFuture = audioCache
.cachedNetworkAudioFile(
AudioClipContent.fromMap(jsonDecode(widget.message.content)))
.cachedNetworkAudioFile(AudioClipContent.fromMap(jsonDecode(widget.message.content)))
.then((value) async {
final path = _audioPlayer.setFilePath(value.path);
await _audioPlayer.setLoopMode(LoopMode.off);
@ -91,10 +87,7 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
textAlign: TextAlign.center,
softWrap: true,
maxLines: 3,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: Theme.of(context).colorScheme.error),
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).colorScheme.error),
),
],
),
@ -108,8 +101,7 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
stream: _audioPlayer.playerStateStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
FlutterError.reportError(FlutterErrorDetails(
exception: snapshot.error!, stack: snapshot.stackTrace));
FlutterError.reportError(FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace));
return _createErrorWidget("Failed to load audio-message.");
}
final playerState = snapshot.data;
@ -125,9 +117,8 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
future: _audioFileFuture,
builder: (context, fileSnapshot) {
if (fileSnapshot.hasError) {
FlutterError.reportError(FlutterErrorDetails(
exception: fileSnapshot.error!,
stack: fileSnapshot.stackTrace));
FlutterError.reportError(
FlutterErrorDetails(exception: fileSnapshot.error!, stack: fileSnapshot.stackTrace));
return const IconButton(
icon: Icon(Icons.warning),
tooltip: "Failed to load audio-message.",
@ -138,8 +129,7 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
onPressed: fileSnapshot.hasData &&
snapshot.hasData &&
playerState != null &&
playerState.processingState !=
ProcessingState.loading
playerState.processingState != ProcessingState.loading
? () {
switch (playerState.processingState) {
case ProcessingState.idle:
@ -162,15 +152,11 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
: null,
color: widget.foregroundColor,
icon: Icon(
((_audioPlayer.duration ??
const Duration(days: 9999)) -
_audioPlayer.position)
((_audioPlayer.duration ?? const Duration(days: 9999)) - _audioPlayer.position)
.inMilliseconds <
10
? Icons.replay
: ((playerState?.playing ?? false)
? Icons.pause
: Icons.play_arrow),
: ((playerState?.playing ?? false) ? Icons.pause : Icons.play_arrow),
),
);
},
@ -180,16 +166,14 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
builder: (context, snapshot) {
_sliderValue = _audioPlayer.duration == null
? 0
: (_audioPlayer.position.inMilliseconds /
(_audioPlayer.duration!.inMilliseconds))
: (_audioPlayer.position.inMilliseconds / (_audioPlayer.duration!.inMilliseconds))
.clamp(0, 1);
return StatefulBuilder(
// Not sure if this makes sense here...
builder: (context, setState) {
return SliderTheme(
data: SliderThemeData(
inactiveTrackColor:
widget.foregroundColor?.withAlpha(100),
inactiveTrackColor: widget.foregroundColor?.withAlpha(100),
),
child: Slider(
thumbColor: widget.foregroundColor,
@ -203,11 +187,7 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
});
_audioPlayer.seek(
Duration(
milliseconds: (value *
(_audioPlayer
.duration?.inMilliseconds ??
0))
.round(),
milliseconds: (value * (_audioPlayer.duration?.inMilliseconds ?? 0)).round(),
),
);
},
@ -231,8 +211,10 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer>
builder: (context, snapshot) {
return Text(
"${snapshot.data?.format() ?? "??"}/${_audioPlayer.duration?.format() ?? "??"}",
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: widget.foregroundColor?.withAlpha(150)),
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: widget.foregroundColor?.withAlpha(150)),
);
},
),

View file

@ -18,11 +18,7 @@ import 'package:recon/widgets/messages/message_attachment_list.dart';
import 'package:record/record.dart';
class MessageInputBar extends StatefulWidget {
const MessageInputBar(
{this.disabled = false,
required this.recipient,
this.onMessageSent,
super.key});
const MessageInputBar({this.disabled = false, required this.recipient, this.onMessageSent, super.key});
final bool disabled;
final Friend recipient;
@ -45,8 +41,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
String _currentText = "";
double? _sendProgress;
bool get _isRecording => _recordingStartTime != null;
set _isRecording(value) =>
_recordingStartTime = value ? DateTime.now() : null;
set _isRecording(value) => _recordingStartTime = value ? DateTime.now() : null;
bool _recordingCancelled = false;
@override
@ -56,8 +51,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
super.dispose();
}
Future<void> sendTextMessage(
ApiClient client, MessagingClient mClient, String content) async {
Future<void> sendTextMessage(ApiClient client, MessagingClient mClient, String content) async {
if (content.isEmpty) return;
final message = Message(
id: Message.generateId(),
@ -71,11 +65,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
mClient.sendMessage(message);
}
Future<void> sendImageMessage(
ApiClient client,
MessagingClient mClient,
File file,
String machineId,
Future<void> sendImageMessage(ApiClient client, MessagingClient mClient, File file, String machineId,
void Function(double progress) progressCallback) async {
final record = await RecordApi.uploadImage(
client,
@ -94,11 +84,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
mClient.sendMessage(message);
}
Future<void> sendVoiceMessage(
ApiClient client,
MessagingClient mClient,
File file,
String machineId,
Future<void> sendVoiceMessage(ApiClient client, MessagingClient mClient, File file, String machineId,
void Function(double progress) progressCallback) async {
final record = await RecordApi.uploadVoiceClip(
client,
@ -118,11 +104,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
mClient.sendMessage(message);
}
Future<void> sendRawFileMessage(
ApiClient client,
MessagingClient mClient,
File file,
String machineId,
Future<void> sendRawFileMessage(ApiClient client, MessagingClient mClient, File file, String machineId,
void Function(double progress) progressCallback) async {
final record = await RecordApi.uploadRawFile(
client,
@ -205,9 +187,8 @@ class _MessageInputBarState extends State<MessageInputBar> {
_sendProgress = 0;
});
final apiClient = cHolder.apiClient;
await sendVoiceMessage(apiClient, mClient, file,
cHolder.settingsClient.currentSettings.machineId.valueOrDefault,
(progress) {
await sendVoiceMessage(
apiClient, mClient, file, cHolder.settingsClient.currentSettings.machineId.valueOrDefault, (progress) {
setState(() {
_sendProgress = progress;
});
@ -229,8 +210,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
top: false,
child: Column(
children: [
if (_isSending && _sendProgress != null)
LinearProgressIndicator(value: _sendProgress),
if (_isSending && _sendProgress != null) LinearProgressIndicator(value: _sendProgress),
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceVariant,
@ -239,8 +219,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
duration: const Duration(milliseconds: 200),
switchInCurve: Curves.easeOut,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, animation) =>
SizeTransition(
transitionBuilder: (Widget child, animation) => SizeTransition(
sizeFactor: animation,
child: child,
),
@ -252,19 +231,12 @@ class _MessageInputBarState extends State<MessageInputBar> {
onPressed: _isSending
? null
: () async {
final result = await FilePicker.platform
.pickFiles(
type: FileType.image,
allowMultiple: true);
final result =
await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: true);
if (result != null) {
setState(() {
_loadedFiles.addAll(result.files
.map((e) => e.path != null
? (
FileType.image,
File(e.path!)
)
: null)
.map((e) => e.path != null ? (FileType.image, File(e.path!)) : null)
.whereNotNull());
});
}
@ -276,29 +248,23 @@ class _MessageInputBarState extends State<MessageInputBar> {
onPressed: _isSending
? null
: () async {
final picture = await _imagePicker
.pickImage(source: ImageSource.camera);
final picture = await _imagePicker.pickImage(source: ImageSource.camera);
if (picture == null) {
if (context.mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(
content: Text(
"Failed to get image path")));
.showSnackBar(const SnackBar(content: Text("Failed to get image path")));
}
return;
}
final file = File(picture.path);
if (await file.exists()) {
setState(() {
_loadedFiles
.add((FileType.image, file));
_loadedFiles.add((FileType.image, file));
});
} else {
if (context.mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(
content: Text(
"Failed to load image file")));
.showSnackBar(const SnackBar(content: Text("Failed to load image file")));
}
}
},
@ -309,16 +275,12 @@ class _MessageInputBarState extends State<MessageInputBar> {
onPressed: _isSending
? null
: () async {
final result = await FilePicker.platform
.pickFiles(
type: FileType.any,
allowMultiple: true);
final result =
await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: true);
if (result != null) {
setState(() {
_loadedFiles.addAll(result.files
.map((e) => e.path != null
? (FileType.any, File(e.path!))
: null)
.map((e) => e.path != null ? (FileType.any, File(e.path!)) : null)
.whereNotNull());
});
}
@ -332,8 +294,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
(_, _) => MessageAttachmentList(
disabled: _isSending,
initialFiles: _loadedFiles,
onChange: (List<(FileType, File)> loadedFiles) =>
setState(() {
onChange: (List<(FileType, File)> loadedFiles) => setState(() {
_loadedFiles.clear();
_loadedFiles.addAll(loadedFiles);
}),
@ -345,13 +306,10 @@ class _MessageInputBarState extends State<MessageInputBar> {
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder:
(Widget child, Animation<double> animation) =>
FadeTransition(
transitionBuilder: (Widget child, Animation<double> animation) => FadeTransition(
opacity: animation,
child: RotationTransition(
turns: Tween<double>(begin: 0.6, end: 1)
.animate(animation),
turns: Tween<double>(begin: 0.6, end: 1).animate(animation),
child: child,
),
),
@ -360,9 +318,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
onPressed: () {},
icon: Icon(
Icons.delete,
color: _recordingCancelled
? Theme.of(context).colorScheme.error
: null,
color: _recordingCancelled ? Theme.of(context).colorScheme.error : null,
),
),
(false, _) => IconButton(
@ -371,9 +327,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
? null
: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
"Sorry, this feature is not yet available")));
const SnackBar(content: Text("Sorry, this feature is not yet available")));
return;
// setState(() {
// _attachmentPickerOpen = true;
@ -392,10 +346,8 @@ class _MessageInputBarState extends State<MessageInputBar> {
await showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text(
"Remove all attachments"),
content: const Text(
"This will remove all attachments, are you sure?"),
title: const Text("Remove all attachments"),
content: const Text("This will remove all attachments, are you sure?"),
actions: [
TextButton(
onPressed: () {
@ -407,8 +359,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
onPressed: () {
setState(() {
_loadedFiles.clear();
_attachmentPickerOpen =
false;
_attachmentPickerOpen = false;
});
Navigator.of(context).pop();
},
@ -430,8 +381,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 4),
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
child: Stack(
children: [
TextField(
@ -453,12 +403,9 @@ class _MessageInputBarState extends State<MessageInputBar> {
style: Theme.of(context).textTheme.bodyLarge,
decoration: InputDecoration(
isDense: true,
hintText: _isRecording
? ""
: "Message ${widget.recipient.username}...",
hintText: _isRecording ? "" : "Message ${widget.recipient.username}...",
hintMaxLines: 1,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
fillColor: Colors.black26,
filled: true,
border: OutlineInputBorder(
@ -468,9 +415,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder:
(Widget child, Animation<double> animation) =>
FadeTransition(
transitionBuilder: (Widget child, Animation<double> animation) => FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween<Offset>(
@ -482,41 +427,33 @@ class _MessageInputBarState extends State<MessageInputBar> {
),
child: _isRecording
? Padding(
padding: const EdgeInsets.symmetric(
vertical: 12.0),
padding: const EdgeInsets.symmetric(vertical: 12.0),
child: _recordingCancelled
? Row(
mainAxisAlignment:
MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
width: 8,
),
const Padding(
padding: EdgeInsets.symmetric(
horizontal: 8.0),
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(
Icons.cancel,
color: Colors.red,
size: 16,
),
),
Text("Cancel Recording",
style: Theme.of(context)
.textTheme
.titleMedium),
Text("Cancel Recording", style: Theme.of(context).textTheme.titleMedium),
],
)
: Row(
mainAxisAlignment:
MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
width: 8,
),
const Padding(
padding: EdgeInsets.symmetric(
horizontal: 8.0),
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(
Icons.circle,
color: Colors.red,
@ -524,14 +461,10 @@ class _MessageInputBarState extends State<MessageInputBar> {
),
),
StreamBuilder<Duration>(
stream:
_recordingDurationStream(),
stream: _recordingDurationStream(),
builder: (context, snapshot) {
return Text(
"Recording: ${snapshot.data?.format()}",
style: Theme.of(context)
.textTheme
.titleMedium);
return Text("Recording: ${snapshot.data?.format()}",
style: Theme.of(context).textTheme.titleMedium);
}),
],
),
@ -544,13 +477,10 @@ class _MessageInputBarState extends State<MessageInputBar> {
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder:
(Widget child, Animation<double> animation) =>
FadeTransition(
transitionBuilder: (Widget child, Animation<double> animation) => FadeTransition(
opacity: animation,
child: RotationTransition(
turns: Tween<double>(begin: 0.5, end: 1)
.animate(animation),
turns: Tween<double>(begin: 0.5, end: 1).animate(animation),
child: child,
),
),
@ -563,12 +493,9 @@ class _MessageInputBarState extends State<MessageInputBar> {
? null
: () async {
final cHolder = ClientHolder.of(context);
final sMsgnr =
ScaffoldMessenger.of(context);
final settings =
cHolder.settingsClient.currentSettings;
final toSend = List<(FileType, File)>.from(
_loadedFiles);
final sMsgnr = ScaffoldMessenger.of(context);
final settings = cHolder.settingsClient.currentSettings;
final toSend = List<(FileType, File)>.from(_loadedFiles);
setState(() {
_isSending = true;
_sendProgress = 0;
@ -586,8 +513,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
file.$2,
settings.machineId.valueOrDefault,
(progress) => setState(() {
_sendProgress = totalProgress +
progress * 1 / toSend.length;
_sendProgress = totalProgress + progress * 1 / toSend.length;
}),
);
} else {
@ -596,12 +522,8 @@ class _MessageInputBarState extends State<MessageInputBar> {
mClient,
file.$2,
settings.machineId.valueOrDefault,
(progress) => setState(() =>
_sendProgress =
totalProgress +
progress *
1 /
toSend.length));
(progress) => setState(
() => _sendProgress = totalProgress + progress * 1 / toSend.length));
}
}
setState(() {
@ -609,22 +531,15 @@ class _MessageInputBarState extends State<MessageInputBar> {
});
if (_currentText.isNotEmpty) {
await sendTextMessage(
cHolder.apiClient,
mClient,
_messageTextController.text);
await sendTextMessage(cHolder.apiClient, mClient, _messageTextController.text);
}
_messageTextController.clear();
_currentText = "";
_loadedFiles.clear();
_attachmentPickerOpen = false;
} catch (e, s) {
FlutterError.reportError(
FlutterErrorDetails(
exception: e, stack: s));
sMsgnr.showSnackBar(SnackBar(
content: Text(
"Failed to send a message: $e")));
FlutterError.reportError(FlutterErrorDetails(exception: e, stack: s));
sMsgnr.showSnackBar(SnackBar(content: Text("Failed to send a message: $e")));
}
setState(() {
_isSending = false;
@ -642,9 +557,7 @@ class _MessageInputBarState extends State<MessageInputBar> {
? null
: (_) async {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
"Sorry, this feature is not yet available")));
const SnackBar(content: Text("Sorry, this feature is not yet available")));
return;
// HapticFeedback.vibrate();
// final hadToAsk =

View file

@ -17,8 +17,7 @@ class MessagesList extends StatefulWidget {
State<StatefulWidget> createState() => _MessagesListState();
}
class _MessagesListState extends State<MessagesList>
with SingleTickerProviderStateMixin {
class _MessagesListState extends State<MessagesList> with SingleTickerProviderStateMixin {
final ScrollController _sessionListScrollController = ScrollController();
bool _showSessionListScrollChevron = false;
@ -36,8 +35,7 @@ class _MessagesListState extends State<MessagesList>
void initState() {
super.initState();
_sessionListScrollController.addListener(() {
if (_sessionListScrollController.position.maxScrollExtent > 0 &&
!_showSessionListScrollChevron) {
if (_sessionListScrollController.position.maxScrollExtent > 0 && !_showSessionListScrollChevron) {
setState(() {
_showSessionListScrollChevron = true;
});
@ -58,9 +56,7 @@ class _MessagesListState extends State<MessagesList>
return Consumer<MessagingClient>(builder: (context, mClient, _) {
final friend = mClient.selectedFriend ?? Friend.empty();
final cache = mClient.getUserMessageCache(friend.id);
final sessions = friend.userStatus.decodedSessions
.where((element) => element.isVisible)
.toList();
final sessions = friend.userStatus.decodedSessions.where((element) => element.isVisible).toList();
return Scaffold(
appBar: AppBar(
title: Row(
@ -77,10 +73,7 @@ class _MessagesListState extends State<MessagesList>
child: Icon(
Icons.dns,
size: 18,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer
.withAlpha(150),
color: Theme.of(context).colorScheme.onSecondaryContainer.withAlpha(150),
),
),
],
@ -110,8 +103,8 @@ class _MessagesListState extends State<MessagesList>
if (sessions.isNotEmpty)
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder: (child, animation) => SizeTransition(
sizeFactor: animation, axis: Axis.vertical, child: child),
transitionBuilder: (child, animation) =>
SizeTransition(sizeFactor: animation, axis: Axis.vertical, child: child),
child: sessions.isEmpty || !_sessionListOpen
? null
: Container(
@ -139,8 +132,7 @@ class _MessagesListState extends State<MessagesList>
child: Align(
alignment: Alignment.centerRight,
child: Container(
padding: const EdgeInsets.only(
left: 16, right: 4, top: 1, bottom: 1),
padding: const EdgeInsets.only(left: 16, right: 4, top: 1, bottom: 1),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
@ -190,13 +182,11 @@ class _MessagesListState extends State<MessagesList>
children: [
const Icon(Icons.message_outlined),
Padding(
padding:
const EdgeInsets.symmetric(vertical: 24),
padding: const EdgeInsets.symmetric(vertical: 24),
child: Text(
"There are no messages here\nWhy not say hello?",
textAlign: TextAlign.center,
style:
Theme.of(context).textTheme.titleMedium,
style: Theme.of(context).textTheme.titleMedium,
),
)
],
@ -207,8 +197,7 @@ class _MessagesListState extends State<MessagesList>
create: (BuildContext context) => AudioCacheClient(),
child: ListView.builder(
reverse: true,
physics: const BouncingScrollPhysics(
decelerationRate: ScrollDecelerationRate.fast),
physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast),
itemCount: cache.messages.length,
itemBuilder: (context, index) {
final entry = cache.messages[index];

View file

@ -17,15 +17,12 @@ class SessionTile extends StatelessWidget {
foregroundColor: Theme.of(context).colorScheme.onSurface,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => SessionView(session: session)));
Navigator.of(context).push(MaterialPageRoute(builder: (context) => SessionView(session: session)));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GenericAvatar(
imageUri: Aux.resdbToHttp(session.thumbnailUrl),
placeholderIcon: Icons.no_photography),
GenericAvatar(imageUri: Aux.resdbToHttp(session.thumbnailUrl), placeholderIcon: Icons.no_photography),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
@ -35,11 +32,10 @@ class SessionTile extends StatelessWidget {
FormattedText(session.formattedName),
Text(
"${session.sessionUsers.length.toString().padLeft(2, "0")}/${session.maxUsers.toString().padLeft(2, "0")} active users",
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(.6)),
style: Theme.of(context)
.textTheme
.labelMedium
?.copyWith(color: Theme.of(context).colorScheme.onSurface.withOpacity(.6)),
)
],
),

View file

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:recon/clients/session_client.dart';
import 'package:recon/widgets/sessions/session_filter_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
class SessionListAppBar extends StatefulWidget {
const SessionListAppBar({super.key});

View file

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SettingsAppBar extends StatelessWidget {
const SettingsAppBar({super.key});

View file

@ -7,7 +7,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:recon/main.dart';
void main() {