fluffychat merge

This commit is contained in:
ggurdin 2026-02-04 10:48:22 -05:00
commit d27758ec18
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
8 changed files with 136 additions and 99 deletions

View file

@ -1,13 +1,6 @@
*Thank you so much for your contribution to FluffyChat ❤️❤️❤️*
Please make sure that your Pull Request meet the following **acceptance criteria**:
- [ ] Code formatting and import sorting has been done with `dart format lib/ test/` and `dart run import_sorter:main --no-comments`
- [ ] The commit message uses the format of [Conventional Commits](https://www.conventionalcommits.org)
- [ ] The commit message describes what has been changed, why it has been changed and how it has been changed
- [ ] Every new feature or change of the design/GUI is linked to an approved design proposal in an issue
- [ ] Every new feature in the app or the build system has a strategy how this will be tested and maintained from now on for every release, e.g. a volunteer who takes over maintainership
- [ ] I have read and understood the [contributing guidelines](https://github.com/krille-chan/fluffychat/blob/main/CONTRIBUTING.md).
### Pull Request has been tested on:

16
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,16 @@
# Contributing to FluffyChat
Contributions are always welcome. Yet we might lack manpower to review all of them in time.
To improve the process please make sure that you read the following guidelines carefully:
## Contributing Guidelines
1. Always create a Pull Request for any changes.
2. Whenever possible please make sure that your Pull Request only contains **one** commit. Cases where multiple commits make sense are very rare.
3. Do not add merge commits. Use rebases.
4. Every Pull Request should change only one thing. For bigger changes it is often better to split them up in multiple Pull Requests.
5. [Sign your commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits).
6. Format the commit message as [Conventional Commits](https://www.conventionalcommits.org).
7. Format (`flutter format lib`) and sort impots (`dart run import_sorter:main --no-comments`) in all code files.
8. For bigger or complex changes (more than a couple of code lines) write an issue or refer to an existing issue and ask for approval from the maintainers (@krille-chan) **before** starting to implement it. This way you reduce the risk that your Pull Request get's declined.
9. Prefer simple and easy to maintain solutions over complexity and fancy ones.

View file

@ -128,6 +128,11 @@ abstract class FluffyThemes {
),
),
),
progressIndicatorTheme: ProgressIndicatorThemeData(
strokeCap: StrokeCap.round,
color: colorScheme.primary,
refreshBackgroundColor: colorScheme.primaryContainer,
),
snackBarTheme: isColumnMode
? const SnackBarThemeData(
showCloseIcon: true,

View file

@ -2670,6 +2670,45 @@
},
"noMessagesYet": "Ingen meldinger enda",
"@noMessagesYet": {},
"notificationRuleMasterDescription": "Overstyrer alle andre regler og deaktiverer alle varsler.",
"@notificationRuleMasterDescription": {},
"notificationRuleSuppressNotices": "Undertrykk automatiserte meldinger",
"@notificationRuleSuppressNotices": {},
"startedKeyVerification": "{sender} startet nøkkelverifisering",
"@startedKeyVerification": {
"type": "String",
"placeholders": {
"sender": {
"type": "String"
}
}
},
"transparent": "Gjennomsiktig",
"@transparent": {},
"stickers": "Stickers",
"@stickers": {},
"commandHint_ignore": "Ignorer den oppgitte matrix IDen",
"@commandHint_ignore": {},
"commandHint_unignore": "Opphev ignorering av den gitte matrix IDen",
"@commandHint_unignore": {},
"unreadChatsInApp": "{appname}: {unread} uleste chatter",
"@unreadChatsInApp": {
"type": "String",
"placeholders": {
"appname": {
"type": "String"
},
"unread": {
"type": "String"
}
}
},
"changeTheVisibilityOfChatHistory": "Endre synligheten til chatloggen",
"@changeTheVisibilityOfChatHistory": {},
"changeTheCanonicalRoomAlias": "Endre hovedadressen til den offentlige chatten",
"@changeTheCanonicalRoomAlias": {},
"sendRoomNotifications": "Send en @room varsling",
"@sendRoomNotifications": {},
"alwaysUse24HourFormat": "falsk",
"commandHint_googly": "Send noen googly-øyne",
"commandHint_cuddle": "Send en kose",
@ -2813,16 +2852,7 @@
"completedKeyVerification": "{sender} fullførte nøkkelverifisering",
"isReadyForKeyVerification": "{sender} er klar for nøkkelverifisering",
"requestedKeyVerification": "{sender} ba om nøkkelverifisering",
"startedKeyVerification": "{sender} startet nøkkelverifisering",
"transparent": "Gjennomsiktig",
"stickers": "Stickers",
"commandHint_ignore": "Ignorer den gitte matrix-ID-en",
"commandHint_unignore": "Fjern ignorering av den gitte matrix-ID-en",
"unreadChatsInApp": "{appname}: {unread} uleste chatter",
"knockRestricted": "Knock-begrenset",
"changeTheVisibilityOfChatHistory": "Endre synligheten av chatthistorikken",
"changeTheCanonicalRoomAlias": "Endre den viktigste offentlige chat-adressen",
"sendRoomNotifications": "Send @rom varsler",
"chatPermissionsDescription": "Definer hvilket maktnivå som er nødvendig for visse handlinger i denne chatten. Maktnivåene 0, 50 og 100 representerer vanligvis brukere, moderatorer og administratorer, men alle grader er mulige.",
"whatIsAHomeserver": "Hva er en hjemserver?",
"homeserverDescription": "Alle dataene dine lagres på hjemserveren, akkurat som en e-postleverandør. Du kan velge hvilken hjemserver du vil bruke, samtidig som du kan kommunisere med alle. Lær mer på https://matrix.org.",
@ -2843,8 +2873,6 @@
"userSpecificNotificationSettings": "Brukerspesifikke varslingsinnstillinger",
"otherNotificationSettings": "Andre varslingsinnstillinger",
"notificationRuleContainsUserNameDescription": "Varsler brukeren når en melding inneholder deres brukernavn.",
"notificationRuleMasterDescription": "Overstyrer alle andre regler og deaktiverer alle varsler.",
"notificationRuleSuppressNotices": "Undertrykk automatiserte meldinger",
"notificationRuleMemberEvent": "Medlemsarrangement",
"notificationRuleMemberEventDescription": "Undertrykker varsler for medlemsarrangementer.",
"notificationRuleIsUserMention": "Brukerhenvisning",
@ -4683,57 +4711,10 @@
}
}
},
"@startedKeyVerification": {
"type": "String",
"placeholders": {
"sender": {
"type": "String"
}
}
},
"@transparent": {
"type": "String",
"placeholders": {}
},
"@stickers": {
"type": "String",
"placeholders": {}
},
"@commandHint_ignore": {
"type": "String",
"placeholders": {}
},
"@commandHint_unignore": {
"type": "String",
"placeholders": {}
},
"@unreadChatsInApp": {
"type": "String",
"placeholders": {
"appname": {
"type": "String"
},
"unread": {
"type": "String"
}
}
},
"@knockRestricted": {
"type": "String",
"placeholders": {}
},
"@changeTheVisibilityOfChatHistory": {
"type": "String",
"placeholders": {}
},
"@changeTheCanonicalRoomAlias": {
"type": "String",
"placeholders": {}
},
"@sendRoomNotifications": {
"type": "String",
"placeholders": {}
},
"@chatPermissionsDescription": {
"type": "String",
"placeholders": {}
@ -4821,14 +4802,6 @@
"type": "String",
"placeholders": {}
},
"@notificationRuleMasterDescription": {
"type": "String",
"placeholders": {}
},
"@notificationRuleSuppressNotices": {
"type": "String",
"placeholders": {}
},
"@notificationRuleMemberEvent": {
"type": "String",
"placeholders": {}

View file

@ -65,6 +65,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
static const double buttonSize = 36;
AudioPlayerStatus status = AudioPlayerStatus.notDownloaded;
double? _downloadProgress;
late final MatrixState matrix;
List<int>? _waveform;
@ -79,8 +80,10 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
@override
void dispose() {
super.dispose();
// #Pangea
// final audioPlayer = matrix.voiceMessageEventId.value != widget.event.eventId
final audioPlayer = matrix.voiceMessageEventId.value != widget.eventId
// Pangea#
? null
: matrix.audioPlayer;
if (audioPlayer != null) {
@ -111,16 +114,10 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
// stream: audioPlayer.positionStream.asBroadcastStream(),
// builder: (context, _) => GestureDetector(
// onTap: () => FluffyChatApp.router.go(
// // #Pangea
// // '/rooms/${widget.event.room.id}?event=${widget.event.eventId}',
// '/rooms/${widget.roomId}?event=${widget.eventId}',
// // Pangea#
// '/rooms/${widget.event.room.id}?event=${widget.event.eventId}',
// ),
// child: Text(
// // #Pangea
// // '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event.senderFromMemoryOrFallback.calcDisplayname()}',
// '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event?.senderFromMemoryOrFallback.calcDisplayname() ?? widget.senderId}',
// // Pangea#
// '🎙️ ${audioPlayer.position.minuteSecondString} / ${audioPlayer.duration?.minuteSecondString} - ${widget.event.senderFromMemoryOrFallback.calcDisplayname()}',
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
// ),
@ -151,8 +148,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
audioPlayer.pause();
audioPlayer.dispose();
matrix.voiceMessageEventId.value = matrix.audioPlayer = null;
matrix.voiceMessageEventId.removeListener(_onPlayerChange);
// #Pangea
matrix.voiceMessageEventId.removeListener(_onPlayerChange);
_onAudioStateChanged?.cancel();
// Pangea#
}
@ -162,7 +159,6 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(matrix.context).clearMaterialBanners();
});
final currentPlayer =
// #Pangea
// matrix.voiceMessageEventId.value != widget.event.eventId
@ -170,7 +166,6 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
// Pangea#
? null
: matrix.audioPlayer;
if (currentPlayer != null) {
// #Pangea
currentPlayer.setSpeed(playbackSpeed);
@ -206,9 +201,25 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
setState(() => status = AudioPlayerStatus.downloading);
try {
// #Pangea
// matrixFile = await widget.event.downloadAndDecryptAttachment();
matrixFile = await widget.event?.downloadAndDecryptAttachment();
// Pangea#
// final fileSize = widget.event.content
final fileSize = widget.event?.content
// Pangea#
.tryGetMap<String, dynamic>('info')
?.tryGet<int>('size');
// #Pangea
// matrixFile = await widget.event.downloadAndDecryptAttachment(
matrixFile = await widget.event?.downloadAndDecryptAttachment(
// Pangea#
onDownloadProgress: fileSize != null && fileSize > 0
? (progress) {
final progressPercentage = progress / fileSize;
setState(() {
_downloadProgress =
progressPercentage < 1 ? progressPercentage : null;
});
}
: null,
);
// #Pangea
// if (!kIsWeb) {
@ -266,7 +277,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
final audioPlayer = matrix.audioPlayer = AudioPlayer();
// #Pangea
// #Pangea
audioPlayer.setSpeed(playbackSpeed);
_onAudioStateChanged?.cancel();
_onAudioStateChanged =
@ -326,8 +337,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
default:
setState(() => playbackSpeed = 1.0);
}
if (audioPlayer == null) return;
// Pangea#
if (audioPlayer == null) return;
switch (audioPlayer.speed) {
// #Pangea
// case 1.0:
@ -370,7 +381,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
widget.event?.content
.tryGetMap<String, dynamic>('org.matrix.msc1767.audio')
?.tryGetList<int>('waveform');
// final eventWaveForm = widget.event?.content
// final eventWaveForm = widget.event.content
// .tryGetMap<String, dynamic>('org.matrix.msc1767.audio')
// ?.tryGetList<int>('waveform');
// Pangea#
@ -409,8 +420,10 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
void initState() {
super.initState();
matrix = Matrix.of(context);
// #Pangea
WidgetsBinding.instance.addPostFrameCallback((_) => _onPlayerChange());
matrix.voiceMessageEventId.addListener(_onPlayerChange);
// Pangea#
_waveform = _getWaveform();
// #Pangea
@ -507,6 +520,7 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
? CircularProgressIndicator(
strokeWidth: 2,
color: widget.color,
value: _downloadProgress,
)
: InkWell(
borderRadius: BorderRadius.circular(64),
@ -576,8 +590,8 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
// #Pangea
// thumbColor: widget.event.senderId ==
// widget.event.room.client.userID
// ? theme.colorScheme.onPrimary
// : theme.colorScheme.primary,
// ? theme.colorScheme.onPrimary
// : theme.colorScheme.primary,
thumbColor: widget.senderId ==
Matrix.of(context).client.userID
? widget.color
@ -673,8 +687,9 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
// borderRadius:
// BorderRadius.circular(AppConfig.borderRadius),
// child: InkWell(
// borderRadius:
// BorderRadius.circular(AppConfig.borderRadius),
// borderRadius: BorderRadius.circular(
// AppConfig.borderRadius,
// ),
// onTap: _toggleSpeed,
// child: SizedBox(
// width: 32,

View file

@ -41,7 +41,9 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
final mimetype = infoMap?.tryGet<String>('mimetype');
return PlatformInfos.isAndroid ? mimetype != "video/quicktime" : true;
}
// Pangea#
double? _downloadProgress;
// The video_player package only doesn't support Windows and Linux.
// #Pangea
@ -60,7 +62,20 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
// #Pangea
setState(() => _error = null);
// Pangea#
final videoFile = await widget.event.downloadAndDecryptAttachment();
final fileSize = widget.event.content
.tryGetMap<String, dynamic>('info')
?.tryGet<int>('size');
final videoFile = await widget.event.downloadAndDecryptAttachment(
onDownloadProgress: fileSize == null
? null
: (progress) {
final progressPercentage = progress / fileSize;
setState(() {
_downloadProgress =
progressPercentage < 1 ? progressPercentage : null;
});
},
);
// Dispose the controllers if we already have them.
_disposeControllers();
@ -189,7 +204,11 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
),
),
// #Pangea
// const Center(child: CircularProgressIndicator.adaptive()),
// Center(
// child: CircularProgressIndicator.adaptive(
// value: _downloadProgress,
// ),
// ),
_error != null
? Center(
child: Column(
@ -220,7 +239,11 @@ class EventVideoPlayerState extends State<EventVideoPlayer> {
],
),
)
: const Center(child: CircularProgressIndicator.adaptive()),
: Center(
child: CircularProgressIndicator.adaptive(
value: _downloadProgress,
),
),
// Pangea#
],
);

View file

@ -14,7 +14,15 @@ extension LocalizedBody on Event {
Future<async.Result<MatrixFile?>> _getFile(BuildContext context) =>
showFutureLoadingDialog(
context: context,
future: downloadAndDecryptAttachment,
futureWithProgress: (onProgress) {
final fileSize =
infoMap['size'] is int ? infoMap['size'] as int : null;
return downloadAndDecryptAttachment(
onDownloadProgress: fileSize == null
? null
: (bytes) => onProgress(bytes / fileSize),
);
},
);
void saveFile(BuildContext context) async {

View file

@ -187,6 +187,10 @@ msix_config:
sign_msix: false
install_certificate: false
# Guidelines for adding a dependency override:
# 1. Don't do it if you can avoid it or fix it upstream in a manageable time
# 2. Always link an (upstream?) issue
# 3. Explain how and when this can be removed (overrides must be temporarily)
dependency_overrides:
fcm_shared_isolate:
path: pangea_packages/fcm_shared_isolate