feat: поддержка медиа-вложений — опросы, видео, файлы и просмотр фото
Опросы (новый тип сообщения):
- модель Poll/PollAnswer и PollAttachment (_type: POLL)
- PollsModule: загрузка через opcode 306 (GET_POLL_UPDATES) с кэшем
- рендер опроса в баблах (вопрос, варианты, прогресс-бары, голоса)
Видео:
- getVideoUrl через opcode 83 (VIDEO_PLAY): выбор MP4_*/HLS из ответа
- полноэкранный плеер (video_player) с play/pause и перемоткой
- тап по видео в чате запускает воспроизведение
Файлы:
- getFileUrl через opcode 88 (FILE_DOWNLOAD) с корректным форматом
{messageId, chatId, fileId} → url (раньше слался неверный {url, token})
- скачивание во временную папку и открытие системным приложением
(path_provider, open_filex)
Фото:
- полноэкранный просмотрщик с зумом (был TODO-заглушкой)
- фикс «сжатости»: декодирование с учётом devicePixelRatio вместо ×2
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:open_filex/open_filex.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class FileDownloadResult {
|
||||
final bool ok;
|
||||
final String? path;
|
||||
final String? error;
|
||||
|
||||
const FileDownloadResult({required this.ok, this.path, this.error});
|
||||
}
|
||||
|
||||
/// Скачивает файл по [url] во временную папку под именем [fileName]
|
||||
/// и открывает его системным приложением.
|
||||
Future<FileDownloadResult> downloadAndOpenFile(
|
||||
String url,
|
||||
String fileName,
|
||||
) async {
|
||||
try {
|
||||
final dir = await getTemporaryDirectory();
|
||||
final safeName = _sanitize(fileName);
|
||||
final file = File(p.join(dir.path, safeName));
|
||||
|
||||
final client = HttpClient();
|
||||
try {
|
||||
final request = await client.getUrl(Uri.parse(url));
|
||||
final response = await request.close();
|
||||
if (response.statusCode != 200) {
|
||||
return FileDownloadResult(ok: false, error: 'HTTP ${response.statusCode}');
|
||||
}
|
||||
final sink = file.openWrite();
|
||||
await response.pipe(sink);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
|
||||
final opened = await OpenFilex.open(file.path);
|
||||
return FileDownloadResult(
|
||||
ok: opened.type == ResultType.done,
|
||||
path: file.path,
|
||||
error: opened.type == ResultType.done ? null : opened.message,
|
||||
);
|
||||
} catch (e) {
|
||||
return FileDownloadResult(ok: false, error: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
String _sanitize(String name) {
|
||||
final cleaned = name.replaceAll(RegExp(r'[\\/:*?"<>|]'), '_').trim();
|
||||
return cleaned.isEmpty ? 'file' : cleaned;
|
||||
}
|
||||
Reference in New Issue
Block a user