Show real photos for people instead of scene art

Your avatar is your photo, picked from the device by tapping it on Home or
from a new Profile photo row in Settings, shrunk to 256 px and kept on the
phone (there is no upload endpoint yet). Group members show the photo the
server sends in a new `photo` field, everywhere avatars appear. Without a
photo, an initial on a neutral disc. Mock mode bundles free Unsplash
portraits for the sample people.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
This commit is contained in:
alvocool
2026-11-09 08:22:41 +03:00
parent 72786701a9
commit 1b5d105400
33 changed files with 406 additions and 168 deletions

View File

@@ -35,6 +35,18 @@ class MockGroups {
static final Random _random = Random(7);
/// Sample people with a bundled portrait (assets/images/person_*.jpg).
static const List<String> _photos = <String>[
'alex',
'sam',
'priya',
'jordan',
'mei',
'tom',
'ade',
'lena'
];
static Map<String, dynamic> _member(String id, String name,
{String role = "Member",
double kept = 0.7,
@@ -44,6 +56,9 @@ class MockGroups {
return <String, dynamic>{
'id': id,
'name': name,
'photo': _photos.contains(name.toLowerCase())
? 'asset:assets/images/person_${name.toLowerCase()}.jpg'
: '',
'role': role,
'keptRate': kept,
'streak': streak,
@@ -55,9 +70,8 @@ class MockGroups {
};
}
static Map<String, dynamic> _objective(
String id, String title, String unit, double target, double current,
double mine) {
static Map<String, dynamic> _objective(String id, String title, String unit,
double target, double current, double mine) {
return <String, dynamic>{
'id': id,
'title': title,
@@ -105,7 +119,8 @@ class MockGroups {
_member('m-sam', 'Sam',
role: 'Owner', kept: 0.92, streak: 11, checkedInHoursAgo: 2),
_member(_me, _myName, kept: 0.78, streak: 4, checkedInHoursAgo: 4),
_member('m-priya', 'Priya', kept: 0.85, streak: 7, checkedInHoursAgo: 3),
_member('m-priya', 'Priya',
kept: 0.85, streak: 7, checkedInHoursAgo: 3),
_member('m-jordan', 'Jordan',
kept: 0.41, streak: 0, standing: 'Warned', checkedInHoursAgo: 52),
],
@@ -114,7 +129,8 @@ class MockGroups {
'id': 'gg-run',
'title': 'Run 200 km this month',
'description': 'Every kilometre counts, treadmill included.',
'targetDate': DateTime(now.year, now.month + 1, 0).toIso8601String(),
'targetDate':
DateTime(now.year, now.month + 1, 0).toIso8601String(),
'colourHex': colorToHex(goalAccents[1]),
'objectives': <Map<String, dynamic>>[
_objective('ob-km', 'Distance run', 'km', 200, 86, 22),
@@ -151,11 +167,14 @@ class MockGroups {
'id': 'gg-drafts',
'title': 'Full drafts submitted',
'description': 'Three theses, one deadline.',
'targetDate': DateTime(now.year, now.month + 1, 15).toIso8601String(),
'targetDate':
DateTime(now.year, now.month + 1, 15).toIso8601String(),
'colourHex': colorToHex(goalAccents[3]),
'objectives': <Map<String, dynamic>>[
_objective('ob-words', 'Words written', 'words', 30000, 18400, 7200),
_objective('ob-chapters', 'Chapters reviewed', 'chapters', 9, 3, 1),
_objective(
'ob-words', 'Words written', 'words', 30000, 18400, 7200),
_objective(
'ob-chapters', 'Chapters reviewed', 'chapters', 9, 3, 1),
_objective('ob-hours', 'Deep-work hours', 'hours', 120, 64, 23),
],
},
@@ -191,15 +210,17 @@ class MockGroups {
_messages['group-run'] = <Map<String, dynamic>>[
_message('group-run', 'm-sam', 'Sam',
'Same route tomorrow? Gate at 6:15.', 60 * 20),
_message('group-run', _me, _myName, "I'm in. Bringing Priya's spare torch.",
60 * 19 + 40),
_message('group-run', _me, _myName,
"I'm in. Bringing Priya's spare torch.", 60 * 19 + 40),
_message('group-run', 'm-priya', 'Priya', 'logged 6 km on Distance run',
60 * 3, kind: 'CheckIn'),
_message('group-run', 'm-sam', 'Sam', 'logged 8 km on Distance run',
60 * 2, kind: 'CheckIn'),
60 * 3,
kind: 'CheckIn'),
_message(
'group-run', 'm-sam', 'Sam', 'logged 8 km on Distance run', 60 * 2,
kind: 'CheckIn'),
_message('group-run', 'm-sam', 'Sam',
'nudged Jordan: two days without a run, we miss you out there',
95, kind: 'Nudge'),
'nudged Jordan: two days without a run, we miss you out there', 95,
kind: 'Nudge'),
_message('group-run', 'm-priya', 'Priya',
'86 km down. We need about 9 a day to make it, doable.', 40),
];
@@ -208,10 +229,15 @@ class MockGroups {
kind: 'System'),
_message('group-thesis', 'm-mei', 'Mei',
'Chapter 3 is with my supervisor. Terrified.', 60 * 26),
_message('group-thesis', _me, _myName, 'logged 1,200 words on Words written',
60 * 22, kind: 'CheckIn'),
_message('group-thesis', 'm-tom', 'Tom',
'Library 9 to 1 tomorrow if anyone wants a co-working block.', 60 * 7),
_message('group-thesis', _me, _myName,
'logged 1,200 words on Words written', 60 * 22,
kind: 'CheckIn'),
_message(
'group-thesis',
'm-tom',
'Tom',
'Library 9 to 1 tomorrow if anyone wants a co-working block.',
60 * 7),
];
_messages['group-walk'] = <Map<String, dynamic>>[
_message('group-walk', 'm-ade', 'Ade',
@@ -236,7 +262,8 @@ class MockGroups {
.toList();
for (Map<String, dynamic> message in due) {
_pending.remove(message);
_messages.putIfAbsent(message['groupId'], () => <Map<String, dynamic>>[])
_messages
.putIfAbsent(message['groupId'], () => <Map<String, dynamic>>[])
.add(message);
final Map<String, dynamic>? group = _find(message['groupId']);
if (group != null) {
@@ -259,8 +286,9 @@ class MockGroups {
}
static void _post(String groupId, String body, String kind) {
_messages.putIfAbsent(groupId, () => <Map<String, dynamic>>[]).add(
_message(groupId, _me, _myName, body, 0, kind: kind));
_messages
.putIfAbsent(groupId, () => <Map<String, dynamic>>[])
.add(_message(groupId, _me, _myName, body, 0, kind: kind));
}
/// Someone answers a few seconds later, so the chat feels inhabited.
@@ -331,8 +359,8 @@ class MockGroups {
'inviteCode': _code(),
'unread': 0,
'members': <Map<String, dynamic>>[
_member(_me, _myName, role: 'Owner', kept: 0.78, streak: 4,
checkedInHoursAgo: 0),
_member(_me, _myName,
role: 'Owner', kept: 0.78, streak: 4, checkedInHoursAgo: 0),
],
'goals': <Map<String, dynamic>>[],
};
@@ -366,8 +394,8 @@ class MockGroups {
'members': <Map<String, dynamic>>[],
'goals': <Map<String, dynamic>>[],
});
(group['members'] as List).add(_member(_me, _myName,
kept: 0.78, streak: 4, checkedInHoursAgo: 0));
(group['members'] as List).add(
_member(_me, _myName, kept: 0.78, streak: 4, checkedInHoursAgo: 0));
_groups.insert(0, group);
_post(group['id'], 'joined the group', 'System');
_queueReply(group['id']);
@@ -422,7 +450,7 @@ class MockGroups {
_post(
group['id'],
'logged $amount ${objective['unit']} on ${objective['title']}'
'${request.note.isEmpty ? "" : " — ${request.note}"}',
'${request.note.isEmpty ? "" : " — ${request.note}"}',
'CheckIn');
_queueReply(group['id']);
}
@@ -465,8 +493,8 @@ class MockGroups {
who = member['name'];
}
}
_post(request.groupId, 'nudged $who: today still counts, go get it',
'Nudge');
_post(
request.groupId, 'nudged $who: today still counts, go get it', 'Nudge');
return <String, dynamic>{'success': true};
}
}