5246a97db2
A new look across the app and a set of everyday messaging features. Interface - Home screen with Chats, Contacts and Settings tabs, a large title, search across chats and message text (diacritics-insensitive), filters and pinned chats. - Conversation view with a translucent header and composer, grouped bubbles, author avatars and colours in groups, swipe to reply, an "N new messages" marker, a jump-to-latest button and in-chat search. - Contact and group profiles: verification, shared photos, groups in common, disappearing messages, mute, block; members, admin badges, adding people, rename and leave for groups. - Full-screen photo viewer that pages through the whole conversation. - A stable avatar colour per person and a clearly visible read state. Messaging - Polls in chats and groups, single or multiple choice, with votes that can be changed or withdrawn; older clients see them as text. - Drafts survive leaving a chat and show in the chat list. - Forwarding of messages, photos, files and voice messages. - Notes to self: a local-only, encrypted chat that never reaches the relay. - @mentions in groups that notify even when the group is muted. - Typing indicators sent as ephemeral messages, opt-in and mutual like read receipts. - Delivery and read times per message and per group member. - Profile photos, cropped, resized and stripped of metadata, delivered encrypted to contacts. - Voice message playback at 1.5x and 2x. - Call buttons in 1:1 chats and call log entries. Fixes - Muted groups no longer notify: muting was checked against the author's chat instead of the group. - A blocked contact could unblock itself by changing its delivery address. - Read receipts no longer regress to delivered when a late delivery receipt arrives. - A message marked as failed corrects itself once it is delivered. - Double ticks in groups now mean delivered to every member.
51 lines
1.7 KiB
Dart
51 lines
1.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:sepot/core/theme/app_theme.dart';
|
|
import 'package:sepot/models/message.dart';
|
|
|
|
/// Stav mé zprávy: ⏱ odesílá se · ✓ u relaye · ✓✓ doručeno · pilulka
|
|
/// přečteno · ✕ selhalo.
|
|
///
|
|
/// „Přečteno“ se liší TVAREM (plná pilulka), ne jen barvou — dřívější
|
|
/// modrošedé ✓✓ mělo na korálové bublině kontrast ~1,2 : 1 a nešlo ho
|
|
/// odlišit od „doručeno“.
|
|
class MessageStatusIcon extends StatelessWidget {
|
|
const MessageStatusIcon({
|
|
super.key,
|
|
required this.status,
|
|
this.onEmber = false,
|
|
});
|
|
|
|
final MessageStatus status;
|
|
|
|
/// Vykresluje se na korálové bublině (jinak na tmavém pozadí).
|
|
final bool onEmber;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final soft = onEmber
|
|
? SepotColors.emberInk.withValues(alpha: 0.6)
|
|
: SepotColors.textSecondary;
|
|
return switch (status) {
|
|
MessageStatus.sending => Icon(Icons.schedule_rounded, size: 14, color: soft),
|
|
MessageStatus.sent => Icon(Icons.done_rounded, size: 15, color: soft),
|
|
MessageStatus.delivered =>
|
|
Icon(Icons.done_all_rounded, size: 15, color: soft),
|
|
MessageStatus.read => Container(
|
|
width: 21,
|
|
height: 14,
|
|
decoration: BoxDecoration(
|
|
color: onEmber ? SepotColors.emberInk : SepotColors.ember,
|
|
borderRadius: BorderRadius.circular(7),
|
|
),
|
|
child: Icon(
|
|
Icons.done_all_rounded,
|
|
size: 11,
|
|
color: onEmber ? SepotColors.ember : SepotColors.emberInk,
|
|
),
|
|
),
|
|
MessageStatus.failed =>
|
|
const Icon(Icons.error_outline_rounded, size: 15, color: SepotColors.error),
|
|
};
|
|
}
|
|
}
|