🐛 Fix poll

This commit is contained in:
LittleSheep 2025-02-15 00:16:06 +08:00
parent b83b0b5efb
commit 48b6d5f6c1
5 changed files with 54 additions and 36 deletions

View File

@ -5,7 +5,7 @@ meta {
} }
post { post {
url: {{endpoint}}/cgi/id/dev/notify/1 url: {{endpoint}}/cgi/id/dev/notify/122
body: json body: json
auth: inherit auth: inherit
} }
@ -15,12 +15,9 @@ body:json {
"client_id": "{{third_client_id}}", "client_id": "{{third_client_id}}",
"client_secret":"{{third_client_tk}}", "client_secret":"{{third_client_tk}}",
"type": "general", "type": "general",
"subject": "测试", "subject": "处理该帐号 @solian 的决定",
"subtitle": "Alphabot です", "subtitle": "违反用户协议",
"content": "全新通知动画", "content": "您的帐号违反了我们用户协议中关于冒充我们官方的行为,至此做出停权的决定。还请见谅。该决定是最终决定,不接受上诉。",
"metadata": {
"image": "D2EDbcrsTugs3xk5"
},
"priority": 10 "priority": 10
} }
} }

View File

@ -16,8 +16,7 @@ class SnPoll with _$SnPoll {
required SnPollMetric metric, required SnPollMetric metric,
}) = _SnPoll; }) = _SnPoll;
factory SnPoll.fromJson(Map<String, Object?> json) => factory SnPoll.fromJson(Map<String, Object?> json) => _$SnPollFromJson(json);
_$SnPollFromJson(json);
} }
@freezed @freezed
@ -25,11 +24,11 @@ class SnPollMetric with _$SnPollMetric {
const factory SnPollMetric({ const factory SnPollMetric({
required int totalAnswer, required int totalAnswer,
@Default({}) Map<String, int> byOptions, @Default({}) Map<String, int> byOptions,
@Default({}) Map<String, int> byOptionsPercentage, @Default({}) Map<String, double> byOptionsPercentage,
}) = _SnPollMetric; }) = _SnPollMetric;
factory SnPollMetric.fromJson(Map<String, Object?> json) factory SnPollMetric.fromJson(Map<String, Object?> json) =>
=> _$SnPollMetricFromJson(json); _$SnPollMetricFromJson(json);
} }
@freezed @freezed
@ -41,6 +40,6 @@ class SnPollOption with _$SnPollOption {
required String description, required String description,
}) = _SnPollOption; }) = _SnPollOption;
factory SnPollOption.fromJson(Map<String, Object?> json) factory SnPollOption.fromJson(Map<String, Object?> json) =>
=> _$SnPollOptionFromJson(json); _$SnPollOptionFromJson(json);
} }

View File

