feat: UI-Umbau - weiches Dunkel, Schriftgroessen-Regler, Sprechblasen umschaltbar, Einstellungen als Vollbild
Bernds Entscheidungen 2026-10-07: - theme.dart: ThemeVariant (Dunkel = neues weiches Dunkelgrau als Standard, Schwarz = bisheriges Fast-Schwarz, Hell) mit PyramidPalette; Nebentexte kontrastreicher. Alte Einstellung theme_dark wird uebernommen (dunkel -> weiches Dunkel). Fest verdrahtete Dunkelfarben im Profil-Popup und GIF-Picker folgen jetzt dem Farbmodus. - appearance.dart: Schriftgroesse = Pyramid-Regler x Systemeinstellung (Handy-Standard 115 %, PC 100 %), MessageStyle Automatisch/Sprechblasen/ Liste, gemeinsame isMobileLayout-Regel. - message_bubbles.dart: Sprechblasen-Darstellung (eigene rechts in Akzentfarbe, Gruppen mit Name + Avatar, Bilder ohne Blase in voller Groesse); Listen-Darstellung unveraendert, Weiche in MessageGroup. - Formatierte Nachrichten (u. a. Antworten) nutzten RichText ohne Schriftskalierung -> erschienen kleiner; jetzt Text.rich. - Einstellungen auf dem Handy als ganze Seite, Schalter unter dem Text. - UI-Durchlauf um Liste/Hell/Gruppe/Einstellungen erweitert; Tests fuer Theme-Varianten, Uebernahme alter Werte, Schriftfaktor, Darstellung. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a1d64ec813
commit
c5f10901e6
@@ -203,8 +203,44 @@ void main() {
|
||||
);
|
||||
await room.sendTextEvent('Und noch eine letzte Nachricht 👋');
|
||||
|
||||
// Eigene Nachrichten (aus Sicht von pyramidtest1 = rechts).
|
||||
final myDm = c1.getRoomById(roomId)!;
|
||||
await myDm.sendTextEvent('Danke, sieht gut aus!');
|
||||
await myDm.sendTextEvent(
|
||||
'Und eine etwas längere eigene Antwort, damit man sieht, wie '
|
||||
'Sprechblasen umbrechen, wenn der Text über mehrere Zeilen geht.',
|
||||
);
|
||||
|
||||
// Gruppenraum (Name + Avatar je Absender in der Sprechblasen-Ansicht).
|
||||
const groupName = 'UI-Test Gruppe';
|
||||
Room? group;
|
||||
for (final r in c2.rooms) {
|
||||
if (r.membership == Membership.join && r.name == groupName) {
|
||||
group = r;
|
||||
}
|
||||
}
|
||||
if (group == null) {
|
||||
final groupId = await c2.createRoom(
|
||||
name: groupName,
|
||||
preset: CreateRoomPreset.privateChat,
|
||||
invite: [c1.userID!],
|
||||
);
|
||||
await c2
|
||||
.waitForRoomInSync(groupId, join: true)
|
||||
.timeout(const Duration(seconds: 60));
|
||||
group = c2.getRoomById(groupId)!;
|
||||
await c1.oneShotSync().timeout(const Duration(seconds: 60));
|
||||
await c1.getRoomById(groupId)?.join();
|
||||
await c1
|
||||
.waitForRoomInSync(groupId, join: true)
|
||||
.timeout(const Duration(seconds: 60));
|
||||
}
|
||||
await group.sendTextEvent('Hallo zusammen! Wer ist am Samstag dabei?');
|
||||
await group.sendTextEvent('Ich bringe Kuchen mit.');
|
||||
await c1.getRoomById(group.id)!.sendTextEvent('Ich komme gern!');
|
||||
|
||||
// ignore: avoid_print
|
||||
print('UI-Testraum bereit: $roomId');
|
||||
print('UI-Testraum bereit: $roomId, Gruppe: ${group.id}');
|
||||
} finally {
|
||||
await _logout(c1);
|
||||
await _logout(c2);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pyramid/core/appearance.dart';
|
||||
import 'package:pyramid/core/settings_prefs.dart';
|
||||
import 'package:pyramid/core/theme.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Tests für die Storage-Fassade, die laut M2-Modulschnitt zur Pflicht-
|
||||
@@ -94,22 +97,117 @@ void main() {
|
||||
group('Theme-Prefs', () {
|
||||
test('leerer Store liefert die dokumentierten Defaults', () async {
|
||||
final t = await loadThemePrefs();
|
||||
expect(t.isDark, true);
|
||||
expect(
|
||||
t.variant,
|
||||
ThemeVariant.soft,
|
||||
reason: 'Standard seit 2026-10-07: weiches Dunkel',
|
||||
);
|
||||
expect(t.accentIdx, 0);
|
||||
expect(t.radius, 12.0);
|
||||
expect(t.motion, 1.0);
|
||||
expect(t.textScale, isNull, reason: 'null = automatisch');
|
||||
expect(t.messageStyle, MessageStyle.auto);
|
||||
});
|
||||
|
||||
test('Roundtrip: gespeicherte Werte kommen zurück, Teil-Save lässt '
|
||||
'den Rest unangetastet', () async {
|
||||
await saveThemePrefs(isDark: false, accentIdx: 3, radius: 8, motion: 0.5);
|
||||
await saveThemePrefs(
|
||||
variant: ThemeVariant.light,
|
||||
accentIdx: 3,
|
||||
radius: 8,
|
||||
motion: 0.5,
|
||||
textScale: 1.3,
|
||||
messageStyle: MessageStyle.list,
|
||||
);
|
||||
var t = await loadThemePrefs();
|
||||
expect((t.isDark, t.accentIdx, t.radius, t.motion), (false, 3, 8.0, 0.5));
|
||||
expect(
|
||||
(
|
||||
t.variant,
|
||||
t.accentIdx,
|
||||
t.radius,
|
||||
t.motion,
|
||||
t.textScale,
|
||||
t.messageStyle,
|
||||
),
|
||||
(ThemeVariant.light, 3, 8.0, 0.5, 1.3, MessageStyle.list),
|
||||
);
|
||||
|
||||
// Nur ein Feld ändern – die anderen dürfen nicht zurückfallen.
|
||||
await saveThemePrefs(accentIdx: 1);
|
||||
t = await loadThemePrefs();
|
||||
expect((t.isDark, t.accentIdx, t.radius, t.motion), (false, 1, 8.0, 0.5));
|
||||
expect(
|
||||
(
|
||||
t.variant,
|
||||
t.accentIdx,
|
||||
t.radius,
|
||||
t.motion,
|
||||
t.textScale,
|
||||
t.messageStyle,
|
||||
),
|
||||
(ThemeVariant.light, 1, 8.0, 0.5, 1.3, MessageStyle.list),
|
||||
);
|
||||
|
||||
// Schriftgröße zurück auf automatisch.
|
||||
await saveThemePrefs(resetTextScale: true);
|
||||
t = await loadThemePrefs();
|
||||
expect(t.textScale, isNull);
|
||||
});
|
||||
|
||||
test(
|
||||
'Übernahme alter Einstellungen (vor 2026-10-07 nur theme_dark)',
|
||||
() async {
|
||||
SharedPreferences.setMockInitialValues({'theme_dark': true});
|
||||
expect(
|
||||
(await loadThemePrefs()).variant,
|
||||
ThemeVariant.soft,
|
||||
reason: 'wer dunkel hatte, bekommt das neue weiche Dunkel',
|
||||
);
|
||||
|
||||
SharedPreferences.setMockInitialValues({'theme_dark': false});
|
||||
expect((await loadThemePrefs()).variant, ThemeVariant.light);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'Variante „Schwarz“ wird gespeichert und theme_dark mitgepflegt',
|
||||
() async {
|
||||
await saveThemePrefs(variant: ThemeVariant.black);
|
||||
final p = await SharedPreferences.getInstance();
|
||||
expect(p.getString('theme_variant'), 'black');
|
||||
expect(
|
||||
p.getBool('theme_dark'),
|
||||
true,
|
||||
reason: 'ältere App-Versionen lesen nur theme_dark',
|
||||
);
|
||||
expect((await loadThemePrefs()).variant, ThemeVariant.black);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('Darstellung', () {
|
||||
test('Sprechblasen: Automatisch = Handy ja, PC nein', () {
|
||||
expect(MessageStyle.auto.useBubbles(mobile: true), isTrue);
|
||||
expect(MessageStyle.auto.useBubbles(mobile: false), isFalse);
|
||||
expect(MessageStyle.bubbles.useBubbles(mobile: false), isTrue);
|
||||
expect(MessageStyle.list.useBubbles(mobile: true), isFalse);
|
||||
});
|
||||
|
||||
test('Schriftfaktor wirkt zusätzlich zur Systemeinstellung', () {
|
||||
const system = TextScaler.linear(1.2);
|
||||
const scaler = PyramidTextScaler(system, 1.15);
|
||||
expect(scaler.scale(14), closeTo(14 * 1.15 * 1.2, 0.001));
|
||||
expect(defaultTextScale(mobile: true), greaterThan(1.0));
|
||||
expect(defaultTextScale(mobile: false), 1.0);
|
||||
});
|
||||
|
||||
test('Handy-Layout-Grenze', () {
|
||||
expect(isMobileLayout(const Size(411, 891)), isTrue);
|
||||
expect(isMobileLayout(const Size(1280, 720)), isFalse);
|
||||
expect(
|
||||
isMobileLayout(const Size(891, 411)),
|
||||
isTrue,
|
||||
reason: 'Handy quer (flach) bleibt Handy-Layout',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user