implemented full interactive poll support in Talk, including creation, voting, and closing functionality
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../api_params.dart';
|
||||
import '../get_poll/get_poll_state_response.dart';
|
||||
import '../talk_api.dart';
|
||||
|
||||
/// Schließt eine Umfrage endgültig — nur Ersteller oder Moderatoren.
|
||||
class ClosePoll extends TalkApi<GetPollStateResponse> {
|
||||
ClosePoll({required String token, required int pollId})
|
||||
: super('v1/poll/$token/$pollId', null);
|
||||
|
||||
@override
|
||||
GetPollStateResponse assemble(String raw) {
|
||||
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
||||
return GetPollStateResponse.fromJson(
|
||||
decoded['ocs'] as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<http.Response> request(
|
||||
Uri uri,
|
||||
ApiParams? body,
|
||||
Map<String, String>? headers,
|
||||
) => http.delete(uri, headers: headers);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../api_params.dart';
|
||||
import '../talk_api.dart';
|
||||
import 'create_poll_params.dart';
|
||||
|
||||
/// Erstellt eine Umfrage; der Server postet die Poll-Nachricht selbst in den
|
||||
/// Chat, danach genügt ein Chat-Refresh. Nur in Gruppen-Chats erlaubt.
|
||||
class CreatePoll extends TalkApi {
|
||||
CreatePoll({required String token, required CreatePollParams params})
|
||||
: super(
|
||||
'v1/poll/$token',
|
||||
params,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
|
||||
@override
|
||||
Null assemble(String raw) => null;
|
||||
|
||||
@override
|
||||
Future<http.Response>? request(
|
||||
Uri uri,
|
||||
ApiParams? body,
|
||||
Map<String, String>? headers,
|
||||
) {
|
||||
if (body is! CreatePollParams) return null;
|
||||
return http.post(uri, headers: headers, body: jsonEncode(body.toJson()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import '../../../api_params.dart';
|
||||
|
||||
part 'create_poll_params.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class CreatePollParams extends ApiParams {
|
||||
String question;
|
||||
List<String> options;
|
||||
|
||||
/// 0 = Ergebnisse öffentlich, 1 = bis zum Schließen verborgen.
|
||||
int resultMode;
|
||||
|
||||
/// Stimmen pro Teilnehmer; 0 = unbegrenzt.
|
||||
int maxVotes;
|
||||
|
||||
CreatePollParams({
|
||||
required this.question,
|
||||
required this.options,
|
||||
required this.resultMode,
|
||||
required this.maxVotes,
|
||||
});
|
||||
factory CreatePollParams.fromJson(Map<String, dynamic> json) =>
|
||||
_$CreatePollParamsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$CreatePollParamsToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'create_poll_params.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CreatePollParams _$CreatePollParamsFromJson(Map<String, dynamic> json) =>
|
||||
CreatePollParams(
|
||||
question: json['question'] as String,
|
||||
options: (json['options'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
resultMode: (json['resultMode'] as num).toInt(),
|
||||
maxVotes: (json['maxVotes'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CreatePollParamsToJson(CreatePollParams instance) =>
|
||||
<String, dynamic>{
|
||||
'question': instance.question,
|
||||
'options': instance.options,
|
||||
'resultMode': instance.resultMode,
|
||||
'maxVotes': instance.maxVotes,
|
||||
};
|
||||
@@ -4,6 +4,18 @@ import '../../../api_response.dart';
|
||||
|
||||
part 'get_poll_state_response.g.dart';
|
||||
|
||||
/// Poll-`status`-Werte der Talk-API.
|
||||
const int pollStatusOpen = 0;
|
||||
const int pollStatusClosed = 1;
|
||||
|
||||
/// Poll-`resultMode`-Werte der Talk-API.
|
||||
const int pollResultModePublic = 0;
|
||||
const int pollResultModeHidden = 1;
|
||||
|
||||
/// `participantType`-Werte (aus dem Room), die eine Umfrage schließen dürfen:
|
||||
/// Owner (1), Moderator (2) und Gast-Moderator (6).
|
||||
const Set<int> pollModeratorParticipantTypes = {1, 2, 6};
|
||||
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class GetPollStateResponse extends ApiResponse {
|
||||
GetPollStateResponseObject data;
|
||||
@@ -50,4 +62,32 @@ class GetPollStateResponseObject {
|
||||
factory GetPollStateResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$GetPollStateResponseObjectFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$GetPollStateResponseObjectToJson(this);
|
||||
|
||||
bool get isClosed => status == pollStatusClosed;
|
||||
|
||||
bool get resultsHidden => resultMode == pollResultModeHidden;
|
||||
|
||||
/// Ergebnisse sichtbar: öffentliche Umfragen jederzeit, verborgene erst nach
|
||||
/// dem Schließen. Der Typ von `votes` taugt nicht als Signal (siehe unten).
|
||||
bool get resultsVisible => resultMode == pollResultModePublic || isClosed;
|
||||
|
||||
/// Normalisiert das dynamische `votes`-Feld zu einer Map: der Server liefert
|
||||
/// bei verborgenen Ergebnissen (und ohne Stimmen) eine leere Liste statt Map.
|
||||
Map<String, num> get voteCounts {
|
||||
final raw = votes;
|
||||
if (raw is! Map) return const {};
|
||||
final result = <String, num>{};
|
||||
raw.forEach((key, value) {
|
||||
if (key is String && value is num) result[key] = value;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Darf der Nutzer die (offene) Umfrage schließen: als Ersteller oder Moderator.
|
||||
bool canClose({required String selfId, required int participantType}) {
|
||||
if (isClosed) return false;
|
||||
final isCreator = actorType == 'users' && actorId == selfId;
|
||||
final isModerator = pollModeratorParticipantTypes.contains(participantType);
|
||||
return isCreator || isModerator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../api_params.dart';
|
||||
import '../get_poll/get_poll_state_response.dart';
|
||||
import '../talk_api.dart';
|
||||
import 'vote_poll_params.dart';
|
||||
|
||||
class VotePoll extends TalkApi<GetPollStateResponse> {
|
||||
// Body als echtes JSON (nicht form-encoded wie die anderen Endpunkte): nur
|
||||
// so kommt das int-Array an; sonst liest der Server optionIds als [] und
|
||||
// löscht die eigene Stimme (Ursache des Readonly-Fallbacks, Issue #42).
|
||||
VotePoll({
|
||||
required String token,
|
||||
required int pollId,
|
||||
required VotePollParams params,
|
||||
}) : super(
|
||||
'v1/poll/$token/$pollId',
|
||||
params,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
|
||||
@override
|
||||
GetPollStateResponse assemble(String raw) {
|
||||
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
||||
return GetPollStateResponse.fromJson(
|
||||
decoded['ocs'] as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<http.Response>? request(
|
||||
Uri uri,
|
||||
ApiParams? body,
|
||||
Map<String, String>? headers,
|
||||
) {
|
||||
if (body is! VotePollParams) return null;
|
||||
return http.post(uri, headers: headers, body: jsonEncode(body.toJson()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import '../../../api_params.dart';
|
||||
|
||||
part 'vote_poll_params.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class VotePollParams extends ApiParams {
|
||||
/// Indizes der gewählten Optionen; leer = eigene Stimme zurückziehen.
|
||||
List<int> optionIds;
|
||||
|
||||
VotePollParams({required this.optionIds});
|
||||
factory VotePollParams.fromJson(Map<String, dynamic> json) =>
|
||||
_$VotePollParamsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$VotePollParamsToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'vote_poll_params.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
VotePollParams _$VotePollParamsFromJson(Map<String, dynamic> json) =>
|
||||
VotePollParams(
|
||||
optionIds: (json['optionIds'] as List<dynamic>)
|
||||
.map((e) => (e as num).toInt())
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$VotePollParamsToJson(VotePollParams instance) =>
|
||||
<String, dynamic>{'optionIds': instance.optionIds};
|
||||
Reference in New Issue
Block a user