From 81f65750b7db28c49c0706833f191663510f4389 Mon Sep 17 00:00:00 2001 From: lars Date: Fri, 10 Oct 2025 02:01:43 +0200 Subject: [PATCH 01/10] added functionality to show own votes in polls --- .../talk/getPoll/getPollState.dart | 22 +++++++ .../talk/getPoll/getPollStateResponse.dart | 50 ++++++++++++++++ .../talk/getPoll/getPollStateResponse.g.dart | 60 +++++++++++++++++++ .../pages/talk/components/chatBubble.dart | 40 +++++++++++++ .../pages/talk/components/chatMessage.dart | 8 +++ .../talk/components/pollOptionsList.dart | 47 +++++++++++++++ 6 files changed, 227 insertions(+) create mode 100644 lib/api/marianumcloud/talk/getPoll/getPollState.dart create mode 100644 lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart create mode 100644 lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart create mode 100644 lib/view/pages/talk/components/pollOptionsList.dart diff --git a/lib/api/marianumcloud/talk/getPoll/getPollState.dart b/lib/api/marianumcloud/talk/getPoll/getPollState.dart new file mode 100644 index 0000000..e7950c8 --- /dev/null +++ b/lib/api/marianumcloud/talk/getPoll/getPollState.dart @@ -0,0 +1,22 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:http/http.dart' as http; + +import '../talkApi.dart'; +import 'getPollStateResponse.dart'; + +class GetPollState extends TalkApi { + String token; + int pollId; + GetPollState({required this.token, required this.pollId}) : super('v1/poll/$token/$pollId', null); + + @override + GetPollStateResponse assemble(String raw) { + log(raw); + return GetPollStateResponse.fromJson(jsonDecode(raw)['ocs']); + } + + @override + Future request(Uri uri, Object? body, Map? headers) => http.get(uri, headers: headers); +} diff --git a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart new file mode 100644 index 0000000..6678803 --- /dev/null +++ b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart @@ -0,0 +1,50 @@ +import 'package:json_annotation/json_annotation.dart'; + +import '../../../apiResponse.dart'; + +part 'getPollStateResponse.g.dart'; + +@JsonSerializable(explicitToJson: true) +class GetPollStateResponse extends ApiResponse { + GetPollStateResponseObject data; + + GetPollStateResponse(this.data); + + factory GetPollStateResponse.fromJson(Map json) => _$GetPollStateResponseFromJson(json); + Map toJson() => _$GetPollStateResponseToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class GetPollStateResponseObject { + int id; + String question; + List options; + dynamic votes; + String actorType; + String actorId; + String actorDisplayName; + int status; + int resultMode; + int maxVotes; + List votedSelf; + int? numVoters; + List? details; + + GetPollStateResponseObject( + this.id, + this.question, + this.options, + this.votes, + this.actorType, + this.actorId, + this.actorDisplayName, + this.status, + this.resultMode, + this.maxVotes, + this.votedSelf, + this.numVoters, + this.details); + + factory GetPollStateResponseObject.fromJson(Map json) => _$GetPollStateResponseObjectFromJson(json); + Map toJson() => _$GetPollStateResponseObjectToJson(this); +} diff --git a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart new file mode 100644 index 0000000..f54d062 --- /dev/null +++ b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart @@ -0,0 +1,60 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'getPollStateResponse.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetPollStateResponse _$GetPollStateResponseFromJson( + Map json) => + GetPollStateResponse( + GetPollStateResponseObject.fromJson(json['data'] as Map), + )..headers = (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ); + +Map _$GetPollStateResponseToJson( + GetPollStateResponse instance) => + { + if (instance.headers case final value?) 'headers': value, + 'data': instance.data.toJson(), + }; + +GetPollStateResponseObject _$GetPollStateResponseObjectFromJson( + Map json) => + GetPollStateResponseObject( + (json['id'] as num).toInt(), + json['question'] as String, + (json['options'] as List).map((e) => e as String).toList(), + json['votes'], + json['actorType'] as String, + json['actorId'] as String, + json['actorDisplayName'] as String, + (json['status'] as num).toInt(), + (json['resultMode'] as num).toInt(), + (json['maxVotes'] as num).toInt(), + (json['votedSelf'] as List) + .map((e) => (e as num).toInt()) + .toList(), + (json['numVoters'] as num?)?.toInt(), + (json['details'] as List?)?.map((e) => e as String).toList(), + ); + +Map _$GetPollStateResponseObjectToJson( + GetPollStateResponseObject instance) => + { + 'id': instance.id, + 'question': instance.question, + 'options': instance.options, + 'votes': instance.votes, + 'actorType': instance.actorType, + 'actorId': instance.actorId, + 'actorDisplayName': instance.actorDisplayName, + 'status': instance.status, + 'resultMode': instance.resultMode, + 'maxVotes': instance.maxVotes, + 'votedSelf': instance.votedSelf, + 'numVoters': instance.numVoters, + 'details': instance.details, + }; diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index fdf5239..9d1dcf6 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:bubble/bubble.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart' as emojis; import 'package:flowder/flowder.dart'; @@ -6,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:jiffy/jiffy.dart'; import 'package:open_filex/open_filex.dart'; +import '../../../../api/marianumcloud/talk/getPoll/getPollState.dart'; import '../../../../extensions/text.dart'; import 'package:provider/provider.dart'; @@ -18,11 +21,13 @@ import '../../../../api/marianumcloud/talk/reactMessage/reactMessageParams.dart' import '../../../../api/marianumcloud/talk/room/getRoomResponse.dart'; import '../../../../model/chatList/chatProps.dart'; import '../../../../widget/debug/debugTile.dart'; +import '../../../../widget/loadingSpinner.dart'; import '../../files/fileElement.dart'; import 'answerReference.dart'; import 'chatBubbleStyles.dart'; import 'chatMessage.dart'; import '../messageReactions.dart'; +import 'pollOptionsList.dart'; class ChatBubble extends StatefulWidget { final BuildContext context; @@ -297,6 +302,41 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM onLongPress: showOptionsDialog, onDoubleTap: showOptionsDialog, onTap: () { + if(message.originalData?['object']?.type == RichObjectStringObjectType.talkPoll) { + log(message.originalData!['object']!.id); + var pollState = GetPollState(token: widget.bubbleData.token, pollId: int.parse(message.originalData!['object']!.id)).run(); + List? ownVotes; + showDialog(context: context, builder: (context) => AlertDialog( + title: Text(message.originalData!['object']!.name, textScaler: TextScaler.linear(0.9)), + content: FutureBuilder( + future: pollState, + builder: (context, snapshot) { + if(snapshot.connectionState == ConnectionState.waiting) return const LoadingSpinner(); + + var pollData = snapshot.data!.data; + ownVotes = pollData.votedSelf; + return PollOptionsList( + pollData: pollData, + callback: (votes) => ownVotes = votes + ); + } + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + if(ownVotes == null) return; + }, + child: const Text('Stimme abgeben') + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Abbrechen') + ), + ], + )); + } + if(message.file == null) return; if(downloadProgress > 0) { diff --git a/lib/view/pages/talk/components/chatMessage.dart b/lib/view/pages/talk/components/chatMessage.dart index 90abb14..d969039 100644 --- a/lib/view/pages/talk/components/chatMessage.dart +++ b/lib/view/pages/talk/components/chatMessage.dart @@ -32,6 +32,14 @@ class ChatMessage { onOpen: onOpen, ); + if(originalData?['object']?.type == RichObjectStringObjectType.talkPoll) { + return ListTile( + leading: const Icon(Icons.poll_outlined), + title: Text(originalData!['object']!.name), + contentPadding: const EdgeInsets.only(left: 10), + ); + } + if(file == null) return contentWidget; return Padding( diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart new file mode 100644 index 0000000..78175f5 --- /dev/null +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -0,0 +1,47 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../../../../api/marianumcloud/talk/getPoll/getPollStateResponse.dart'; + +class PollOptionsList extends StatefulWidget { + final GetPollStateResponseObject pollData; + final Function(List) callback; + const PollOptionsList({super.key, required this.pollData, required this.callback}); + + @override + State createState() => _PollOptionsListState(); +} + +class _PollOptionsListState extends State { + late List ownVotes; + + @override + void initState() { + super.initState(); + ownVotes = widget.pollData.votedSelf; + } + + @override + Widget build(BuildContext context) => ListView( + shrinkWrap: true, + children: [ + ...widget.pollData.options.map( + (option) => CheckboxListTile( + value: ownVotes.contains(widget.pollData.options.indexOf(option)), + title: Text(option), + onChanged: (value) { + var optionId = widget.pollData.options.indexOf(option); + setState(() { + if(ownVotes.contains(optionId)) { + ownVotes.remove(optionId); + } else { + ownVotes.add(optionId); + } + }); + widget.callback(ownVotes); + } + ) + ) + ], + ); +} From b68bec9ebdcae243962f66a0a7a8ef21c80e9a4b Mon Sep 17 00:00:00 2001 From: lars Date: Fri, 10 Oct 2025 11:39:57 +0200 Subject: [PATCH 02/10] WIP: add option to vote on polls --- .../talk/getPoll/getPollState.dart | 6 +--- .../marianumcloud/talk/votePoll/votePoll.dart | 30 +++++++++++++++++++ .../talk/votePoll/votePollParams.dart | 14 +++++++++ .../talk/votePoll/votePollParams.g.dart | 19 ++++++++++++ .../pages/talk/components/chatBubble.dart | 11 +++---- 5 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 lib/api/marianumcloud/talk/votePoll/votePoll.dart create mode 100644 lib/api/marianumcloud/talk/votePoll/votePollParams.dart create mode 100644 lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart diff --git a/lib/api/marianumcloud/talk/getPoll/getPollState.dart b/lib/api/marianumcloud/talk/getPoll/getPollState.dart index e7950c8..503c1d0 100644 --- a/lib/api/marianumcloud/talk/getPoll/getPollState.dart +++ b/lib/api/marianumcloud/talk/getPoll/getPollState.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:developer'; import 'package:http/http.dart' as http; @@ -12,10 +11,7 @@ class GetPollState extends TalkApi { GetPollState({required this.token, required this.pollId}) : super('v1/poll/$token/$pollId', null); @override - GetPollStateResponse assemble(String raw) { - log(raw); - return GetPollStateResponse.fromJson(jsonDecode(raw)['ocs']); - } + GetPollStateResponse assemble(String raw) => GetPollStateResponse.fromJson(jsonDecode(raw)['ocs']); @override Future request(Uri uri, Object? body, Map? headers) => http.get(uri, headers: headers); diff --git a/lib/api/marianumcloud/talk/votePoll/votePoll.dart b/lib/api/marianumcloud/talk/votePoll/votePoll.dart new file mode 100644 index 0000000..076b49f --- /dev/null +++ b/lib/api/marianumcloud/talk/votePoll/votePoll.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:http/http.dart' as http; +import 'package:http/http.dart'; + +import '../getPoll/getPollStateResponse.dart'; +import '../talkApi.dart'; +import 'votePollParams.dart'; + +class VotePoll extends TalkApi { + String token; + int pollId; + VotePoll({required this.token, required this.pollId, required VotePollParams params}) : super('v1/poll/$token/$pollId', params); + + @override + GetPollStateResponse assemble(String raw) { + log(raw); + return GetPollStateResponse.fromJson(jsonDecode(raw)['ocs']); + } + + @override + Future? request(Uri uri, Object? body, Map? headers) { + if(body is VotePollParams) { + log(body.toJson().toString()); + return http.post(uri, headers: headers, body: body.toJson().toString()); + } + return null; + } +} diff --git a/lib/api/marianumcloud/talk/votePoll/votePollParams.dart b/lib/api/marianumcloud/talk/votePoll/votePollParams.dart new file mode 100644 index 0000000..c8a7bca --- /dev/null +++ b/lib/api/marianumcloud/talk/votePoll/votePollParams.dart @@ -0,0 +1,14 @@ +import 'package:json_annotation/json_annotation.dart'; + +import '../../../apiParams.dart'; + +part 'votePollParams.g.dart'; + +@JsonSerializable() +class VotePollParams extends ApiParams { + List optionIds; + + VotePollParams({required this.optionIds}); + factory VotePollParams.fromJson(Map json) => _$VotePollParamsFromJson(json); + Map toJson() => _$VotePollParamsToJson(this); +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart b/lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart new file mode 100644 index 0000000..cb7d6f0 --- /dev/null +++ b/lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'votePollParams.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +VotePollParams _$VotePollParamsFromJson(Map json) => + VotePollParams( + optionIds: (json['optionIds'] as List) + .map((e) => (e as num).toInt()) + .toList(), + ); + +Map _$VotePollParamsToJson(VotePollParams instance) => + { + 'optionIds': instance.optionIds, + }; diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index 9d1dcf6..e45a5bf 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:bubble/bubble.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart' as emojis; import 'package:flowder/flowder.dart'; @@ -9,6 +7,8 @@ import 'package:flutter/services.dart'; import 'package:jiffy/jiffy.dart'; import 'package:open_filex/open_filex.dart'; import '../../../../api/marianumcloud/talk/getPoll/getPollState.dart'; +import '../../../../api/marianumcloud/talk/votePoll/votePoll.dart'; +import '../../../../api/marianumcloud/talk/votePoll/votePollParams.dart'; import '../../../../extensions/text.dart'; import 'package:provider/provider.dart'; @@ -303,8 +303,8 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM onDoubleTap: showOptionsDialog, onTap: () { if(message.originalData?['object']?.type == RichObjectStringObjectType.talkPoll) { - log(message.originalData!['object']!.id); - var pollState = GetPollState(token: widget.bubbleData.token, pollId: int.parse(message.originalData!['object']!.id)).run(); + var pollId = int.parse(message.originalData!['object']!.id); + var pollState = GetPollState(token: widget.bubbleData.token, pollId: pollId).run(); List? ownVotes; showDialog(context: context, builder: (context) => AlertDialog( title: Text(message.originalData!['object']!.name, textScaler: TextScaler.linear(0.9)), @@ -323,9 +323,10 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM ), actions: [ TextButton( - onPressed: () { + onPressed: () async { Navigator.of(context).pop(); if(ownVotes == null) return; + VotePoll(pollId: pollId, token: widget.bubbleData.token, params: VotePollParams(optionIds: ownVotes!)).run(); }, child: const Text('Stimme abgeben') ), From bfa0b0f5c0e1172c3cf6aed906585b1c2345bee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sun, 18 Jan 2026 10:28:17 +0100 Subject: [PATCH 03/10] feat: add devtools extensions and fix poll dialog UI/UX - Enabled `provider` and `shared_preferences` extensions in `devtools_options.yaml`. - Added logging for message object data on chat bubble tap. - Fixed layout issues in poll dialog by wrapping `LoadingSpinner` in a `Column` and changing `ListView` to a `Column` in `pollOptionsList.dart`. - Updated poll submission button to wait for the poll state to load before allowing interaction. --- devtools_options.yaml | 2 ++ .../pages/talk/components/chatBubble.dart | 19 ++++++++++--------- .../talk/components/pollOptionsList.dart | 7 +++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/devtools_options.yaml b/devtools_options.yaml index 7e7e7f6..7093540 100644 --- a/devtools_options.yaml +++ b/devtools_options.yaml @@ -1 +1,3 @@ extensions: + - provider: true + - shared_preferences: true \ No newline at end of file diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index e45a5bf..2fd7ef9 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -311,7 +311,7 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM content: FutureBuilder( future: pollState, builder: (context, snapshot) { - if(snapshot.connectionState == ConnectionState.waiting) return const LoadingSpinner(); + if(snapshot.connectionState == ConnectionState.waiting) return const Column(mainAxisSize: MainAxisSize.min, children: [LoadingSpinner()]); var pollData = snapshot.data!.data; ownVotes = pollData.votedSelf; @@ -322,14 +322,15 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM } ), actions: [ - TextButton( - onPressed: () async { - Navigator.of(context).pop(); - if(ownVotes == null) return; - VotePoll(pollId: pollId, token: widget.bubbleData.token, params: VotePollParams(optionIds: ownVotes!)).run(); - }, - child: const Text('Stimme abgeben') - ), + FutureBuilder(future: pollState, builder: (context, snapshot) => TextButton( + onPressed: () async { + if(snapshot.connectionState != ConnectionState.done) return; + Navigator.of(context).pop(); + if(ownVotes == null) return; + VotePoll(pollId: pollId, token: widget.bubbleData.token, params: VotePollParams(optionIds: ownVotes!)).run(); + }, + child: const Text('Stimme abgeben') + )), TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Abbrechen') diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart index 78175f5..db26406 100644 --- a/lib/view/pages/talk/components/pollOptionsList.dart +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -22,11 +22,10 @@ class _PollOptionsListState extends State { } @override - Widget build(BuildContext context) => ListView( - shrinkWrap: true, + Widget build(BuildContext context) => Column( + mainAxisSize: MainAxisSize.min, children: [ - ...widget.pollData.options.map( - (option) => CheckboxListTile( + ...widget.pollData.options.map((option) => CheckboxListTile( value: ownVotes.contains(widget.pollData.options.indexOf(option)), title: Text(option), onChanged: (value) { From 3469d02033b80c9ff6c5b514d6525ff82c914a39 Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 1 Feb 2026 03:23:36 +0100 Subject: [PATCH 04/10] changed poll dialog to only show results --- .../talk/getPoll/getPollStateResponse.g.dart | 86 ++++++++++--------- .../marianumcloud/talk/votePoll/votePoll.dart | 1 + .../talk/votePoll/votePollParams.g.dart | 4 +- .../pages/talk/components/chatBubble.dart | 25 ++---- .../talk/components/pollOptionsList.dart | 74 ++++++++++------ 5 files changed, 103 insertions(+), 87 deletions(-) diff --git a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart index f54d062..9d89f4c 100644 --- a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart +++ b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart @@ -7,54 +7,56 @@ part of 'getPollStateResponse.dart'; // ************************************************************************** GetPollStateResponse _$GetPollStateResponseFromJson( - Map json) => + Map json, +) => GetPollStateResponse( - GetPollStateResponseObject.fromJson(json['data'] as Map), - )..headers = (json['headers'] as Map?)?.map( + GetPollStateResponseObject.fromJson( + json['data'] as Map, + ), + ) + ..headers = (json['headers'] as Map?)?.map( (k, e) => MapEntry(k, e as String), ); Map _$GetPollStateResponseToJson( - GetPollStateResponse instance) => - { - if (instance.headers case final value?) 'headers': value, - 'data': instance.data.toJson(), - }; + GetPollStateResponse instance, +) => { + 'headers': ?instance.headers, + 'data': instance.data.toJson(), +}; GetPollStateResponseObject _$GetPollStateResponseObjectFromJson( - Map json) => - GetPollStateResponseObject( - (json['id'] as num).toInt(), - json['question'] as String, - (json['options'] as List).map((e) => e as String).toList(), - json['votes'], - json['actorType'] as String, - json['actorId'] as String, - json['actorDisplayName'] as String, - (json['status'] as num).toInt(), - (json['resultMode'] as num).toInt(), - (json['maxVotes'] as num).toInt(), - (json['votedSelf'] as List) - .map((e) => (e as num).toInt()) - .toList(), - (json['numVoters'] as num?)?.toInt(), - (json['details'] as List?)?.map((e) => e as String).toList(), - ); + Map json, +) => GetPollStateResponseObject( + (json['id'] as num).toInt(), + json['question'] as String, + (json['options'] as List).map((e) => e as String).toList(), + json['votes'], + json['actorType'] as String, + json['actorId'] as String, + json['actorDisplayName'] as String, + (json['status'] as num).toInt(), + (json['resultMode'] as num).toInt(), + (json['maxVotes'] as num).toInt(), + (json['votedSelf'] as List).map((e) => (e as num).toInt()).toList(), + (json['numVoters'] as num?)?.toInt(), + (json['details'] as List?)?.map((e) => e as String).toList(), +); Map _$GetPollStateResponseObjectToJson( - GetPollStateResponseObject instance) => - { - 'id': instance.id, - 'question': instance.question, - 'options': instance.options, - 'votes': instance.votes, - 'actorType': instance.actorType, - 'actorId': instance.actorId, - 'actorDisplayName': instance.actorDisplayName, - 'status': instance.status, - 'resultMode': instance.resultMode, - 'maxVotes': instance.maxVotes, - 'votedSelf': instance.votedSelf, - 'numVoters': instance.numVoters, - 'details': instance.details, - }; + GetPollStateResponseObject instance, +) => { + 'id': instance.id, + 'question': instance.question, + 'options': instance.options, + 'votes': instance.votes, + 'actorType': instance.actorType, + 'actorId': instance.actorId, + 'actorDisplayName': instance.actorDisplayName, + 'status': instance.status, + 'resultMode': instance.resultMode, + 'maxVotes': instance.maxVotes, + 'votedSelf': instance.votedSelf, + 'numVoters': instance.numVoters, + 'details': instance.details, +}; diff --git a/lib/api/marianumcloud/talk/votePoll/votePoll.dart b/lib/api/marianumcloud/talk/votePoll/votePoll.dart index 076b49f..a0880bd 100644 --- a/lib/api/marianumcloud/talk/votePoll/votePoll.dart +++ b/lib/api/marianumcloud/talk/votePoll/votePoll.dart @@ -8,6 +8,7 @@ import '../getPoll/getPollStateResponse.dart'; import '../talkApi.dart'; import 'votePollParams.dart'; +@Deprecated('VotePoll is broken (known issues)') class VotePoll extends TalkApi { String token; int pollId; diff --git a/lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart b/lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart index cb7d6f0..5b43858 100644 --- a/lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart +++ b/lib/api/marianumcloud/talk/votePoll/votePollParams.g.dart @@ -14,6 +14,4 @@ VotePollParams _$VotePollParamsFromJson(Map json) => ); Map _$VotePollParamsToJson(VotePollParams instance) => - { - 'optionIds': instance.optionIds, - }; + {'optionIds': instance.optionIds}; diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index 2fd7ef9..5ce3b97 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -7,8 +7,6 @@ import 'package:flutter/services.dart'; import 'package:jiffy/jiffy.dart'; import 'package:open_filex/open_filex.dart'; import '../../../../api/marianumcloud/talk/getPoll/getPollState.dart'; -import '../../../../api/marianumcloud/talk/votePoll/votePoll.dart'; -import '../../../../api/marianumcloud/talk/votePoll/votePollParams.dart'; import '../../../../extensions/text.dart'; import 'package:provider/provider.dart'; @@ -305,7 +303,6 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM if(message.originalData?['object']?.type == RichObjectStringObjectType.talkPoll) { var pollId = int.parse(message.originalData!['object']!.id); var pollState = GetPollState(token: widget.bubbleData.token, pollId: pollId).run(); - List? ownVotes; showDialog(context: context, builder: (context) => AlertDialog( title: Text(message.originalData!['object']!.name, textScaler: TextScaler.linear(0.9)), content: FutureBuilder( @@ -314,26 +311,20 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM if(snapshot.connectionState == ConnectionState.waiting) return const Column(mainAxisSize: MainAxisSize.min, children: [LoadingSpinner()]); var pollData = snapshot.data!.data; - ownVotes = pollData.votedSelf; - return PollOptionsList( - pollData: pollData, - callback: (votes) => ownVotes = votes + return ListView( + shrinkWrap: true, + children: [ + PollOptionsList( + pollData: pollData + ) + ] ); } ), actions: [ - FutureBuilder(future: pollState, builder: (context, snapshot) => TextButton( - onPressed: () async { - if(snapshot.connectionState != ConnectionState.done) return; - Navigator.of(context).pop(); - if(ownVotes == null) return; - VotePoll(pollId: pollId, token: widget.bubbleData.token, params: VotePollParams(optionIds: ownVotes!)).run(); - }, - child: const Text('Stimme abgeben') - )), TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text('Abbrechen') + child: const Text('Zurück') ), ], )); diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart index db26406..b3d2426 100644 --- a/lib/view/pages/talk/components/pollOptionsList.dart +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -1,45 +1,69 @@ + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_linkify/flutter_linkify.dart'; import '../../../../api/marianumcloud/talk/getPoll/getPollStateResponse.dart'; class PollOptionsList extends StatefulWidget { final GetPollStateResponseObject pollData; - final Function(List) callback; - const PollOptionsList({super.key, required this.pollData, required this.callback}); + const PollOptionsList({super.key, required this.pollData}); @override State createState() => _PollOptionsListState(); } class _PollOptionsListState extends State { - late List ownVotes; - - @override - void initState() { - super.initState(); - ownVotes = widget.pollData.votedSelf; - } - @override Widget build(BuildContext context) => Column( mainAxisSize: MainAxisSize.min, children: [ - ...widget.pollData.options.map((option) => CheckboxListTile( - value: ownVotes.contains(widget.pollData.options.indexOf(option)), - title: Text(option), - onChanged: (value) { - var optionId = widget.pollData.options.indexOf(option); - setState(() { - if(ownVotes.contains(optionId)) { - ownVotes.remove(optionId); - } else { - ownVotes.add(optionId); - } - }); - widget.callback(ownVotes); - } - ) + ...widget.pollData.options.map((option) { + var optionId = widget.pollData.options.indexOf(option); + var votedSelf = widget.pollData.votedSelf.contains(optionId); + var votes = widget.pollData.votes.runtimeType is Map + ? widget.pollData.votes['option-$optionId'] + : 0; + int numVoters = 1; + if(widget.pollData.numVoters != null && widget.pollData.numVoters != 0) { + numVoters = widget.pollData.numVoters!; + } + var portion = (votes / numVoters); + return ListTile( + enabled: false, + isThreeLine: true, + title: Text( + option, + style: Theme.of(context).textTheme.bodyLarge, + ), + trailing: Icon( + votedSelf ? Icons.check_circle_outlined : Icons.circle_outlined, + color: votedSelf + ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.8) + : Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.8), + ), + subtitle: Visibility( + visible: widget.pollData.numVoters != null, + child: Row( + children: [ + Expanded( + child: LinearProgressIndicator(value: portion), + ), + Container( + margin: const EdgeInsets.only(left: 10), + child: Text('${(portion * 100).round()}%'), + ), + ], + ) + ) + ); + }), + ListTile( + title: Linkify( + text: 'Zurzeit kann in dieser App leider nicht an Abstimmungen teilgenommen werden. ' + 'Um abzustimmen verwende die Webversion unter https://cloud.marianum-fulda.de', + style: Theme.of(context).textTheme.bodySmall, + ), ) ], ); From 541d6ef16481c0b72e8f2839661867db5511bc92 Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 1 Feb 2026 13:32:18 +0100 Subject: [PATCH 05/10] fixed issues with null values in votes map --- .../talk/components/pollOptionsList.dart | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart index b3d2426..5fdb698 100644 --- a/lib/view/pages/talk/components/pollOptionsList.dart +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -1,5 +1,4 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; @@ -16,22 +15,20 @@ class PollOptionsList extends StatefulWidget { class _PollOptionsListState extends State { @override Widget build(BuildContext context) => Column( - mainAxisSize: MainAxisSize.min, + mainAxisSize: MainAxisSize.min, children: [ ...widget.pollData.options.map((option) { var optionId = widget.pollData.options.indexOf(option); var votedSelf = widget.pollData.votedSelf.contains(optionId); - var votes = widget.pollData.votes.runtimeType is Map - ? widget.pollData.votes['option-$optionId'] - : 0; - int numVoters = 1; - if(widget.pollData.numVoters != null && widget.pollData.numVoters != 0) { - numVoters = widget.pollData.numVoters!; - } - var portion = (votes / numVoters); + var portionsVisible = widget.pollData.votes is Map; + var votes = !portionsVisible ? 0 + : (widget.pollData.votes['option-$optionId'] as num?) ?? 0; + var numVoters = widget.pollData.numVoters ?? 0; + double portion = numVoters == 0 ? 0 : (votes / numVoters); + return ListTile( enabled: false, - isThreeLine: true, + isThreeLine: portionsVisible, title: Text( option, style: Theme.of(context).textTheme.bodyLarge, @@ -43,11 +40,11 @@ class _PollOptionsListState extends State { : Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.8), ), subtitle: Visibility( - visible: widget.pollData.numVoters != null, + visible: portionsVisible, child: Row( children: [ Expanded( - child: LinearProgressIndicator(value: portion), + child: LinearProgressIndicator(value: portion.clamp(0.0, 1.0)), ), Container( margin: const EdgeInsets.only(left: 10), @@ -60,8 +57,8 @@ class _PollOptionsListState extends State { }), ListTile( title: Linkify( - text: 'Zurzeit kann in dieser App leider nicht an Abstimmungen teilgenommen werden. ' - 'Um abzustimmen verwende die Webversion unter https://cloud.marianum-fulda.de', + text: 'Zurzeit kann in dieser App leider nicht abgestimmt werden. ' + 'Verwende dafür die Webversion unter https://cloud.marianum-fulda.de', style: Theme.of(context).textTheme.bodySmall, ), ) From 567184bcf92960da5e09302dc9a94ce320a67aad Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 1 Feb 2026 13:56:39 +0100 Subject: [PATCH 06/10] filtered system messages for poll votes --- lib/view/pages/talk/chatView.dart | 3 ++- .../pages/talk/components/chatBubble.dart | 26 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/view/pages/talk/chatView.dart b/lib/view/pages/talk/chatView.dart index 34577bb..4d6ca48 100644 --- a/lib/view/pages/talk/chatView.dart +++ b/lib/view/pages/talk/chatView.dart @@ -51,6 +51,7 @@ class _ChatViewState extends State { var elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000); if(element.systemMessage.contains('reaction')) return; + if(element.systemMessage.contains('poll_voted')) return; var commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? '0'); if(!elementDate.isSameDay(lastDate)) { @@ -128,7 +129,7 @@ class _ChatViewState extends State { ), ), Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: TalkNavigator.isSecondaryVisible(context) ? ChatTextfield(widget.room.token, selfId: widget.selfId) : SafeArea(child: ChatTextfield(widget.room.token, selfId: widget.selfId) diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index 5ce3b97..a6a1f2b 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -306,20 +306,20 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM showDialog(context: context, builder: (context) => AlertDialog( title: Text(message.originalData!['object']!.name, textScaler: TextScaler.linear(0.9)), content: FutureBuilder( - future: pollState, - builder: (context, snapshot) { - if(snapshot.connectionState == ConnectionState.waiting) return const Column(mainAxisSize: MainAxisSize.min, children: [LoadingSpinner()]); + future: pollState, + builder: (context, snapshot) { + if(snapshot.connectionState == ConnectionState.waiting) return const Column(mainAxisSize: MainAxisSize.min, children: [LoadingSpinner()]); - var pollData = snapshot.data!.data; - return ListView( - shrinkWrap: true, - children: [ - PollOptionsList( - pollData: pollData - ) - ] - ); - } + var pollData = snapshot.data!.data; + return ListView( + shrinkWrap: true, + children: [ + PollOptionsList( + pollData: pollData + ) + ] + ); + } ), actions: [ TextButton( From 174e6ac0b7cc504fe31dc1b8ffdb4a5e7c0301ac Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 1 Feb 2026 15:07:48 +0100 Subject: [PATCH 07/10] fixed finished polls causing errors, made poll list dense --- .../talk/getPoll/getPollStateResponse.dart | 2 +- .../talk/getPoll/getPollStateResponse.g.dart | 2 +- .../pages/talk/components/pollOptionsList.dart | 16 ++++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart index 6678803..75d20c0 100644 --- a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart +++ b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.dart @@ -28,7 +28,7 @@ class GetPollStateResponseObject { int maxVotes; List votedSelf; int? numVoters; - List? details; + List? details; GetPollStateResponseObject( this.id, diff --git a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart index 9d89f4c..3015979 100644 --- a/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart +++ b/lib/api/marianumcloud/talk/getPoll/getPollStateResponse.g.dart @@ -40,7 +40,7 @@ GetPollStateResponseObject _$GetPollStateResponseObjectFromJson( (json['maxVotes'] as num).toInt(), (json['votedSelf'] as List).map((e) => (e as num).toInt()).toList(), (json['numVoters'] as num?)?.toInt(), - (json['details'] as List?)?.map((e) => e as String).toList(), + json['details'] as List?, ); Map _$GetPollStateResponseObjectToJson( diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart index 5fdb698..6a9b63d 100644 --- a/lib/view/pages/talk/components/pollOptionsList.dart +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -15,7 +15,6 @@ class PollOptionsList extends StatefulWidget { class _PollOptionsListState extends State { @override Widget build(BuildContext context) => Column( - mainAxisSize: MainAxisSize.min, children: [ ...widget.pollData.options.map((option) { var optionId = widget.pollData.options.indexOf(option); @@ -29,6 +28,7 @@ class _PollOptionsListState extends State { return ListTile( enabled: false, isThreeLine: portionsVisible, + dense: true, title: Text( option, style: Theme.of(context).textTheme.bodyLarge, @@ -36,12 +36,10 @@ class _PollOptionsListState extends State { trailing: Icon( votedSelf ? Icons.check_circle_outlined : Icons.circle_outlined, color: votedSelf - ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.8) - : Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.8), + ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.6) + : Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.6), ), - subtitle: Visibility( - visible: portionsVisible, - child: Row( + subtitle: portionsVisible ? Row( children: [ Expanded( child: LinearProgressIndicator(value: portion.clamp(0.0, 1.0)), @@ -51,14 +49,12 @@ class _PollOptionsListState extends State { child: Text('${(portion * 100).round()}%'), ), ], - ) - ) + ) : null, ); }), ListTile( title: Linkify( - text: 'Zurzeit kann in dieser App leider nicht abgestimmt werden. ' - 'Verwende dafür die Webversion unter https://cloud.marianum-fulda.de', + text: 'Wenn du abstimmen möchtest, verwende die Webversion unter https://cloud.marianum-fulda.de', style: Theme.of(context).textTheme.bodySmall, ), ) From 92a9a7358ede1fab5e2ba1194b1864447648c4e4 Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 1 Feb 2026 15:20:01 +0100 Subject: [PATCH 08/10] changed link to directly open the chat --- lib/utils/UrlOpener.dart | 10 ++++++++++ lib/view/pages/talk/components/chatBubble.dart | 3 ++- lib/view/pages/talk/components/chatMessage.dart | 10 ++-------- lib/view/pages/talk/components/pollOptionsList.dart | 7 +++++-- 4 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 lib/utils/UrlOpener.dart diff --git a/lib/utils/UrlOpener.dart b/lib/utils/UrlOpener.dart new file mode 100644 index 0000000..450ed94 --- /dev/null +++ b/lib/utils/UrlOpener.dart @@ -0,0 +1,10 @@ +import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:url_launcher/url_launcher_string.dart'; + +class UrlOpener { + static Future onOpen(LinkableElement link) async { + if(await canLaunchUrlString(link.url)) { + await launchUrlString(link.url); + } + } +} diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index a6a1f2b..a5d281d 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -315,7 +315,8 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM shrinkWrap: true, children: [ PollOptionsList( - pollData: pollData + pollData: pollData, + chatToken: widget.chatData.token ) ] ); diff --git a/lib/view/pages/talk/components/chatMessage.dart b/lib/view/pages/talk/components/chatMessage.dart index d969039..468805f 100644 --- a/lib/view/pages/talk/components/chatMessage.dart +++ b/lib/view/pages/talk/components/chatMessage.dart @@ -2,12 +2,12 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; -import 'package:url_launcher/url_launcher_string.dart'; import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart'; import '../../../../api/marianumcloud/talk/chat/richObjectStringProcessor.dart'; import '../../../../model/accountData.dart'; import '../../../../model/endpointData.dart'; +import '../../../../utils/UrlOpener.dart'; class ChatMessage { String originalMessage; @@ -29,7 +29,7 @@ class ChatMessage { var contentWidget = Linkify( text: content, - onOpen: onOpen, + onOpen: UrlOpener.onOpen, ); if(originalData?['object']?.type == RichObjectStringObjectType.talkPoll) { @@ -73,10 +73,4 @@ class ChatMessage { ) ); } - - Future onOpen(LinkableElement link) async { - if(await canLaunchUrlString(link.url)) { - await launchUrlString(link.url); - } - } } diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart index 6a9b63d..670d8e4 100644 --- a/lib/view/pages/talk/components/pollOptionsList.dart +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -3,10 +3,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import '../../../../api/marianumcloud/talk/getPoll/getPollStateResponse.dart'; +import '../../../../utils/UrlOpener.dart'; class PollOptionsList extends StatefulWidget { final GetPollStateResponseObject pollData; - const PollOptionsList({super.key, required this.pollData}); + final String chatToken; + const PollOptionsList({super.key, required this.pollData, required this.chatToken}); @override State createState() => _PollOptionsListState(); @@ -54,7 +56,8 @@ class _PollOptionsListState extends State { }), ListTile( title: Linkify( - text: 'Wenn du abstimmen möchtest, verwende die Webversion unter https://cloud.marianum-fulda.de', + text: 'Wenn du abstimmen möchtest, verwende die Webversion unter https://cloud.marianum-fulda.de/call/${widget.chatToken}', + onOpen: UrlOpener.onOpen, style: Theme.of(context).textTheme.bodySmall, ), ) From dacefd321bda9d163df8756299d0b76a87a28c06 Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 1 Feb 2026 15:35:40 +0100 Subject: [PATCH 09/10] updated poll list design --- lib/view/pages/talk/components/chatBubble.dart | 2 +- lib/view/pages/talk/components/pollOptionsList.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index a5d281d..0190b3b 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -304,7 +304,7 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM var pollId = int.parse(message.originalData!['object']!.id); var pollState = GetPollState(token: widget.bubbleData.token, pollId: pollId).run(); showDialog(context: context, builder: (context) => AlertDialog( - title: Text(message.originalData!['object']!.name, textScaler: TextScaler.linear(0.9)), + title: Text(message.originalData!['object']!.name, textScaler: TextScaler.linear(0.9), overflow: TextOverflow.ellipsis), content: FutureBuilder( future: pollState, builder: (context, snapshot) { diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart index 670d8e4..dd9cd70 100644 --- a/lib/view/pages/talk/components/pollOptionsList.dart +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -35,7 +35,7 @@ class _PollOptionsListState extends State { option, style: Theme.of(context).textTheme.bodyLarge, ), - trailing: Icon( + leading: Icon( votedSelf ? Icons.check_circle_outlined : Icons.circle_outlined, color: votedSelf ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.6) From e4243e53ac3369f317c7934d6e5e3c64709296ce Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 1 Feb 2026 15:55:43 +0100 Subject: [PATCH 10/10] resolved pr issues --- lib/api/marianumcloud/talk/votePoll/votePoll.dart | 9 ++------- lib/api/marianumcloud/talk/votePoll/votePollParams.dart | 3 ++- lib/view/pages/talk/components/chatBubble.dart | 2 +- lib/view/pages/talk/components/pollOptionsList.dart | 5 +++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/api/marianumcloud/talk/votePoll/votePoll.dart b/lib/api/marianumcloud/talk/votePoll/votePoll.dart index a0880bd..7cedfe1 100644 --- a/lib/api/marianumcloud/talk/votePoll/votePoll.dart +++ b/lib/api/marianumcloud/talk/votePoll/votePoll.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:developer'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; @@ -8,22 +7,18 @@ import '../getPoll/getPollStateResponse.dart'; import '../talkApi.dart'; import 'votePollParams.dart'; -@Deprecated('VotePoll is broken (known issues)') +@Deprecated('VotePoll is broken') class VotePoll extends TalkApi { String token; int pollId; VotePoll({required this.token, required this.pollId, required VotePollParams params}) : super('v1/poll/$token/$pollId', params); @override - GetPollStateResponse assemble(String raw) { - log(raw); - return GetPollStateResponse.fromJson(jsonDecode(raw)['ocs']); - } + GetPollStateResponse assemble(String raw) => GetPollStateResponse.fromJson(jsonDecode(raw)['ocs']); @override Future? request(Uri uri, Object? body, Map? headers) { if(body is VotePollParams) { - log(body.toJson().toString()); return http.post(uri, headers: headers, body: body.toJson().toString()); } return null; diff --git a/lib/api/marianumcloud/talk/votePoll/votePollParams.dart b/lib/api/marianumcloud/talk/votePoll/votePollParams.dart index c8a7bca..151459d 100644 --- a/lib/api/marianumcloud/talk/votePoll/votePollParams.dart +++ b/lib/api/marianumcloud/talk/votePoll/votePollParams.dart @@ -5,10 +5,11 @@ import '../../../apiParams.dart'; part 'votePollParams.g.dart'; @JsonSerializable() +@Deprecated('VotePoll is broken') class VotePollParams extends ApiParams { List optionIds; VotePollParams({required this.optionIds}); factory VotePollParams.fromJson(Map json) => _$VotePollParamsFromJson(json); Map toJson() => _$VotePollParamsToJson(this); -} \ No newline at end of file +} diff --git a/lib/view/pages/talk/components/chatBubble.dart b/lib/view/pages/talk/components/chatBubble.dart index 0190b3b..08b9866 100644 --- a/lib/view/pages/talk/components/chatBubble.dart +++ b/lib/view/pages/talk/components/chatBubble.dart @@ -304,7 +304,7 @@ class _ChatBubbleState extends State with SingleTickerProviderStateM var pollId = int.parse(message.originalData!['object']!.id); var pollState = GetPollState(token: widget.bubbleData.token, pollId: pollId).run(); showDialog(context: context, builder: (context) => AlertDialog( - title: Text(message.originalData!['object']!.name, textScaler: TextScaler.linear(0.9), overflow: TextOverflow.ellipsis), + title: Text(message.originalData!['object']!.name, overflow: TextOverflow.ellipsis), content: FutureBuilder( future: pollState, builder: (context, snapshot) { diff --git a/lib/view/pages/talk/components/pollOptionsList.dart b/lib/view/pages/talk/components/pollOptionsList.dart index dd9cd70..3e4eec4 100644 --- a/lib/view/pages/talk/components/pollOptionsList.dart +++ b/lib/view/pages/talk/components/pollOptionsList.dart @@ -22,8 +22,9 @@ class _PollOptionsListState extends State { var optionId = widget.pollData.options.indexOf(option); var votedSelf = widget.pollData.votedSelf.contains(optionId); var portionsVisible = widget.pollData.votes is Map; - var votes = !portionsVisible ? 0 - : (widget.pollData.votes['option-$optionId'] as num?) ?? 0; + var votes = portionsVisible + ? (widget.pollData.votes['option-$optionId'] as num?) ?? 0 + : 0; var numVoters = widget.pollData.numVoters ?? 0; double portion = numVoters == 0 ? 0 : (votes / numVoters);