feat: implement cute events

Cute events help against social distancing. You can send googly eyes,
hugs and cuddles.

Fixes:
https://rail.chat/@AgathaSorceress@eldritch.cafe/109336005433123570

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
TheOneWithTheBraid 2022-11-15 11:39:50 +01:00
parent ad185f4a95
commit c07ffaa0c2
9 changed files with 164 additions and 5 deletions

3
.gitignore vendored
View File

@ -14,7 +14,7 @@ lib/generated_plugin_registrant.dart
prime
# libolm package
/assets/js/package/*
/assets/js/package
# IntelliJ related
*.iml
@ -62,4 +62,3 @@ ios/Podfile.lock
/linux/out
/macos/out
.vs
assets/js/package/olm.js

View File

View File

@ -1 +0,0 @@
// Dummy file :-)

View File

@ -103,6 +103,30 @@
"type": "text",
"placeholders": {}
},
"commandHint_googly":"Send some googly eyes",
"commandHint_cuddle": "Send a cuddle",
"commandHint_hug": "Send a hug",
"googlyEyesContent": "{senderName} sends you googly eyes",
"@googlyEyesContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"cuddleContent": "{senderName} cuddles you",
"@cuddleContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"hugContent": "{senderName} hugs you",
"@hugContent": {
"type": "text",
"placeholders": {
"senderName": {}
}
},
"answeredTheCall": "{senderName} answered the call",
"@answeredTheCall": {
"type": "text",

View File

@ -44,6 +44,12 @@ String commandHint(L10n l10n, String command) {
return l10n.commandHint_markasdm;
case 'markasgroup':
return l10n.commandHint_markasgroup;
case 'googly':
return l10n.commandHint_googly;
case 'hug':
return l10n.commandHint_hug;
case 'cuddle':
return l10n.commandHint_cuddle;
default:
return "";
}

View File

@ -0,0 +1,128 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
class CuteContent extends StatefulWidget {
final Event event;
const CuteContent(this.event, {Key? key}) : super(key: key);
@override
State<CuteContent> createState() => _CuteContentState();
}
class _CuteContentState extends State<CuteContent> {
static final List<OverlayEntry> overlays = [];
@override
void initState() {
if (AppConfig.autoplayImages && overlays.isEmpty) {
addOverlay();
}
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<User?>(
future: widget.event.fetchSenderUser(),
builder: (context, snapshot) {
final label = generateLabel(snapshot.data);
return GestureDetector(
onTap: addOverlay,
child: SizedBox.square(
dimension: 300,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.event.text,
style: const TextStyle(fontSize: 150),
),
if (label != null) Text(label)
],
),
),
);
},
);
}
Widget overlayBuilder(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final position = Size(
Random().nextInt(constraints.maxWidth.round() - 64).toDouble(),
Random().nextInt(constraints.maxHeight.round() - 64).toDouble());
return Padding(
padding: EdgeInsets.only(
top: position.height,
left: position.width,
bottom: constraints.maxHeight - 64 - position.height,
right: constraints.maxWidth - 64 - position.width),
child: SizedBox.square(
dimension: 64,
child: GestureDetector(
onTap: removeOverlay,
child: Text(
widget.event.text,
style: const TextStyle(fontSize: 48),
),
),
),
);
});
}
Future<void> addOverlay() async {
await Future.delayed(const Duration(milliseconds: 50));
for (int i = 0; i < 5; i++) {
final overlay = OverlayEntry(
builder: overlayBuilder,
);
Overlay.of(context)?.insert(overlay);
Future.delayed(Duration(seconds: Random().nextInt(35))).then((_) {
overlay.remove();
overlays.remove(overlay);
});
overlays.add(overlay);
}
}
void removeOverlay() {
if (overlays.isEmpty) return;
final overlay = overlays.removeLast();
overlay.remove();
}
generateLabel(User? user) {
switch (widget.event.content['cute_type']) {
case 'googly_eyes':
return L10n.of(context)?.googlyEyesContent(
user?.displayName ??
widget.event.senderFromMemoryOrFallback.displayName ??
'',
);
case 'cuddle':
return L10n.of(context)?.cuddleContent(
user?.displayName ??
widget.event.senderFromMemoryOrFallback.displayName ??
'',
);
case 'hug':
return L10n.of(context)?.hugContent(
user?.displayName ??
widget.event.senderFromMemoryOrFallback.displayName ??
'',
);
}
}
}

View File

@ -14,6 +14,7 @@ import '../../../utils/platform_infos.dart';
import '../../../utils/url_launcher.dart';
import '../../bootstrap/bootstrap_dialog.dart';
import 'audio_player.dart';
import 'cute_events.dart';
import 'html_message.dart';
import 'image_bubble.dart';
import 'map_bubble.dart';
@ -108,6 +109,8 @@ class MessageContent extends StatelessWidget {
case MessageTypes.Sticker:
if (event.redacted) continue textmessage;
return Sticker(event);
case CuteEventContent.eventType:
return CuteContent(event);
case MessageTypes.Audio:
if (PlatformInfos.isMobile || PlatformInfos.isMacOS) {
return AudioPlayerWidget(

View File

@ -956,7 +956,7 @@ packages:
name: matrix
url: "https://pub.dartlang.org"
source: hosted
version: "0.15.2"
version: "0.15.4"
matrix_api_lite:
dependency: transitive
description:

View File

@ -60,7 +60,7 @@ dependencies:
just_audio: ^0.9.20
keyboard_shortcuts: ^0.1.4
latlong2: ^0.8.1
matrix: ^0.15.2
matrix: ^0.15.4
matrix_homeserver_recommendations: ^0.3.0
matrix_link_text: ^1.0.2
native_imaging: ^0.1.0