Опросы (новый тип сообщения):
- модель 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
88 lines
2.2 KiB
Dart
88 lines
2.2 KiB
Dart
class PollAnswer {
|
|
final int answerId;
|
|
final String text;
|
|
final int voteCount;
|
|
final double rate;
|
|
final List<int> votes;
|
|
|
|
const PollAnswer({
|
|
required this.answerId,
|
|
required this.text,
|
|
this.voteCount = 0,
|
|
this.rate = 0,
|
|
this.votes = const [],
|
|
});
|
|
}
|
|
|
|
class Poll {
|
|
final int pollId;
|
|
final String title;
|
|
final int settings;
|
|
final int version;
|
|
final int total;
|
|
final List<PollAnswer> answers;
|
|
final List<int> voterPreviewIds;
|
|
|
|
const Poll({
|
|
required this.pollId,
|
|
required this.title,
|
|
this.settings = 0,
|
|
this.version = 0,
|
|
this.total = 0,
|
|
this.answers = const [],
|
|
this.voterPreviewIds = const [],
|
|
});
|
|
|
|
bool get isMultiple => settings & 0x1 != 0;
|
|
|
|
bool votedBy(int userId) =>
|
|
answers.any((a) => a.votes.contains(userId));
|
|
|
|
factory Poll.fromServerMap(Map<dynamic, dynamic> map) {
|
|
final state = map['state'];
|
|
final stateMap = state is Map ? state : const {};
|
|
|
|
final resultsById = <int, Map>{};
|
|
final result = stateMap['result'];
|
|
if (result is List) {
|
|
for (final r in result) {
|
|
if (r is Map && r['answerId'] is int) {
|
|
resultsById[r['answerId'] as int] = r;
|
|
}
|
|
}
|
|
}
|
|
|
|
final answers = <PollAnswer>[];
|
|
final rawAnswers = map['answers'];
|
|
if (rawAnswers is List) {
|
|
for (final a in rawAnswers) {
|
|
if (a is! Map) continue;
|
|
final id = a['answerId'] as int? ?? 0;
|
|
final res = resultsById[id];
|
|
answers.add(PollAnswer(
|
|
answerId: id,
|
|
text: a['text']?.toString() ?? '',
|
|
voteCount: (res?['voteCount'] as num?)?.toInt() ?? 0,
|
|
rate: (res?['rate'] as num?)?.toDouble() ?? 0,
|
|
votes: (res?['votes'] as List?)
|
|
?.whereType<int>()
|
|
.toList() ??
|
|
const [],
|
|
));
|
|
}
|
|
}
|
|
|
|
return Poll(
|
|
pollId: map['pollId'] as int? ?? 0,
|
|
title: map['title']?.toString() ?? '',
|
|
settings: map['settings'] as int? ?? 0,
|
|
version: map['version'] as int? ?? 0,
|
|
total: (stateMap['total'] as num?)?.toInt() ?? 0,
|
|
answers: answers,
|
|
voterPreviewIds:
|
|
(stateMap['voterPreviewIds'] as List?)?.whereType<int>().toList() ??
|
|
const [],
|
|
);
|
|
}
|
|
}
|