@ -345,7 +345,7 @@ SnPollMetric _$SnPollMetricFromJson(Map<String, dynamic> json) {
mixin _$SnPollMetric { mixin _$SnPollMetric {
int get totalAnswer => throw _privateConstructorUsedError; int get totalAnswer => throw _privateConstructorUsedError;
Map<String, int> get byOptions => throw _privateConstructorUsedError; Map<String, int> get byOptions => throw _privateConstructorUsedError;
Map<String, int> get byOptionsPercentage => Map<String, double> get byOptionsPercentage =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
/// Serializes this SnPollMetric to a JSON map. /// Serializes this SnPollMetric to a JSON map.
@ -367,7 +367,7 @@ abstract class $SnPollMetricCopyWith<$Res> {
$Res call( $Res call(
{int totalAnswer, {int totalAnswer,
Map<String, int> byOptions, Map<String, int> byOptions,
Map<String, int> byOptionsPercentage}); Map<String, double> byOptionsPercentage});
} }
/// @nodoc /// @nodoc
@ -401,7 +401,7 @@ class _$SnPollMetricCopyWithImpl<$Res, $Val extends SnPollMetric>
byOptionsPercentage: null == byOptionsPercentage byOptionsPercentage: null == byOptionsPercentage
? _value.byOptionsPercentage ? _value.byOptionsPercentage
: byOptionsPercentage // ignore: cast_nullable_to_non_nullable : byOptionsPercentage // ignore: cast_nullable_to_non_nullable
as Map<String, int>, as Map<String, double>,
) as $Val); ) as $Val);
} }
} }
@ -417,7 +417,7 @@ abstract class _$$SnPollMetricImplCopyWith<$Res>
$Res call( $Res call(
{int totalAnswer, {int totalAnswer,
Map<String, int> byOptions, Map<String, int> byOptions,
Map<String, int> byOptionsPercentage}); Map<String, double> byOptionsPercentage});
} }
/// @nodoc /// @nodoc
@ -449,7 +449,7 @@ class __$$SnPollMetricImplCopyWithImpl<$Res>
byOptionsPercentage: null == byOptionsPercentage byOptionsPercentage: null == byOptionsPercentage
? _value._byOptionsPercentage ? _value._byOptionsPercentage
: byOptionsPercentage // ignore: cast_nullable_to_non_nullable : byOptionsPercentage // ignore: cast_nullable_to_non_nullable
as Map<String, int>, as Map<String, double>,
)); ));
} }
} }
@ -460,7 +460,7 @@ class _$SnPollMetricImpl implements _SnPollMetric {
const _$SnPollMetricImpl( const _$SnPollMetricImpl(
{required this.totalAnswer, {required this.totalAnswer,
final Map<String, int> byOptions = const {}, final Map<String, int> byOptions = const {},
final Map<String, int> byOptionsPercentage = const {}}) final Map<String, double> byOptionsPercentage = const {}})
: _byOptions = byOptions, : _byOptions = byOptions,
_byOptionsPercentage = byOptionsPercentage; _byOptionsPercentage = byOptionsPercentage;
@ -478,10 +478,10 @@ class _$SnPollMetricImpl implements _SnPollMetric {
return EqualUnmodifiableMapView(_byOptions); return EqualUnmodifiableMapView(_byOptions);
} }
final Map<String, int> _byOptionsPercentage; final Map<String, double> _byOptionsPercentage;
@override @override
@JsonKey() @JsonKey()
Map<String, int> get byOptionsPercentage { Map<String, double> get byOptionsPercentage {
if (_byOptionsPercentage is EqualUnmodifiableMapView) if (_byOptionsPercentage is EqualUnmodifiableMapView)
return _byOptionsPercentage; return _byOptionsPercentage;
// ignore: implicit_dynamic_type // ignore: implicit_dynamic_type
@ -534,7 +534,7 @@ abstract class _SnPollMetric implements SnPollMetric {
const factory _SnPollMetric( const factory _SnPollMetric(
{required final int totalAnswer, {required final int totalAnswer,
final Map<String, int> byOptions, final Map<String, int> byOptions,
final Map<String, int> byOptionsPercentage}) = _$SnPollMetricImpl; final Map<String, double> byOptionsPercentage}) = _$SnPollMetricImpl;
factory _SnPollMetric.fromJson(Map<String, dynamic> json) = factory _SnPollMetric.fromJson(Map<String, dynamic> json) =
_$SnPollMetricImpl.fromJson; _$SnPollMetricImpl.fromJson;
@ -544,7 +544,7 @@ abstract class _SnPollMetric implements SnPollMetric {
@override @override
Map<String, int> get byOptions; Map<String, int> get byOptions;
@override @override
Map<String, int> get byOptionsPercentage; Map<String, double> get byOptionsPercentage;
/// Create a copy of SnPollMetric /// Create a copy of SnPollMetric
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.

View File

@ -40,7 +40,7 @@ _$SnPollMetricImpl _$$SnPollMetricImplFromJson(Map<String, dynamic> json) =>
const {}, const {},
byOptionsPercentage: byOptionsPercentage:
(json['by_options_percentage'] as Map<String, dynamic>?)?.map( (json['by_options_percentage'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toInt()), (k, e) => MapEntry(k, (e as num).toDouble()),
) ?? ) ??
const {}, const {},
); );

View File

@ -31,19 +31,27 @@ class _PostPollState extends State<PostPoll> {
String? _answeredChoice; String? _answeredChoice;
Future<void> _refreshPoll() async {
final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get('/cgi/co/polls/${widget.poll.id}');
if (!mounted) return;
setState(() => _poll = SnPoll.fromJson(resp.data!));
}
Future<void> _fetchAnswer() async { Future<void> _fetchAnswer() async {
final ua = context.read<UserProvider>(); final ua = context.read<UserProvider>();
if (!ua.isAuthorized) return; if (!ua.isAuthorized) return;
try { try {
setState(() => _isBusy = true); setState(() => _isBusy = true);
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get('/cgi/co/polls/${widget.poll.id}/answer'); final resp =
await sn.client.get('/cgi/co/polls/${widget.poll.id}/answer');
_answeredChoice = resp.data?['answer']; _answeredChoice = resp.data?['answer'];
if (!mounted) return; if (!mounted) return;
setState(() {}); setState(() {});
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;
context.showErrorDialog(err); // ignore because it may not found
} finally { } finally {
setState(() => _isBusy = false); setState(() => _isBusy = false);
} }
@ -59,8 +67,9 @@ class _PostPollState extends State<PostPoll> {
'answer': option.id, 'answer': option.id,
}); });
if (!mounted) return; if (!mounted) return;
context.showSnackbar('pollAnswered'.tr());
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
_answeredChoice = option.id;
_refreshPoll();
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;
context.showErrorDialog(err); context.showErrorDialog(err);
@ -78,15 +87,24 @@ class _PostPollState extends State<PostPoll> {
for (final option in _poll.options) for (final option in _poll.options)
Stack( Stack(
children: [ children: [
Container( ClipRRect(
height: 60, borderRadius: const BorderRadius.all(Radius.circular(8)),
width: MediaQuery.of(context).size.width * (_poll.metric.byOptionsPercentage[option.id] ?? 0).toDouble(), child: Container(
color: Theme.of(context).colorScheme.surfaceContainerHigh, height: 60,
width: MediaQuery.of(context).size.width *
(_poll.metric.byOptionsPercentage[option.id] ?? 0)
.toDouble(),
color: Theme.of(context).colorScheme.surfaceContainerHigh,
),
), ),
ListTile( ListTile(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
minTileHeight: 60, minTileHeight: 60,
leading: _answeredChoice == option.id ? const Icon(Symbols.circle, fill: 1) : const Icon(Symbols.circle), leading: _answeredChoice == option.id
? const Icon(Symbols.circle, fill: 1)
: const Icon(Symbols.circle),
title: Text(option.name), title: Text(option.name),
subtitle: Column( subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -95,14 +113,18 @@ class _PostPollState extends State<PostPoll> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text('pollVotes'.plural(_poll.metric.byOptions[option.id] ?? 0)), Text(
'pollVotes'
.plural(_poll.metric.byOptions[option.id] ?? 0),
),
Text(' · ').padding(horizontal: 4), Text(' · ').padding(horizontal: 4),
Text( Text(
'${((_poll.metric.byOptionsPercentage[option.id] ?? 0).toDouble() * 100).toStringAsFixed(2)}%', '${((_poll.metric.byOptionsPercentage[option.id] ?? 0).toDouble() * 100).toStringAsFixed(2)}%',
), ),
], ],
), ),
if (option.description.isNotEmpty) Text(option.description), if (option.description.isNotEmpty)
Text(option.description),
], ],
), ),
onTap: _isBusy ? null : () => _voteForOption(option), onTap: _isBusy ? null : () => _voteForOption(option),