From 634c9898d9b5338ecdf93241e84f78b5d21122f1 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 11 Apr 2024 12:52:46 +0200 Subject: [PATCH 1/7] Paper plane animation on test page --- .../assets/animations/rive/paper_plane.riv | Bin 0 -> 5270 bytes .../components/animations/paper_plane.dart | 41 ++++++++++++++++ frontend/lib/pages/test_page.dart | 45 +++++++++++------- frontend/pubspec.yaml | 1 + 4 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 frontend/assets/animations/rive/paper_plane.riv create mode 100644 frontend/lib/components/animations/paper_plane.dart diff --git a/frontend/assets/animations/rive/paper_plane.riv b/frontend/assets/animations/rive/paper_plane.riv new file mode 100644 index 0000000000000000000000000000000000000000..f1fd7fd1a41e6b346921510991c5406f64dd0ec3 GIT binary patch literal 5270 zcmai24RBP|6~1qG-w;A}vzrjIt8{lkHb^A~n!hH%?t5=HPeMb5jX5rMIb$ibJ+4pug z>oW7;+;hM4opbLw_nx~%Ti3VDAeWNW*R4-W*Cc{}Dv4w(QU~K(VqJsVW)M;nDI(-p zsqpHRwxs9mC!eSg!bbSE2Zo1-Q`2xw0=eHwYfbvyAXjSet>-Ho(AGwG|N1rwyS3UQ?+>Ob&N3ir6EabQ^>FE61 z7F_CL%2MrqXrxE+oEo{R07JD~nf$Hdxpl){L7yHOWzA?^3?JrE*uLxhY6m7$+L zv0QB(-5=7{?zk2NZR_aCpt7~Jc9{y-O-Kt)SP&a%Eek0dmEa2XrT*O^t>q^_%ZK$f zTJ^sBh^Mxe+*I!*-iV~%Xc!E^z)lD1-=7M@z(EH~M!jKxehm}>5{O70aE}pCAUs2{ zDy@jecK-8iIRg}F!)?wMDY3b7}=(bOzcpxw$Z|5W)Pi8uG=j$PE+A=?d zOU*hc`N|`!^z*^8j!eEP$~uhsU|AUn2#;%z~_Ty9pUMn`WkI|S6?6OAJze0 zW|o#c`pSnOdw3ICTK4KkE~HO3&RsYGnG$oukx_JmX~VgVm|TncjyaglL9%w!hfa-j-@O~9`hHgI!`o-mu+AUVB10@R$}DoD;n zH0KvgG1#!{9ACn)4lvf&{Nj-Wh~QBF>_7J>K?Gy{to1??(ClWXPiBU$7(0dKis+*D zkB|e={_|@!Sh(J-@c@y_Q@$_lWh)4D>68I0F z9YqoG{jtmFqr$XqdX=+vgNU~&uA7T|+ps%2d0e>M+eX85|Tqnxk6u}_q zop<5A$v}MV7`zm{JPsKzX^+Qtik~%2UYdAtQp5riyB)xeD>mNo1#>)+0-Rx&Q%cCm z8yGJdp81; zsSS$h1WpsTuW=^4mHfBH`nD}<+6PeHiNi~Q)p#}0q492_L;iB&jTTknQ1J4{;(#g= z3E_>K0_mOwuslWSAdv4Q@5zN3c~>sXDEcz^sMz%SnS_ zOxS=KyS_Wf`vSG$&sucIdpmPTn}j#K5A;+_ck#jn#hYN^g5nLoFe7jIg&BF9E)3*t zzp!imj3~^An+nad5F^~eZL!$S-hu7@ySjJBo6cCESh1ioN}(3E{f6}brts?^zH;ty z6gPv|b?iPA`$2qe%PJHP;`7jbV-V$YD0i&-Ag4T#@Q-(YA2CSS@ZTyFk?@>v5{nNB zt+sh-!W!SEG~wZYY~a6y=*b5zPV?&V?_K0He{E~T{)jXmpMMxd8keX3m*b?q(FH$I z{O$3sc(;Fb*Vdlifo{@tND?m)5+o~*N;abYP_q3U4DCuujn3Dt createState() => _PaperPlaneState(); +} + +class _PaperPlaneState extends State { + final riveFileName = 'assets/animations/rive/paper_plane.riv'; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Positioned( + key: UniqueKey(), + top: widget.clickY - 144, + left: widget.clickX - 602, + child: SizedBox( + width: 700, + height: 1200, + child: RiveAnimation.asset( + riveFileName, + fit: BoxFit.cover, + artboard: 'Artboard', + stateMachines: const ['Hit State Machine'], + ), + ), + ); + } +} diff --git a/frontend/lib/pages/test_page.dart b/frontend/lib/pages/test_page.dart index b4896055..2963b651 100644 --- a/frontend/lib/pages/test_page.dart +++ b/frontend/lib/pages/test_page.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:frontend/components/animations/paper_plane.dart'; +import 'package:rive/rive.dart'; class TestPage extends StatefulWidget { const TestPage({super.key}); @@ -8,20 +10,18 @@ class TestPage extends StatefulWidget { } class _TestPageState extends State with TickerProviderStateMixin { - late AnimationController _controller; + List _animations = []; + final riveFileName = 'assets/animations/rive/paper_plane.riv'; @override void initState() { super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 600), - vsync: this, - )..forward(); } @override Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; + return Scaffold( appBar: AppBar( title: const Text("Test Seite", @@ -30,21 +30,30 @@ class _TestPageState extends State with TickerProviderStateMixin { backgroundColor: colors.primary, ), body: SafeArea( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - - ], - ), + child: GestureDetector( + onTapUp: (details) { + print("ADD ANIMATION"); + setState(() { + _animations.insert(0, PaperPlane( + key: UniqueKey(), + clickX: details.localPosition.dx, + clickY: details.localPosition.dy)); + print(_animations.length); + }); + Future.delayed(const Duration(milliseconds: 2500), () { + setState(() { + _animations.removeLast(); + }); + }); + }, + child: Stack(children: [ + Container( + color: Colors + .transparent), // This container takes up the whole screen + ..._animations, + ]), ), ), ); } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } } diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index 1a92fa75..49a0f5cc 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -76,6 +76,7 @@ flutter: - assets/titles/ - assets/logo/ - assets/animations/rive/animations.riv + - assets/animations/rive/paper_plane.riv fonts: - family: NunitoSans From ddf472cf6ef760e74cc4cbb7b34b8b24e83bb5fe Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 11 Apr 2024 16:26:27 +0200 Subject: [PATCH 2/7] Improve paper plane animation --- .../assets/animations/rive/paper_plane.riv | Bin 5270 -> 5270 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/assets/animations/rive/paper_plane.riv b/frontend/assets/animations/rive/paper_plane.riv index f1fd7fd1a41e6b346921510991c5406f64dd0ec3..3bfe9a0d02de468250d50859db0f699c5d509a0c 100644 GIT binary patch delta 1588 zcmZ8fUuaup6#u^5+@@)gn?Fn1d(*AyqD?a;S!!-7ZDNv}Hfgm>?UL071Rbv6)QKpa zLt*QNf?<7d@wh37I#HZ#pddqE#3k{iusnz;L(pyDOVOzl9MnHp&-eA-EZf65oZr85 zZ_drB9jA7jh~i@%2FYo8pbCK%X9Mw8_Mr) zl4A%oQvjbp-U>9Oy+1w1-YderQTf=){;YClWi5-osGPlgF{^|Zo@c2j)J*qmRh?h0 zAJ4c}-aDGX6uaK2Ldff*Y=A2!|8~_Gc<}nB!M(Au%v?znQ-&)Ae!-7xJ~QRB#u^O` zIm3{HIl*62ozmU5x?z|?!?aYSu{2 z!WV+SPc^hMYI_WA5Bw^~%U+QXFa9&U`1QT`g}tJT6~GE%MX(ZBNh}R(3af}!CbB5* zB;<59Mn@fkQZpkg@NhHn?JYl)B-PeMV5?z3{$$1cDBQIw9rgN9PIb zF5Z2dvGQ(%e;sVYDt0%-q&V$Gj{=7!H_bp@3gdMyyTf#b&nmns#qes3Ny4I&UE?+E zW%$j>u2jdl2yPd>a1#dWCiB@IM8q- z5O10_F=I#3#B9!N31(b)v-or4XuTnkL}O-*iNDzmnM>Rfml)%$=l%G;@^j(EIX%yF z&U4P&r(Ha-c;G@<8mS~?qUa=Ky>OQ<#FHJIL`u&V*kLIL|uq+NF3weXPjjqu$S zd1UEK5&J6kJ$By%3A*noPHlPPgShm~_iG6`_wt~QT~ zic(EmzJC3XJ~TA+sfj3AN$MO~Es{q>4VLNt)*5LQ77B81{9qDQK2zo2rt)iBRsM^d z|4~b%{@ct%0aFyj13|5SXu6>P;UP(eAjA3b0D0eElt*tr&z{v}&W5%5{Dh$|<<||I z3v0&DUm1F6_%)^q!y@x8_0h!xuF)y;_wVEYTiOMFzAhV4KH<`ay_>zj+J?op-Q&3X?B;IyG1rQ% z%Ao3qz4i#?`MTW^rLcDDrajPy1IO%vV^HAhW+Qeli@ia+H@NNOi+J+f#>s2ENJy0# z0)9F%RE$~-A4UM938M`oieX@6F#0gEL=%OBgj_LNXe&kFL@--M>eg1R3CtNcfEQ!4{x)m5(L1ruHvw_GSl7%0Hc^ZMY#75c+lVV*n{&a-skseal9w31f9hsV% zVnl2w!6Bi-eX#-ViEqvx-`Vag6V}GzokV|`uucq=2}{yZCaj5cnXo2O7O~5Er~fd2 z^rfjzA3m6UsT!!SsG@ObsR+^-j5>p~$IQJZ6NR;kFwMZi`jrB%vlII z8Ylq^VBs^CcqsPtV9cCV2C*XwB37r*t zXurv&&9lR1P9d{j_a7EAW0RBkTxO?4d?_wHojE$s!|zUA`0O$+cDSCP3e31#X(cSX zBD4z9n4Qq=K0yWe#WhZ=;l8_-*CQ%ib@xJpq*Di6km{*y<|>vumF><2*moJROsxE$ zo&f(y?Aj`36 z9(z>gcN{YZr_~2&7}KC3sP8aqv<+)C3UfbWAn$QQmv;|MAqv7r-fGl-;Vo6i$dm6- zfh69g!h6(xnf}RNp(o+nUBRcRiPtL-lg{&$&rHs+S!&NR-c%FD5S(U=!Dcwd@D>wi afP+j`D1 Date: Thu, 11 Apr 2024 22:41:16 +0200 Subject: [PATCH 3/7] Quiz scoreboard paper planes synchronized with sockets --- .../services/quiz/socket/Fun.java | 50 ++++++ .../services/quiz/socket/LiveQuizSocket.java | 22 ++- .../quiz/socket/LiveQuizSocketMessage.java | 16 +- frontend/lib/pages/quiz/attend_quiz_page.dart | 149 +++++++++++++++++- .../lib/pages/quiz/quiz_control_page.dart | 149 ++++++++++++++---- frontend/lib/pages/test_page.dart | 1 - 6 files changed, 349 insertions(+), 38 deletions(-) create mode 100644 backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/Fun.java diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/Fun.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/Fun.java new file mode 100644 index 00000000..e44baa74 --- /dev/null +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/Fun.java @@ -0,0 +1,50 @@ +package de.htwg_konstanz.mobilelearning.services.quiz.socket; + +import org.bson.types.ObjectId; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import de.htwg_konstanz.mobilelearning.helper.ObjectIdTypeAdapter; + +public class Fun { + + public String action; // THROW_PAPER_PLANE + public double percentageX; + public double percentageY; + + public Fun(String message) { + + + Gson gson = new GsonBuilder().registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter()).create(); + Fun funString = gson.fromJson(message, Fun.class); + this.action = funString.action; + this.percentageX = funString.percentageX; + this.percentageY = funString.percentageY; + + System.out.println("Fun Action: " + this.action); + System.out.println("Fun percentageX: " + this.percentageX); + System.out.println("Fun percentageY: " + this.percentageY); + } + + public Fun(String action, double percentageX, double percentageY) { + this.action = action; + this.percentageX = percentageX; + this.percentageY = percentageY; + } + + public String toJson() { + Gson gson = new GsonBuilder().registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter()).create(); + return gson.toJson(this); + } + + public Fun copy() { + return new Fun(this.action, this.percentageX, this.percentageY); + } + + public static Fun getByJsonWithForm(String message) { + Gson gson = new GsonBuilder().registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter()).create(); + return gson.fromJson(message, Fun.class); + } + +} diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java index c12f3377..18cee110 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java @@ -182,7 +182,7 @@ public void onMessage(String message, @PathParam("courseId") String courseId, @P private void broadcast(LiveQuizSocketMessage message, String courseId, String formId) { // copy the message to not change the original - LiveQuizSocketMessage messageToSend = new LiveQuizSocketMessage(message.action, message.formStatus, message.resultElementId, message.resultValues, null); + LiveQuizSocketMessage messageToSend = new LiveQuizSocketMessage(message.action, message.formStatus, message.resultElementId, message.resultValues, null, message.fun); connections.values().forEach(connection -> { @@ -248,6 +248,10 @@ private Boolean evaluateMessage(LiveQuizSocketMessage quizSocketMessage, String return this.next(quizSocketMessage, course, formId, user); } + if (quizSocketMessage.action.equals("FUN")) { + return this.fun(quizSocketMessage, course, formId, user); + } + return false; }; @@ -408,4 +412,20 @@ private Boolean next(LiveQuizSocketMessage quizSocketMessage, Course course, Str return true; } + private Boolean fun(LiveQuizSocketMessage quizSocketMessage, Course course, String formId, User user) { + + System.out.println("Throw paper plane"); + + // TODO: useless but error if not there + QuizForm form = course.getQuizFormById(new ObjectId(formId)); + if (form == null) { + System.out.println("Form not found"); + return false; + } + + quizSocketMessage.form = form; + this.broadcast(quizSocketMessage, course.getId().toHexString(), formId); + + return true; + } } diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocketMessage.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocketMessage.java index ead303e2..47548142 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocketMessage.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocketMessage.java @@ -25,6 +25,9 @@ public class LiveQuizSocketMessage { // outgoing message public QuizForm form; + // fun stuff (throw paper plane) + public Fun fun; + public LiveQuizSocketMessage(String message) { @@ -35,6 +38,7 @@ public LiveQuizSocketMessage(String message) { this.formStatus = quizSocketMessage.formStatus; this.resultElementId = quizSocketMessage.resultElementId; this.resultValues = quizSocketMessage.resultValues; + this.fun = quizSocketMessage.fun; this.form = null; System.out.println("Action: " + this.action); @@ -43,12 +47,22 @@ public LiveQuizSocketMessage(String message) { System.out.println("Result value: " + this.resultValues); } + public LiveQuizSocketMessage(String action, String formStatus, String resultElementId, List resultValues, QuizForm form, Fun fun) { + this.action = action; + this.formStatus = formStatus; + this.resultElementId = resultElementId; + this.resultValues = resultValues; + this.form = form; + this.fun = fun; + } + public LiveQuizSocketMessage(String action, String formStatus, String resultElementId, List resultValues, QuizForm form) { this.action = action; this.formStatus = formStatus; this.resultElementId = resultElementId; this.resultValues = resultValues; this.form = form; + this.fun = null; } public String toJson() { @@ -57,7 +71,7 @@ public String toJson() { } public LiveQuizSocketMessage copy() { - return new LiveQuizSocketMessage(this.action, this.formStatus, this.resultElementId, this.resultValues, this.form); + return new LiveQuizSocketMessage(this.action, this.formStatus, this.resultElementId, this.resultValues, this.form, this.fun); } public static LiveQuizSocketMessage getByJsonWithForm(String message) { diff --git a/frontend/lib/pages/quiz/attend_quiz_page.dart b/frontend/lib/pages/quiz/attend_quiz_page.dart index c1324622..378a3dc6 100644 --- a/frontend/lib/pages/quiz/attend_quiz_page.dart +++ b/frontend/lib/pages/quiz/attend_quiz_page.dart @@ -3,10 +3,12 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:frontend/auth_state.dart'; +import 'package:frontend/components/animations/paper_plane.dart'; import 'package:frontend/components/elements/quiz/single_choice_quiz.dart'; import 'package:frontend/components/elements/quiz/yes_no_quiz.dart'; import 'package:frontend/components/error/general_error_widget.dart'; import 'package:frontend/components/error/network_error_widget.dart'; +import 'package:frontend/components/general/quiz/QuizScoreboard.dart'; import 'package:frontend/components/general/quiz/choose_alias.dart'; import 'package:frontend/enums/form_status.dart'; import 'package:frontend/enums/question_type.dart'; @@ -38,12 +40,19 @@ class _AttendQuizPageState extends AuthState { QuizForm? _form; WebSocketChannel? _socketChannel; + late List _scoreboard; + dynamic _value; bool _voted = false; bool _loading = false; String _fetchResult = ''; + // for the paper plane animation + final mainStackKey = GlobalKey(); + final scoreboardKey = GlobalKey(); + List _animations = []; + @override void initState() { super.initState(); @@ -112,7 +121,6 @@ class _AttendQuizPageState extends AuthState { if (response.statusCode == 200) { setState(() { _aliasChosen = true; - _loading = false; _fetchResult = 'success'; }); return true; @@ -155,6 +163,9 @@ class _AttendQuizPageState extends AuthState { if (response.statusCode == 200) { var data = jsonDecode(response.body); var form = QuizForm.fromJson(data); + if (form.status == FormStatus.finished) { + _scoreboard = getScoreboard(data); + } startWebsocket(); @@ -215,6 +226,9 @@ class _AttendQuizPageState extends AuthState { _voted = false; _form?.currentQuestionIndex = form.currentQuestionIndex; _form?.currentQuestionFinished = form.currentQuestionFinished; + if (_form?.status == FormStatus.finished) { + _scoreboard = getScoreboard(data["form"]); + } }); } if (data["action"] == "CLOSED_QUESTION" || @@ -227,6 +241,13 @@ class _AttendQuizPageState extends AuthState { _form?.currentQuestionFinished = form.currentQuestionFinished; }); } + if (data["action"] == "FUN") { + if (data["fun"]["action"] == "THROW_PAPER_PLANE") { + double percentageX = data["fun"]["percentageX"]; + double percentageY = data["fun"]["percentageY"]; + animatePaperPlane(percentageX, percentageY); + } + } }, onError: (error) { //TODO: Should there be another error handling for this? setState(() { @@ -235,12 +256,75 @@ class _AttendQuizPageState extends AuthState { }); } + List getScoreboard(Map form) { + print("GET SCOREBOARD"); + List elements = form["participants"]; + int rank = 0; + int lastScore = -1; + List sortedElements = List.from(elements); + sortedElements.sort((a, b) => b["score"] - a["score"]); + return sortedElements.map((element) { + if (lastScore != element["score"]) { + rank++; + } + lastScore = element["score"]; + return { + "userAlias": element["userAlias"], + "score": element["score"], + "rank": rank, + }; + }).toList(); + } + @override void dispose() { _socketChannel?.sink.close(); super.dispose(); } + void throwPaperPlane(double percentageX, double percentageY) { + if (_socketChannel != null) { + _socketChannel!.sink.add(jsonEncode({ + "action": "FUN", + "fun": { + "action": "THROW_PAPER_PLANE", + "percentageX": percentageX, + "percentageY": percentageY, + }, + "role": "STUDENT", + "userId": _userId, + })); + } + } + + void animatePaperPlane(double percentageX, double percentageY) { + print("ADD ANIMATION"); + + final RenderBox mainStackBox = + mainStackKey.currentContext!.findRenderObject() as RenderBox; + final RenderBox scoreBoardBox = + scoreboardKey.currentContext!.findRenderObject() as RenderBox; + final mainStackPosition = mainStackBox.localToGlobal(Offset.zero); + final scoreboardPosition = scoreBoardBox.localToGlobal(Offset.zero); + double dX = scoreboardPosition.dx - + mainStackPosition.dx + + percentageX * scoreBoardBox.size.width; + double dY = scoreboardPosition.dy - + mainStackPosition.dy + + percentageY * scoreBoardBox.size.height; + + setState(() { + _animations.insert( + 0, PaperPlane(key: UniqueKey(), clickX: dX, clickY: dY)); + print(_animations.length); + }); + Future.delayed(const Duration(milliseconds: 2500), () { + setState(() { + _animations.removeLast(); + }); + }); + } + @override Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; @@ -280,6 +364,69 @@ class _AttendQuizPageState extends AuthState { ); } + if (_form?.status == FormStatus.finished) { + return Scaffold( + appBar: appbar, + body: Stack( + key: mainStackKey, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Text( + "Quiz beendet", + style: Theme.of(context).textTheme.headlineMedium, + ), + // Container( + // margin: const EdgeInsets.only(top: 30.0, bottom: 10.0), + // width: 250, + // height: 250, + // child: RiveAnimation.asset( + // 'assets/animations/rive/animations.riv', + // fit: BoxFit.cover, + // artboard: 'rigged without bodyparts darker firework', + // stateMachines: ['State Machine Winner'], + // ), + // ), + const SizedBox(height: 16), + Center( + child: GestureDetector( + key: scoreboardKey, + onTapUp: (details) { + // get the position of the tap and convert it to a percentage of the total height + final RenderBox box = scoreboardKey.currentContext! + .findRenderObject() as RenderBox; + double x = details.localPosition.dx; + double percentageX = x / box.size.width; + double y = details.localPosition.dy; + double percentageY = y / box.size.height; + throwPaperPlane(percentageX, percentageY); + }, + child: Container( + constraints: const BoxConstraints(maxWidth: 800), + child: Card( + child: Padding( + padding: const EdgeInsets.all(8), + child: QuizScoreboard(scoreboard: _scoreboard), + ), + ), + ), + ), + ), + ], + ), + ), + IgnorePointer( + child: Stack( + children: _animations, + ), + ) + ], + ), + ); + } + if (_form?.status != FormStatus.started || _voted) { return Scaffold( appBar: appbar, diff --git a/frontend/lib/pages/quiz/quiz_control_page.dart b/frontend/lib/pages/quiz/quiz_control_page.dart index f8f456ce..8a235d54 100644 --- a/frontend/lib/pages/quiz/quiz_control_page.dart +++ b/frontend/lib/pages/quiz/quiz_control_page.dart @@ -2,9 +2,12 @@ import 'dart:convert'; import 'dart:io'; import 'dart:async'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:frontend/auth_state.dart'; +import 'package:frontend/components/animations/paper_plane.dart'; import 'package:frontend/enums/form_status.dart'; import 'package:frontend/enums/question_type.dart'; import 'package:frontend/components/elements/quiz/single_choice_quiz_result.dart'; @@ -50,6 +53,11 @@ class _QuizControlPageState extends AuthState { bool _loading = false; String _fetchResult = ''; + // for the paper plane animation + final mainStackKey = GlobalKey(); + final scoreboardKey = GlobalKey(); + List _animations = []; + @override void initState() { super.initState(); @@ -177,6 +185,13 @@ class _QuizControlPageState extends AuthState { .toList(); }); } + if (data["action"] == "FUN") { + if (data["fun"]["action"] == "THROW_PAPER_PLANE") { + double percentageX = data["fun"]["percentageX"]; + double percentageY = data["fun"]["percentageY"]; + animatePaperPlane(percentageX, percentageY); + } + } }, onError: (error) { //TODO: Should there be another error handling for this? setState(() { @@ -244,6 +259,49 @@ class _QuizControlPageState extends AuthState { } } + void throwPaperPlane(double percentageX, double percentageY) { + if (_socketChannel != null) { + _socketChannel!.sink.add(jsonEncode({ + "action": "FUN", + "fun": { + "action": "THROW_PAPER_PLANE", + "percentageX": percentageX, + "percentageY": percentageY, + }, + "roles": _roles, + "userId": _userId, + })); + } + } + + void animatePaperPlane(double percentageX, double percentageY) { + print("ADD ANIMATION"); + + final RenderBox mainStackBox = + mainStackKey.currentContext!.findRenderObject() as RenderBox; + final RenderBox scoreBoardBox = + scoreboardKey.currentContext!.findRenderObject() as RenderBox; + final mainStackPosition = mainStackBox.localToGlobal(Offset.zero); + final scoreboardPosition = scoreBoardBox.localToGlobal(Offset.zero); + double dX = scoreboardPosition.dx - + mainStackPosition.dx + + percentageX * scoreBoardBox.size.width; + double dY = scoreboardPosition.dy - + mainStackPosition.dy + + percentageY * scoreBoardBox.size.height; + + setState(() { + _animations.insert( + 0, PaperPlane(key: UniqueKey(), clickX: dX, clickY: dY)); + print(_animations.length); + }); + Future.delayed(const Duration(milliseconds: 2500), () { + setState(() { + _animations.removeLast(); + }); + }); + } + List> getResults(Map form) { List elements = form["questions"]; return elements.map((element) { @@ -403,44 +461,67 @@ class _QuizControlPageState extends AuthState { if (_form.status == FormStatus.finished) { return Scaffold( appBar: appBar, - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Text( - "Quiz beendet", - style: Theme.of(context).textTheme.headlineMedium, - ), - Container( - margin: const EdgeInsets.only(top: 30.0, bottom: 10.0), - width: 250, - height: 250, - child: RiveAnimation.asset( - 'assets/animations/rive/animations.riv', - fit: BoxFit.cover, - artboard: 'rigged without bodyparts darker firework', - stateMachines: ['State Machine Winner'], - ), - ), - const SizedBox(height: 16), - Center( - child: Container( - constraints: const BoxConstraints(maxWidth: 800), - child: Card( - child: Padding( - padding: const EdgeInsets.all(8), - child: QuizScoreboard(scoreboard: _scoreboard), + body: Stack( + key: mainStackKey, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Text( + "Quiz beendet", + style: Theme.of(context).textTheme.headlineMedium, + ), + Container( + margin: const EdgeInsets.only(top: 30.0, bottom: 10.0), + width: 250, + height: 250, + child: RiveAnimation.asset( + 'assets/animations/rive/animations.riv', + fit: BoxFit.cover, + artboard: 'rigged without bodyparts darker firework', + stateMachines: ['State Machine Winner'], ), ), - ), + const SizedBox(height: 16), + Center( + child: GestureDetector( + key: scoreboardKey, + onTapUp: (details) { + // get the position of the tap and convert it to a percentage of the total height + final RenderBox box = scoreboardKey.currentContext! + .findRenderObject() as RenderBox; + double x = details.localPosition.dx; + double percentageX = x / box.size.width; + double y = details.localPosition.dy; + double percentageY = y / box.size.height; + throwPaperPlane(percentageX, percentageY); + }, + child: Container( + constraints: const BoxConstraints(maxWidth: 800), + child: Card( + child: Padding( + padding: const EdgeInsets.all(8), + child: QuizScoreboard(scoreboard: _scoreboard), + ), + ), + ), + ), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: resetForm, + child: const Text('Quiz zurücksetzen'), + ), + ], ), - const SizedBox(height: 16), - ElevatedButton( - onPressed: resetForm, - child: const Text('Quiz zurücksetzen'), + ), + IgnorePointer( + child: Stack( + children: _animations, ), - ], - ), + ) + ], ), ); } diff --git a/frontend/lib/pages/test_page.dart b/frontend/lib/pages/test_page.dart index 2963b651..c91fb1e1 100644 --- a/frontend/lib/pages/test_page.dart +++ b/frontend/lib/pages/test_page.dart @@ -11,7 +11,6 @@ class TestPage extends StatefulWidget { class _TestPageState extends State with TickerProviderStateMixin { List _animations = []; - final riveFileName = 'assets/animations/rive/paper_plane.riv'; @override void initState() { From 76468353b7f6eda1f4a9f396553d0349dd59454e Mon Sep 17 00:00:00 2001 From: Johannes Brandenburger <79154528+johannesbrandenburger@users.noreply.github.com> Date: Fri, 12 Apr 2024 18:57:07 +0200 Subject: [PATCH 4/7] send participants to participant --- .../mobilelearning/services/quiz/socket/LiveQuizSocket.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java index 4fa8563c..e6c6431c 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java @@ -270,9 +270,8 @@ private void broadcast(LiveQuizSocketMessage message, String courseId, String fo messageToSend.form.fillQuestionContents(courseRepository.findById(new ObjectId(courseId))); if (connection.getType().equals(SocketConnectionType.PARTICIPANT)) { messageToSend.form.clearResults(); - } else { - messageToSend.form.setParticipants(message.form.getParticipants()); } + messageToSend.form.setParticipants(message.form.getParticipants()); // send the message String messageString = messageToSend.toJson(); From 0e68909f748863074f1e1d9ffec72db2668d0f4d Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 14 Apr 2024 16:59:14 +0200 Subject: [PATCH 5/7] throw stone and dart animation --- frontend/assets/animations/rive/dart.riv | Bin 0 -> 3561 bytes frontend/assets/animations/rive/stone.riv | Bin 0 -> 8178 bytes .../components/animations/paper_plane.dart | 41 -------- frontend/lib/components/animations/throw.dart | 91 ++++++++++++++++++ frontend/lib/pages/quiz/attend_quiz_page.dart | 4 +- .../lib/pages/quiz/quiz_control_page.dart | 6 +- frontend/lib/pages/test_page.dart | 7 +- frontend/pubspec.yaml | 2 + 8 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 frontend/assets/animations/rive/dart.riv create mode 100644 frontend/assets/animations/rive/stone.riv delete mode 100644 frontend/lib/components/animations/paper_plane.dart create mode 100644 frontend/lib/components/animations/throw.dart diff --git a/frontend/assets/animations/rive/dart.riv b/frontend/assets/animations/rive/dart.riv new file mode 100644 index 0000000000000000000000000000000000000000..8119bf919a920fc6896e5bfea25e21d75c36cf67 GIT binary patch literal 3561 zcmai%2~ZPP7{}i;Svh1kSqhp|L0U*htO%&!Sde`QcwjLtM?kFALBKI8$iz5!jEasp z$cSRG-Ur?|v{^Vw^*mn zF0d4#9?GD!0*f`G+)_|hR%~GqJ7&>_R4!wpF%|A9={*LsY_xEUo3N}h5h<7+EGp_P za$h~Mwjwp7Kn$Zqk&IGPv89e=1q`{Il5M*aL{Knu62r$8tUG-8a0gN-19Hq|5d)^1 zt|ywEgmG>^W--E(oT_PqOrm`J&JSY5vB+ImE=9 zSt0kn>b#J*B59Q#@(9$BlXaw%6U=;zbq^mtq>&X6*(0Xf5tDbuY3Ew7&T6$HxVq|WpA%Ejznx;j02A)Sg!{nQx-i!~F|`##?0|^`Oq3H7 z*#Q(UM_3xQUB)f9v zhXgV*X#B_-QTxUH47gLOuu(NR+8eoYJ8Ebs_bz7P!C0 zy<>pl@}W3}nFz)Gb`U9vpbB(P}o z8k-X|*#Vl;4J3J9pmSIk6=idPScE<}#hn|zk0w(HsT66D_z68aZxUVJqbuBLNZ>qu z!DPPHPe?jcXQoH?%tQ%TH#gbH)_@_bJ?6?#;ls<7c^9TZS@KPDVZ4|2oVxW zKY(xn>#D1(JK+7#q*@9JXEJ8>*Wx0=KQvSkHt=|;ox2=bVTZ#|qIyOKi|o%^c*4D2 zK7~MvrEuP6Lw50ra0Jsy_SMY6$&lx4jrdF;+K=Xb>Re>;US#RMNUdF$n8L@Gjw9@- zM;i$D&C3uy(C2y!=XwZ}m%R&HdH#;T-ijxL+kC)GAA@<`!g<|=D&?<#+te=)Hns52 zw?4J84^|!^-2VEVH0(le^+FHz*61}Y>S;YM3VisPCV>_A)DtcvPfOK{z154mtJQ0- zn+czP>a~s4rYGn*gXsuOdMWl+F2x?wy$j9}zM5$k*pgvT@1X02RJvS7y4+nF%v_`g z<}TLR?RI+FN>qXG|0wXkcPtUu%ke>kOEO#NeyyZm(A6@yYIhu6=VQ-mWVAJ2TKR3O zmC@FEX=^6Evhjb=#0wYhBixQX^|bAEGTJ&%t;(@)FZ~A*{_O4-DJ3B-`2|j z)_Vff^8Mc=quu1L4Q4LU{{P_WKU*|r0O5?Rm3DnQ?fMQG$PRZ1UFW0gyJWPxytMLN z-z%ft>!p?NdV`F%!BgAW^}fru+4wc=0K%4jJYUZ(e*Tsnif)tvG|B_yAGanMK$90> zSkhUW+w=P7m%e(g{6qtNb`Hq^4tWB6?E4WJ?GblvFw;i&{hqY%!OUf>>*(l!f6x^S zLLu-!bqEsxrUvT+76ujx77Z2)mH?InrUx4jmJc==tPpGlSSc7hvLW;Yg#b~*DB^HD zhOv({hDl?jG)7BftTZM_W0Eu$#v)WtB&RgC?b4+b$ytfrz3pyZ|KUu|bj$4W5#dNX z6r;Bj)F?a+kEU91cz1_&IQq=71{9te28()CMWn$Z&M}{~sw7RCZ_B7Oz`V*azo#u! z0JPbO?py<(Gz@%^3(hd!H?%{0!%ZF1rQr#V9|^kDey&T%fX*U-tK63%)U}LWO3OM> z|KZB9=AvS$%wPx?mVthg2baQEOL_8W%jr}qT<`-MwT0r^@ dSbLGub`)adA}UUF_nB%Ick-erqamI{ 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 createState() => _PaperPlaneState(); -} - -class _PaperPlaneState extends State { - final riveFileName = 'assets/animations/rive/paper_plane.riv'; - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Positioned( - key: UniqueKey(), - top: widget.clickY - 144, - left: widget.clickX - 602, - child: SizedBox( - width: 700, - height: 1200, - child: RiveAnimation.asset( - riveFileName, - fit: BoxFit.cover, - artboard: 'Artboard', - stateMachines: const ['Hit State Machine'], - ), - ), - ); - } -} diff --git a/frontend/lib/components/animations/throw.dart b/frontend/lib/components/animations/throw.dart new file mode 100644 index 00000000..38113ac0 --- /dev/null +++ b/frontend/lib/components/animations/throw.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:rive/rive.dart'; + +enum ThrowType { + paperPlane( + filename: "paper_plane.riv", + artboard: "Artboard", + stateMachine: "Hit State Machine", + width: 700, + height: 1200, + hitX: 602, + hitY: 144), + stone( + filename: "stone.riv", + artboard: "New Artboard", + stateMachine: "State Machine 1", + width: 700, + height: 500, + hitX: 614, + hitY: 96), + dart( + filename: "dart.riv", + artboard: "New Artboard", + stateMachine: "State Machine 1", + width: 700, + height: 1000, + hitX: 660, + hitY: 41); + // ball("ball.riv"), + + final String filename; + final String artboard; + final String stateMachine; + final double width; + final double height; + final double hitX; + final double hitY; + + const ThrowType( + {required this.filename, + required this.artboard, + required this.stateMachine, + required this.hitX, + required this.width, + required this.height, + required this.hitY}); +} + +class Throw extends StatefulWidget { + final ThrowType throwType; + final double clickX; + final double clickY; + + const Throw( + {required this.throwType, + required this.clickX, + required this.clickY, + super.key}); + + @override + State createState() => _ThrowState(); +} + +class _ThrowState extends State { + final riveDirName = 'assets/animations/rive'; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Positioned( + key: UniqueKey(), + left: widget.clickX - widget.throwType.hitX, + top: widget.clickY - widget.throwType.hitY, + child: SizedBox( + width: widget.throwType.width, + height: widget.throwType.height, + child: RiveAnimation.asset( + "$riveDirName/${widget.throwType.filename}", + fit: BoxFit.cover, + artboard: widget.throwType.artboard, + stateMachines: [widget.throwType.stateMachine], + ), + ), + ); + } +} diff --git a/frontend/lib/pages/quiz/attend_quiz_page.dart b/frontend/lib/pages/quiz/attend_quiz_page.dart index 380eb2c2..b30d58ef 100644 --- a/frontend/lib/pages/quiz/attend_quiz_page.dart +++ b/frontend/lib/pages/quiz/attend_quiz_page.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:frontend/auth_state.dart'; -import 'package:frontend/components/animations/paper_plane.dart'; +import 'package:frontend/components/animations/throw.dart'; import 'package:frontend/components/elements/quiz/single_choice_quiz.dart'; import 'package:frontend/components/elements/quiz/yes_no_quiz.dart'; import 'package:frontend/components/error/general_error_widget.dart'; @@ -351,7 +351,7 @@ class _AttendQuizPageState extends AuthState { setState(() { _animations.insert( - 0, PaperPlane(key: UniqueKey(), clickX: dX, clickY: dY)); + 0, Throw(key: UniqueKey(), throwType: ThrowType.paperPlane, clickX: dX, clickY: dY)); print(_animations.length); }); Future.delayed(const Duration(milliseconds: 2500), () { diff --git a/frontend/lib/pages/quiz/quiz_control_page.dart b/frontend/lib/pages/quiz/quiz_control_page.dart index 8a235d54..e167aa52 100644 --- a/frontend/lib/pages/quiz/quiz_control_page.dart +++ b/frontend/lib/pages/quiz/quiz_control_page.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:frontend/auth_state.dart'; -import 'package:frontend/components/animations/paper_plane.dart'; +import 'package:frontend/components/animations/throw.dart'; import 'package:frontend/enums/form_status.dart'; import 'package:frontend/enums/question_type.dart'; import 'package:frontend/components/elements/quiz/single_choice_quiz_result.dart'; @@ -292,9 +292,11 @@ class _QuizControlPageState extends AuthState { setState(() { _animations.insert( - 0, PaperPlane(key: UniqueKey(), clickX: dX, clickY: dY)); + 0, Throw(key: UniqueKey(), throwType: ThrowType.paperPlane, clickX: dX, clickY: dY)); print(_animations.length); }); + + // todo cancel timer in dispose Future.delayed(const Duration(milliseconds: 2500), () { setState(() { _animations.removeLast(); diff --git a/frontend/lib/pages/test_page.dart b/frontend/lib/pages/test_page.dart index c91fb1e1..d0519f70 100644 --- a/frontend/lib/pages/test_page.dart +++ b/frontend/lib/pages/test_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:frontend/components/animations/paper_plane.dart'; +import 'package:frontend/components/animations/throw.dart'; import 'package:rive/rive.dart'; class TestPage extends StatefulWidget { @@ -33,8 +33,11 @@ class _TestPageState extends State with TickerProviderStateMixin { onTapUp: (details) { print("ADD ANIMATION"); setState(() { - _animations.insert(0, PaperPlane( + _animations.insert(0, Throw( key: UniqueKey(), + // throwType: ThrowType.paperPlane, + // throwType: ThrowType.stone, + throwType: ThrowType.dart, clickX: details.localPosition.dx, clickY: details.localPosition.dy)); print(_animations.length); diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index 49a0f5cc..59447220 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -77,6 +77,8 @@ flutter: - assets/logo/ - assets/animations/rive/animations.riv - assets/animations/rive/paper_plane.riv + - assets/animations/rive/dart.riv + - assets/animations/rive/stone.riv fonts: - family: NunitoSans From e04a54d269f271de610c8f307c7db92bf75126f9 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 14 Apr 2024 21:41:07 +0200 Subject: [PATCH 6/7] fix Future delayed --- frontend/lib/pages/quiz/attend_quiz_page.dart | 2 ++ frontend/lib/pages/quiz/quiz_control_page.dart | 2 +- frontend/lib/pages/test_page.dart | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/lib/pages/quiz/attend_quiz_page.dart b/frontend/lib/pages/quiz/attend_quiz_page.dart index b30d58ef..7f27a1e2 100644 --- a/frontend/lib/pages/quiz/attend_quiz_page.dart +++ b/frontend/lib/pages/quiz/attend_quiz_page.dart @@ -354,7 +354,9 @@ class _AttendQuizPageState extends AuthState { 0, Throw(key: UniqueKey(), throwType: ThrowType.paperPlane, clickX: dX, clickY: dY)); print(_animations.length); }); + Future.delayed(const Duration(milliseconds: 2500), () { + if (!mounted) return; setState(() { _animations.removeLast(); }); diff --git a/frontend/lib/pages/quiz/quiz_control_page.dart b/frontend/lib/pages/quiz/quiz_control_page.dart index e167aa52..407202ab 100644 --- a/frontend/lib/pages/quiz/quiz_control_page.dart +++ b/frontend/lib/pages/quiz/quiz_control_page.dart @@ -296,8 +296,8 @@ class _QuizControlPageState extends AuthState { print(_animations.length); }); - // todo cancel timer in dispose Future.delayed(const Duration(milliseconds: 2500), () { + if (!mounted) return; setState(() { _animations.removeLast(); }); diff --git a/frontend/lib/pages/test_page.dart b/frontend/lib/pages/test_page.dart index d0519f70..d865ed51 100644 --- a/frontend/lib/pages/test_page.dart +++ b/frontend/lib/pages/test_page.dart @@ -43,6 +43,7 @@ class _TestPageState extends State with TickerProviderStateMixin { print(_animations.length); }); Future.delayed(const Duration(milliseconds: 2500), () { + if (!mounted) return; setState(() { _animations.removeLast(); }); From 7e9a228fd4707726d1b33bf150cb0230a00eabbc Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 14 Apr 2024 22:20:03 +0200 Subject: [PATCH 7/7] backend: improve quiz socket fun method --- .../services/quiz/socket/LiveQuizSocket.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java index d242f5b6..4b9203e2 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/services/quiz/socket/LiveQuizSocket.java @@ -290,6 +290,10 @@ private Boolean evaluateMessage(LiveQuizSocketMessage quizSocketMessage, String System.out.println("Action is null"); return false; } + + if (quizSocketMessage.action.equals("FUN")) { + return this.fun(quizSocketMessage, formId); + } // if the user is not an owner or participant, return User user = userRepository.findById(new ObjectId(userId)); @@ -310,10 +314,6 @@ private Boolean evaluateMessage(LiveQuizSocketMessage quizSocketMessage, String return this.next(quizSocketMessage, course, formId, user); } - if (quizSocketMessage.action.equals("FUN")) { - return this.fun(quizSocketMessage, course, formId, user); - } - return false; }; @@ -474,7 +474,7 @@ private Boolean next(LiveQuizSocketMessage quizSocketMessage, Course course, Str return true; } - private Boolean fun(LiveQuizSocketMessage quizSocketMessage, Course course, String formId, User user) { + private Boolean fun(LiveQuizSocketMessage quizSocketMessage, String formId) { System.out.println("Throw paper plane"); @@ -484,6 +484,11 @@ private Boolean fun(LiveQuizSocketMessage quizSocketMessage, Course course, Stri String messageString = quizSocketMessage.toJson(); connections.values().forEach(connection -> { + // check if the form ID match + if (!connection.getFormId().equals(new ObjectId(formId))) { + return; + } + connection.session.getAsyncRemote().sendObject(messageString, result -> { if (result.getException() != null) { System.out.println("Unable to send message: " + result.getException());