mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-27 23:09:35 +01:00
chore: Add replies and hold to show
This commit is contained in:
parent
71a2ff5170
commit
010607112d
@ -2668,5 +2668,6 @@
|
||||
"whoCanSeeMyStories": "Who can see my stories?",
|
||||
"unsubscribeStories": "Unsubscribe stories",
|
||||
"thisUserHasNotPostedAnythingYet": "This user has not posted anything in their story yet",
|
||||
"yourStory": "Your story"
|
||||
"yourStory": "Your story",
|
||||
"replyHasBeenSent": "Reply has been sent"
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
@ -32,12 +33,60 @@ class StoryPageController extends State<StoryPage> {
|
||||
Timer? _progressTimer;
|
||||
bool loadingMode = false;
|
||||
|
||||
final TextEditingController replyController = TextEditingController();
|
||||
final FocusNode replyFocus = FocusNode();
|
||||
|
||||
final List<Event> events = [];
|
||||
|
||||
Timeline? timeline;
|
||||
|
||||
Event? get currentEvent => index < events.length ? events[index] : null;
|
||||
|
||||
bool replyLoading = false;
|
||||
|
||||
void replyEmojiAction() async {
|
||||
if (replyLoading) return;
|
||||
hold();
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => EmojiPicker(
|
||||
onEmojiSelected: (c, e) {
|
||||
Navigator.of(context).pop();
|
||||
replyAction(e.emoji);
|
||||
},
|
||||
),
|
||||
);
|
||||
unhold();
|
||||
}
|
||||
|
||||
void replyAction([String? message]) async {
|
||||
message ??= replyController.text;
|
||||
setState(() {
|
||||
replyLoading = true;
|
||||
});
|
||||
try {
|
||||
final client = Matrix.of(context).client;
|
||||
final roomId = await client.startDirectChat(currentEvent!.senderId);
|
||||
await client.getRoomById(roomId)!.sendTextEvent(
|
||||
message,
|
||||
inReplyTo: currentEvent!,
|
||||
);
|
||||
replyController.clear();
|
||||
replyFocus.unfocus();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context)!.replyHasBeenSent),
|
||||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to reply to story', e, s);
|
||||
} finally {
|
||||
setState(() {
|
||||
replyLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<User> get currentSeenByUsers {
|
||||
final timeline = this.timeline;
|
||||
final currentEvent = this.currentEvent;
|
||||
@ -88,8 +137,6 @@ class StoryPageController extends State<StoryPage> {
|
||||
);
|
||||
}
|
||||
|
||||
final TextEditingController replyController = TextEditingController();
|
||||
|
||||
static const Duration _step = Duration(milliseconds: 50);
|
||||
static const Duration maxProgress = Duration(seconds: 5);
|
||||
|
||||
@ -145,13 +192,19 @@ class StoryPageController extends State<StoryPage> {
|
||||
|
||||
DateTime _holdedAt = DateTime.fromMicrosecondsSinceEpoch(0);
|
||||
|
||||
void hold(_) {
|
||||
bool isHold = false;
|
||||
|
||||
void hold([_]) {
|
||||
_holdedAt = DateTime.now();
|
||||
if (loadingMode) return;
|
||||
_progressTimer?.cancel();
|
||||
setState(() {
|
||||
isHold = true;
|
||||
});
|
||||
}
|
||||
|
||||
void unhold([_]) {
|
||||
isHold = false;
|
||||
if (DateTime.now().millisecondsSinceEpoch -
|
||||
_holdedAt.millisecondsSinceEpoch <
|
||||
200) {
|
||||
|
@ -26,39 +26,47 @@ class StoryView extends StatelessWidget {
|
||||
backgroundColor: Colors.blueGrey,
|
||||
appBar: AppBar(
|
||||
titleSpacing: 0,
|
||||
title: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
controller.title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: Colors.black,
|
||||
offset: Offset(0, 0),
|
||||
blurRadius: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
subtitle: currentEvent != null
|
||||
? Text(
|
||||
currentEvent.originServerTs.localizedTime(context),
|
||||
style: const TextStyle(
|
||||
color: Colors.white70,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: Colors.black,
|
||||
offset: Offset(0, 0),
|
||||
blurRadius: 5,
|
||||
),
|
||||
],
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
title: AnimatedOpacity(
|
||||
duration: const Duration(seconds: 1),
|
||||
opacity: controller.isHold ? 0 : 1,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
controller.title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: Colors.black,
|
||||
offset: Offset(0, 0),
|
||||
blurRadius: 5,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
leading: Avatar(
|
||||
mxContent: controller.avatar,
|
||||
name: controller.title,
|
||||
],
|
||||
),
|
||||
),
|
||||
subtitle: currentEvent != null
|
||||
? Text(
|
||||
currentEvent.originServerTs.localizedTime(context),
|
||||
style: const TextStyle(
|
||||
color: Colors.white70,
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: Colors.black,
|
||||
offset: Offset(0, 0),
|
||||
blurRadius: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: null,
|
||||
leading: Avatar(
|
||||
mxContent: controller.avatar,
|
||||
name: controller.title,
|
||||
),
|
||||
),
|
||||
),
|
||||
systemOverlayStyle: SystemUiOverlayStyle.light,
|
||||
@ -199,56 +207,98 @@ class StoryView extends StatelessWidget {
|
||||
top: 4,
|
||||
left: 4,
|
||||
right: 4,
|
||||
child: SafeArea(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (var i = 0; i < events.length; i++)
|
||||
Expanded(
|
||||
child: i == controller.index
|
||||
? LinearProgressIndicator(
|
||||
color: Colors.white,
|
||||
minHeight: 2,
|
||||
backgroundColor: Colors.grey.shade600,
|
||||
value: controller.loadingMode
|
||||
? null
|
||||
: controller.progress.inMilliseconds /
|
||||
StoryPageController
|
||||
.maxProgress.inMilliseconds,
|
||||
)
|
||||
: Container(
|
||||
margin: const EdgeInsets.all(4),
|
||||
height: 2,
|
||||
color: i < controller.index
|
||||
? Colors.white
|
||||
: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(seconds: 1),
|
||||
opacity: controller.isHold ? 0 : 1,
|
||||
child: SafeArea(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (var i = 0; i < events.length; i++)
|
||||
Expanded(
|
||||
child: i == controller.index
|
||||
? LinearProgressIndicator(
|
||||
color: Colors.white,
|
||||
minHeight: 2,
|
||||
backgroundColor: Colors.grey.shade600,
|
||||
value: controller.loadingMode
|
||||
? null
|
||||
: controller.progress.inMilliseconds /
|
||||
StoryPageController
|
||||
.maxProgress.inMilliseconds,
|
||||
)
|
||||
: Container(
|
||||
margin: const EdgeInsets.all(4),
|
||||
height: 2,
|
||||
color: i < controller.index
|
||||
? Colors.white
|
||||
: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!controller.isOwnStory && currentEvent != null)
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(seconds: 1),
|
||||
opacity: controller.isHold ? 0 : 1,
|
||||
child: SafeArea(
|
||||
child: TextField(
|
||||
onTap: controller.hold,
|
||||
onEditingComplete: controller.unhold,
|
||||
focusNode: controller.replyFocus,
|
||||
controller: controller.replyController,
|
||||
minLines: 1,
|
||||
maxLines: 7,
|
||||
onSubmitted: controller.replyAction,
|
||||
textInputAction: TextInputAction.newline,
|
||||
readOnly: controller.replyLoading,
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context)!.reply,
|
||||
prefixIcon: IconButton(
|
||||
onPressed: controller.replyEmojiAction,
|
||||
icon: const Icon(Icons.emoji_emotions_outlined),
|
||||
),
|
||||
suffixIcon: controller.replyLoading
|
||||
? const CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2)
|
||||
: IconButton(
|
||||
onPressed: controller.replyAction,
|
||||
icon: const Icon(Icons.send_outlined),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (controller.isOwnStory &&
|
||||
controller.currentSeenByUsers.isNotEmpty)
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: controller.displaySeenByUsers,
|
||||
icon: const Icon(
|
||||
Icons.visibility_outlined,
|
||||
color: Colors.white70,
|
||||
),
|
||||
label: Text(
|
||||
controller.seenByUsersTitle,
|
||||
style: const TextStyle(color: Colors.white70),
|
||||
),
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: controller.displaySeenByUsers,
|
||||
icon: const Icon(
|
||||
Icons.visibility_outlined,
|
||||
color: Colors.white70,
|
||||
),
|
||||
label: Text(
|
||||
controller.seenByUsersTitle,
|
||||
style: const TextStyle(color: Colors.white70),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user