fix: починил загрузку файлов

This commit is contained in:
Jganenokk
2026-07-23 17:49:56 +07:00
parent 476951e894
commit 5a3e1c8883
5 changed files with 95 additions and 40 deletions
+3 -2
View File
@@ -1786,9 +1786,10 @@ class MessagesModule {
}) async {
try {
final response = await _api.sendRequest(Opcode.fileDownload, {
'messageId': int.tryParse(messageId) ?? 0,
'chatId': chatId,
'fileId': fileId,
'chatId': chatId,
'messageId': int.tryParse(messageId) ?? 0,
'itemType': 'REGULAR',
});
if (!response.isOk) return null;
+34
View File
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
@@ -18,6 +19,8 @@ class MediaCache {
static Directory? _dir;
static int? _cachedSize;
static final Map<String, Future<File?>> _inFlight = {};
static final Set<String> _present = {};
static final Map<String, ValueNotifier<bool>> _presence = {};
static Future<Directory> _cacheDir() async {
final cached = _dir;
@@ -49,8 +52,10 @@ class MediaCache {
try {
await file.setLastModified(DateTime.now());
} catch (_) {}
_markPresent(name, true);
return file;
}
_markPresent(name, false);
return null;
}
@@ -103,6 +108,7 @@ class MediaCache {
}
await sink.close();
await part.rename(file.path);
_markPresent(name, true);
final known = _cachedSize;
if (known != null) {
try {
@@ -161,6 +167,10 @@ class MediaCache {
}
}
_cachedSize = 0;
_present.clear();
for (final notifier in _presence.values) {
notifier.value = false;
}
return freed;
}
@@ -195,11 +205,35 @@ class MediaCache {
try {
total -= await file.length();
await file.delete();
_markAbsentByBasename(p.basename(file.path));
} catch (_) {}
}
_cachedSize = total;
}
/// Реактивный флаг наличия файла [name] в кэше (для UI-иконки «скачано»).
static ValueListenable<bool> presence(String name) {
final key = _sanitize(name);
return _presence.putIfAbsent(key, () {
final notifier = ValueNotifier(_present.contains(key));
if (!notifier.value) existing(name).ignore();
return notifier;
});
}
static void _markPresent(String name, bool present) {
final key = _sanitize(name);
final changed = present ? _present.add(key) : _present.remove(key);
if (!changed) return;
_presence[key]?.value = present;
}
static void _markAbsentByBasename(String basename) {
if (_present.remove(basename)) {
_presence[basename]?.value = false;
}
}
static String _sanitize(String name) {
final cleaned = name.replaceAll(RegExp(r'[\\/:*?"<>|]'), '_').trim();
return cleaned.isEmpty ? 'file' : cleaned;
@@ -53,6 +53,7 @@ class ComposerInputBar extends StatelessWidget {
this.showAttachButton = true,
this.forceSend = false,
this.hintText = 'Message',
this.bottomSafe = true,
});
final String chatType;
@@ -87,6 +88,7 @@ class ComposerInputBar extends StatelessWidget {
final bool showAttachButton;
final bool forceSend;
final String hintText;
final bool bottomSafe;
@override
Widget build(BuildContext context) {
@@ -172,6 +174,7 @@ class ComposerInputBar extends StatelessWidget {
}
final bar = SafeArea(
bottom: bottomSafe,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
+9 -5
View File
@@ -2353,9 +2353,12 @@ class _ChatScreenState extends State<ChatScreen>
);
},
),
ComposerInputBar(
chatType: _commentsMode ? 'CHAT' : widget.chatType,
chrome: _effectiveChrome,
AnimatedBuilder(
animation: _stickers.anim,
builder: (context, _) => ComposerInputBar(
bottomSafe: _stickers.anim.value == 0,
chatType: _commentsMode ? 'CHAT' : widget.chatType,
chrome: _effectiveChrome,
style: AppComposerStyle.current.value,
background: AppComposerBackground.current.value,
backdropKey: _pillBackdrop,
@@ -2385,8 +2388,9 @@ class _ChatScreenState extends State<ChatScreen>
onSubscribe: _subscribeChannel,
showStickerButton: !_commentsMode,
showAttachButton: !_commentsMode,
forceSend: _commentsMode,
hintText: _commentsMode ? 'Комментарий' : 'Message',
forceSend: _commentsMode,
hintText: _commentsMode ? 'Комментарий' : 'Message',
),
),
StickerPanelView(
stickers: _stickers,
@@ -5,6 +5,7 @@ import 'package:komet/main.dart';
import '../../../../core/utils/download_progress.dart';
import '../../../../core/utils/file_download.dart';
import '../../../../core/utils/media_cache.dart';
import '../../../../core/utils/format.dart';
import '../../../../core/utils/haptics.dart';
import '../../../../models/attachment.dart';
@@ -113,38 +114,49 @@ class FileBubble extends StatelessWidget {
ValueListenableBuilder<double?>(
valueListenable: MediaDownloadProgress.notifier(cacheName),
builder: (context, progress, _) {
final downloading = progress != null;
return GestureDetector(
onTap: downloading
? null
: () => _downloadFile(ctx.context, file, name),
child: Container(
width: 34,
height: 34,
decoration: BoxDecoration(
color: isMe
? ctx.systemTint
: ctx.cs.surfaceContainerHighest,
shape: BoxShape.circle,
final iconColor = isMe
? ctx.cs.onPrimaryContainer
: ctx.cs.primary;
Widget circle(Widget child, VoidCallback? onTap) {
return GestureDetector(
onTap: onTap,
child: Container(
width: 34,
height: 34,
decoration: BoxDecoration(
color: isMe
? ctx.systemTint
: ctx.cs.surfaceContainerHighest,
shape: BoxShape.circle,
),
child: child,
),
child: downloading
? Padding(
padding: const EdgeInsets.all(8),
child: CircularProgressIndicator(
strokeWidth: 2,
value: progress > 0 ? progress : null,
color: isMe
? ctx.cs.onPrimaryContainer
: ctx.cs.primary,
),
)
: Icon(
Symbols.download,
color: isMe
? ctx.cs.onPrimaryContainer
: ctx.cs.primary,
size: 18,
),
);
}
if (progress != null) {
return circle(
Padding(
padding: const EdgeInsets.all(8),
child: CircularProgressIndicator(
strokeWidth: 2,
value: progress > 0 ? progress : null,
color: iconColor,
),
),
null,
);
}
return ValueListenableBuilder<bool>(
valueListenable: MediaCache.presence(cacheName),
builder: (context, cached, _) => circle(
Icon(
cached ? Symbols.check : Symbols.download,
color: iconColor,
size: 18,
),
() => _downloadFile(ctx.context, file, name),
),
);
},
@@ -171,8 +183,9 @@ class FileBubble extends StatelessWidget {
Haptics.tap();
final cacheName = '${fileId}_$name';
final cached = (await MediaCache.existing(cacheName)) != null;
MediaDownloadProgress.set(cacheName, 0);
if (!cached) MediaDownloadProgress.set(cacheName, 0);
final result = await openCachedFile(
cacheName,
() => messagesModule.getFileUrl(
@@ -182,7 +195,7 @@ class FileBubble extends StatelessWidget {
),
onProgress: (p) => MediaDownloadProgress.set(cacheName, p),
);
MediaDownloadProgress.set(cacheName, null);
if (!cached) MediaDownloadProgress.set(cacheName, null);
if (!context.mounted) return;
if (!result.ok) {
showCustomNotification(