feat(ratings): Wallapop-style ratings in chat and offer detail

- SocialSession grows a ratings transport (fifth interface on the one
  shared channel).
- PeerRatingCubit: count, locale-aware average, and circleCount — how
  many ratings come from your own circle (reuses the ego-centric trust
  rule), the signal that makes strangers' review-stuffing irrelevant.
- Chat: slim rating strip under the trust banner; you can rate only
  someone you've talked with (soft anchor until the signed bilateral
  exchange form lands). Sheet with 5 stars + optional comment,
  pre-filled for editing, with a take-back action.
- Offer detail: author's stars under their name, with 'N from people
  you know' when your circle rated them; nothing shown when unrated.
- i18n ratings.* (en/es/pt/ast); tests for cubit and sheet.
This commit is contained in:
vjrj 2026-07-11 13:13:43 +02:00
parent acdacf1821
commit 1926e3bd98
18 changed files with 873 additions and 12 deletions

View file

@ -78,6 +78,7 @@ class Translations with BaseTranslations<AppLocale, Translations> {
late final Translations$chat$en chat = Translations$chat$en.internal(_root);
late final Translations$trust$en trust = Translations$trust$en.internal(_root);
late final Translations$yourPeople$en yourPeople = Translations$yourPeople$en.internal(_root);
late final Translations$ratings$en ratings = Translations$ratings$en.internal(_root);
late final Translations$notifications$en notifications = Translations$notifications$en.internal(_root);
late final Translations$plantare$en plantare = Translations$plantare$en.internal(_root);
late final Translations$sale$en sale = Translations$sale$en.internal(_root);
@ -1505,6 +1506,33 @@ class Translations$yourPeople$en {
String get offline => 'You\'re offline — try again when you\'re connected';
}
// Path: ratings
class Translations$ratings$en {
Translations$ratings$en.internal(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Rate this person'
String get rate => 'Rate this person';
/// en: 'Edit your rating'
String get edit => 'Edit your rating';
/// en: 'How did it go? (optional)'
String get commentHint => 'How did it go? (optional)';
/// en: '{n} from people you know'
String fromYourCircle({required Object n}) => '${n} from people you know';
/// en: 'Remove your rating'
String get retract => 'Remove your rating';
/// en: 'Rating saved'
String get saved => 'Rating saved';
}
// Path: notifications
class Translations$notifications$en {
Translations$notifications$en.internal(this._root);
@ -2525,6 +2553,12 @@ extension on Translations {
'yourPeople.revoke' => 'Stop vouching',
'yourPeople.revokeConfirm' => 'Stop vouching for this person?',
'yourPeople.offline' => 'You\'re offline — try again when you\'re connected',
'ratings.rate' => 'Rate this person',
'ratings.edit' => 'Edit your rating',
'ratings.commentHint' => 'How did it go? (optional)',
'ratings.fromYourCircle' => ({required Object n}) => '${n} from people you know',
'ratings.retract' => 'Remove your rating',
'ratings.saved' => 'Rating saved',
'notifications.newMessageFrom' => ({required Object name}) => 'New message from ${name}',
'plantare.title' => 'Plantares',
'plantare.help' => 'A Plantare is a promise to reproduce a seed and return some — how a variety keeps travelling from hand to hand. It\'s not a sale.',