🐛 Fix poll
This commit is contained in:
parent
b83b0b5efb
commit
48b6d5f6c1
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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 {},
|
||||||
);
|
);
|
||||||
|
@ -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),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user