Compare commits
4 Commits
3818328afe
...
fe028860e9
Author | SHA1 | Date | |
---|---|---|---|
fe028860e9 | |||
a2d2ce4d38 | |||
167c11b9eb | |||
8cb3933fcc |
@ -166,9 +166,9 @@
|
||||
"postPosted": "Post has been posted.",
|
||||
"postPublishedAt": "Published At",
|
||||
"postPublishedUntil": "Published Until",
|
||||
"postEditingNotice": "You're about to editing a post that posted {}.",
|
||||
"postReplyingNotice": "You're about to reply to a post that posted {}.",
|
||||
"postRepostingNotice": "You're about to repost a post that posted {}.",
|
||||
"postEditingNotice": "You're about to editing a post that posted by {}.",
|
||||
"postReplyingNotice": "You're about to reply to a post that posted by {}.",
|
||||
"postRepostingNotice": "You're about to repost a post that posted by {}.",
|
||||
"postReact": "React",
|
||||
"postReactions": "Reactions of Post",
|
||||
"postReactionUpvote": {
|
||||
|
@ -71,7 +71,7 @@ class ChatMessageController extends ChangeNotifier {
|
||||
resp.data as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
_wsSubscription = _ws.stream.stream.listen((event) {
|
||||
_wsSubscription = _ws.pk.stream.listen((event) {
|
||||
switch (event.method) {
|
||||
case 'events.new':
|
||||
if (event.payload?['channel_id'] != channel?.id) break;
|
||||
|
@ -168,6 +168,7 @@ class PostWriteController extends ChangeNotifier {
|
||||
});
|
||||
contentController.addListener(() {
|
||||
_temporaryPlanSave();
|
||||
notifyListeners();
|
||||
});
|
||||
if (doLoadFromTemporary) _temporaryLoad();
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ class NotificationProvider extends ChangeNotifier {
|
||||
List<SnNotification> notifications = List.empty(growable: true);
|
||||
|
||||
void listen() {
|
||||
_ws.stream.stream.listen((event) {
|
||||
_ws.pk.stream.listen((event) {
|
||||
if (event.method == 'notifications.new') {
|
||||
final notification = SnNotification.fromJson(event.payload!);
|
||||
if (showingCount < 0) showingCount = 0;
|
||||
@ -103,10 +103,10 @@ class NotificationProvider extends ChangeNotifier {
|
||||
|
||||
void updateTray() {
|
||||
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
|
||||
if (notifications.isEmpty) {
|
||||
if (showingTrayCount == 0) {
|
||||
trayManager.setTitle('');
|
||||
} else {
|
||||
trayManager.setTitle(' ${notifications.length.toString()}');
|
||||
trayManager.setTitle(' $showingTrayCount');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,8 @@ class WebSocketProvider extends ChangeNotifier {
|
||||
late final SnNetworkProvider _sn;
|
||||
late final UserProvider _ua;
|
||||
|
||||
StreamController<WebSocketPackage> stream = StreamController.broadcast();
|
||||
StreamController<WebSocketPackage> pk = StreamController.broadcast();
|
||||
Stream<dynamic>? _wsStream;
|
||||
|
||||
WebSocketProvider(BuildContext context) {
|
||||
_sn = context.read<SnNetworkProvider>();
|
||||
@ -36,7 +37,7 @@ class WebSocketProvider extends ChangeNotifier {
|
||||
Completer<void>? _connectCompleter;
|
||||
|
||||
Future<void> connect({noRetry = false}) async {
|
||||
if(_connectCompleter != null) {
|
||||
if (_connectCompleter != null) {
|
||||
await _connectCompleter!.future;
|
||||
_connectCompleter = null;
|
||||
}
|
||||
@ -59,6 +60,7 @@ class WebSocketProvider extends ChangeNotifier {
|
||||
try {
|
||||
conn = WebSocketChannel.connect(uri);
|
||||
await conn!.ready;
|
||||
_wsStream = conn!.stream.asBroadcastStream();
|
||||
listen();
|
||||
log('[WebSocket] Connected to server!');
|
||||
isConnected = true;
|
||||
@ -73,7 +75,7 @@ class WebSocketProvider extends ChangeNotifier {
|
||||
log('Retry connecting to websocket in 3 seconds...');
|
||||
return Future.delayed(
|
||||
const Duration(seconds: 3),
|
||||
() => connect(noRetry: true),
|
||||
() => connect(noRetry: true),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@ -93,11 +95,12 @@ class WebSocketProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void listen() {
|
||||
conn?.stream.listen(
|
||||
if (_wsStream == null) return;
|
||||
_wsStream!.listen(
|
||||
(event) {
|
||||
final packet = WebSocketPackage.fromJson(jsonDecode(event));
|
||||
log('Websocket incoming message: ${packet.method} ${packet.message}');
|
||||
stream.sink.add(packet);
|
||||
pk.sink.add(packet);
|
||||
},
|
||||
onDone: () {
|
||||
isConnected = false;
|
||||
|
@ -155,12 +155,16 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
||||
text: TextSpan(children: [
|
||||
TextSpan(
|
||||
text: 'call'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge!.copyWith(color: Colors.white),
|
||||
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||
color: Theme.of(context).appBarTheme.foregroundColor,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: '\n'),
|
||||
TextSpan(
|
||||
text: call.lastDuration.toString(),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.white),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: Theme.of(context).appBarTheme.foregroundColor,
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
|
@ -206,7 +206,7 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
||||
});
|
||||
|
||||
final ws = context.read<WebSocketProvider>();
|
||||
_wsSubscription = ws.stream.stream.listen((event) {
|
||||
_wsSubscription = ws.pk.stream.listen((event) {
|
||||
switch (event.method) {
|
||||
case 'calls.new':
|
||||
final payload = SnChatCall.fromJson(event.payload!);
|
||||
|
@ -12,13 +12,16 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:pasteboard/pasteboard.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:surface/controllers/post_write_controller.dart';
|
||||
import 'package:surface/providers/config.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
import 'package:surface/types/attachment.dart';
|
||||
import 'package:surface/types/post.dart';
|
||||
import 'package:surface/widgets/account/account_image.dart';
|
||||
import 'package:surface/widgets/loading_indicator.dart';
|
||||
import 'package:surface/widgets/markdown_content.dart';
|
||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||
import 'package:surface/widgets/post/post_item.dart';
|
||||
import 'package:surface/widgets/post/post_media_pending_list.dart';
|
||||
@ -26,8 +29,6 @@ import 'package:surface/widgets/post/post_meta_editor.dart';
|
||||
import 'package:surface/widgets/dialog.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../types/attachment.dart';
|
||||
|
||||
class PostEditorExtra {
|
||||
final String? text;
|
||||
final String? title;
|
||||
@ -124,6 +125,16 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void _showPublisherPopup() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => _PostPublisherPopup(
|
||||
controller: _writeController,
|
||||
publishers: _publishers,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_writeController.dispose();
|
||||
@ -197,174 +208,42 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<SnPublisher>(
|
||||
isExpanded: true,
|
||||
hint: Text(
|
||||
'fieldPostPublisher',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
).tr(),
|
||||
items: <DropdownMenuItem<SnPublisher>>[
|
||||
...(_publishers?.map(
|
||||
(item) => DropdownMenuItem<SnPublisher>(
|
||||
enabled: _writeController.editingPost == null,
|
||||
value: item,
|
||||
child: Row(
|
||||
children: [
|
||||
AccountImage(content: item.avatar, radius: 16),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(item.nick).textStyle(Theme.of(context).textTheme.bodyMedium!),
|
||||
Text('@${item.name}')
|
||||
.textStyle(Theme.of(context).textTheme.bodySmall!)
|
||||
.fontSize(12),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
) ??
|
||||
[]),
|
||||
DropdownMenuItem<SnPublisher>(
|
||||
value: null,
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 16,
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Theme.of(context).colorScheme.onSurface,
|
||||
child: const Icon(Symbols.add),
|
||||
),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('publishersNew').tr().textStyle(Theme.of(context).textTheme.bodyMedium!),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
if (_writeController.editingPost != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 4, bottom: 4, left: 20, right: 20),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||
),
|
||||
),
|
||||
],
|
||||
value: _writeController.publisher,
|
||||
onChanged: (SnPublisher? value) {
|
||||
if (value == null) {
|
||||
GoRouter.of(context).pushNamed('accountPublisherNew').then((value) {
|
||||
if (value == true) {
|
||||
_publishers = null;
|
||||
_fetchPublishers();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
_writeController.setPublisher(value);
|
||||
final config = context.read<ConfigProvider>();
|
||||
config.prefs.setInt('int_last_publisher_id', value.id);
|
||||
}
|
||||
},
|
||||
buttonStyleData: const ButtonStyleData(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
height: 48,
|
||||
),
|
||||
menuItemStyleData: const MenuItemStyleData(
|
||||
height: 48,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.edit, size: 16),
|
||||
const Gap(10),
|
||||
Text('postEditingNotice').tr(args: ['@${_writeController.editingPost!.publisher.name}']),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
padding: EdgeInsets.only(bottom: 160),
|
||||
child: Column(
|
||||
children: [
|
||||
// Replying Notice
|
||||
if (_writeController.replyingPost != null)
|
||||
Column(
|
||||
children: [
|
||||
ExpansionTile(
|
||||
minTileHeight: 48,
|
||||
leading: const Icon(Symbols.reply).padding(left: 4),
|
||||
title: Text('postReplyingNotice')
|
||||
.fontSize(15)
|
||||
.tr(args: ['@${_writeController.replyingPost!.publisher.name}']),
|
||||
children: <Widget>[PostItem(data: _writeController.replyingPost!)],
|
||||
),
|
||||
const Divider(height: 1),
|
||||
],
|
||||
),
|
||||
// Reposting Notice
|
||||
if (_writeController.repostingPost != null)
|
||||
Column(
|
||||
children: [
|
||||
ExpansionTile(
|
||||
minTileHeight: 48,
|
||||
leading: const Icon(Symbols.forward).padding(left: 4),
|
||||
title: Text('postRepostingNotice')
|
||||
.fontSize(15)
|
||||
.tr(args: ['@${_writeController.repostingPost!.publisher.name}']),
|
||||
children: <Widget>[
|
||||
PostItem(
|
||||
data: _writeController.repostingPost!,
|
||||
)
|
||||
],
|
||||
),
|
||||
const Divider(height: 1),
|
||||
],
|
||||
),
|
||||
// Editing Notice
|
||||
if (_writeController.editingPost != null)
|
||||
Column(
|
||||
children: [
|
||||
ExpansionTile(
|
||||
minTileHeight: 48,
|
||||
leading: const Icon(Symbols.edit_note).padding(left: 4),
|
||||
title: Text('postEditingNotice')
|
||||
.fontSize(15)
|
||||
.tr(args: ['@${_writeController.editingPost!.publisher.name}']),
|
||||
children: <Widget>[PostItem(data: _writeController.editingPost!)],
|
||||
),
|
||||
const Divider(height: 1),
|
||||
],
|
||||
),
|
||||
// Content Input Area
|
||||
Container(
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
child: TextField(
|
||||
controller: _writeController.contentController,
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'fieldPostContent'.tr(),
|
||||
hintStyle: TextStyle(fontSize: 14),
|
||||
isCollapsed: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
child: switch (_writeController.mode) {
|
||||
'stories' => _PostStoryEditor(
|
||||
controller: _writeController,
|
||||
onTapPublisher: _showPublisherPopup,
|
||||
),
|
||||
]
|
||||
.expandIndexed(
|
||||
(idx, ele) => [
|
||||
if (idx != 0 || _writeController.isRelatedNull) const Gap(8),
|
||||
ele,
|
||||
],
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
'articles' => _PostArticleEditor(
|
||||
controller: _writeController,
|
||||
onTapPublisher: _showPublisherPopup,
|
||||
),
|
||||
_ => const Placeholder(),
|
||||
},
|
||||
),
|
||||
if (_writeController.attachments.isNotEmpty || _writeController.thumbnail != null)
|
||||
Positioned(
|
||||
@ -508,3 +387,212 @@ class _PostEditorActionScrollBehavior extends MaterialScrollBehavior {
|
||||
PointerDeviceKind.mouse,
|
||||
};
|
||||
}
|
||||
|
||||
class _PostPublisherPopup extends StatelessWidget {
|
||||
final PostWriteController controller;
|
||||
final List<SnPublisher>? publishers;
|
||||
|
||||
const _PostPublisherPopup({required this.controller, this.publishers});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.face, size: 24),
|
||||
const Gap(16),
|
||||
Text('accountPublishers', style: Theme.of(context).textTheme.titleLarge).tr(),
|
||||
],
|
||||
).padding(horizontal: 20, top: 16, bottom: 12),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: publishers?.length ?? 0,
|
||||
itemBuilder: (context, idx) {
|
||||
final publisher = publishers![idx];
|
||||
return ListTile(
|
||||
title: Text(publisher.nick),
|
||||
subtitle: Text('@${publisher.name}'),
|
||||
leading: AccountImage(content: publisher.avatar, radius: 18),
|
||||
onTap: () {
|
||||
controller.setPublisher(publisher);
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PostStoryEditor extends StatelessWidget {
|
||||
final PostWriteController controller;
|
||||
final Function? onTapPublisher;
|
||||
|
||||
const _PostStoryEditor({required this.controller, this.onTapPublisher});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Material(
|
||||
elevation: 1,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
onTapPublisher?.call();
|
||||
},
|
||||
child: AccountImage(
|
||||
content: controller.publisher?.avatar,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller.contentController,
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'fieldPostContent'.tr(),
|
||||
hintStyle: TextStyle(fontSize: 14),
|
||||
isCollapsed: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
).padding(top: 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PostArticleEditor extends StatelessWidget {
|
||||
final PostWriteController controller;
|
||||
final Function? onTapPublisher;
|
||||
|
||||
const _PostArticleEditor({required this.controller, this.onTapPublisher});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final editorWidgets = <Widget>[
|
||||
Material(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
child: InkWell(
|
||||
child: Row(
|
||||
children: [
|
||||
AccountImage(content: controller.publisher?.avatar, radius: 20),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(controller.publisher?.nick ?? 'loading'.tr()).bold(),
|
||||
Text('@${controller.publisher?.name}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 12, vertical: 8),
|
||||
onTap: () {
|
||||
onTapPublisher?.call();
|
||||
},
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
TextField(
|
||||
controller: controller.titleController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'fieldPostTitle'.tr(),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
).padding(horizontal: 16),
|
||||
const Gap(4),
|
||||
TextField(
|
||||
controller: controller.descriptionController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'fieldPostDescription'.tr(),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
maxLines: null,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
).padding(horizontal: 16),
|
||||
const Gap(8),
|
||||
];
|
||||
|
||||
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE)) {
|
||||
return Container(
|
||||
constraints: const BoxConstraints(maxWidth: 640 * 2 + 8),
|
||||
child: Column(
|
||||
children: [
|
||||
...editorWidgets,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller.contentController,
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'fieldPostContent'.tr(),
|
||||
hintStyle: TextStyle(fontSize: 14),
|
||||
isCollapsed: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: MarkdownTextContent(
|
||||
content: controller.contentController.text,
|
||||
).padding(horizontal: 24),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
...editorWidgets,
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
child: TextField(
|
||||
controller: controller.contentController,
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'fieldPostContent'.tr(),
|
||||
hintStyle: TextStyle(fontSize: 14),
|
||||
isCollapsed: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
@ -96,10 +98,14 @@ class _AccountSelectState extends State<AccountSelect> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
).padding(left: 24, right: 24, top: 16, bottom: 16),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.group, size: 24),
|
||||
const Gap(16),
|
||||
Text(widget.title, style: Theme.of(context).textTheme.titleLarge),
|
||||
],
|
||||
).padding(horizontal: 20, top: 16, bottom: 12),
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||
|
@ -336,6 +336,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
||||
: 'fieldChatMessage'.tr(args: [widget.controller.channel?.name ?? 'loading'.tr()]),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
textInputAction: TextInputAction.send,
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
onSubmitted: (_) {
|
||||
if (_isBusy) return;
|
||||
|
@ -833,7 +833,7 @@ class _PostContentHeader extends StatelessWidget {
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => _PostGetInsightSheet(postId: data.id),
|
||||
builder: (context) => _PostGetInsightPopup(postId: data.id),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -1292,16 +1292,16 @@ class _PostAbuseReportDialogState extends State<_PostAbuseReportDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
class _PostGetInsightSheet extends StatefulWidget {
|
||||
class _PostGetInsightPopup extends StatefulWidget {
|
||||
final int postId;
|
||||
|
||||
const _PostGetInsightSheet({required this.postId});
|
||||
const _PostGetInsightPopup({required this.postId});
|
||||
|
||||
@override
|
||||
State<_PostGetInsightSheet> createState() => _PostGetInsightSheetState();
|
||||
State<_PostGetInsightPopup> createState() => _PostGetInsightPopupState();
|
||||
}
|
||||
|
||||
class _PostGetInsightSheetState extends State<_PostGetInsightSheet> {
|
||||
class _PostGetInsightPopupState extends State<_PostGetInsightPopup> {
|
||||
String? _response;
|
||||
String? _thinkingProcess;
|
||||
|
||||
|
@ -292,7 +292,7 @@ class PostMediaPendingList extends StatelessWidget {
|
||||
constraints: const BoxConstraints(maxHeight: 120),
|
||||
child: Row(
|
||||
children: [
|
||||
const Gap(8),
|
||||
const Gap(16),
|
||||
if (thumbnail != null)
|
||||
ContextMenuArea(
|
||||
contextMenu: _createContextMenu(context, -1, thumbnail!),
|
||||
@ -337,15 +337,10 @@ class _PostMediaPendingItem extends StatelessWidget {
|
||||
|
||||
final sn = context.read<SnNetworkProvider>();
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
return Material(
|
||||
elevation: 4,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
child: Row(
|
||||
|
@ -19,6 +19,7 @@ const Map<int, String> kPostVisibilityLevel = {
|
||||
|
||||
class PostMetaEditor extends StatelessWidget {
|
||||
final PostWriteController controller;
|
||||
|
||||
const PostMetaEditor({super.key, required this.controller});
|
||||
|
||||
Future<DateTime?> _selectDate(
|
||||
@ -87,26 +88,14 @@ class PostMetaEditor extends StatelessWidget {
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 8),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: controller.titleController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'fieldPostTitle'.tr(),
|
||||
border: UnderlineInputBorder(),
|
||||
),
|
||||
onTapOutside: (_) =>
|
||||
FocusManager.instance.primaryFocus?.unfocus(),
|
||||
).padding(horizontal: 24),
|
||||
if (controller.mode == 'articles') const Gap(4),
|
||||
if (controller.mode == 'articles')
|
||||
if (controller.mode == 'stories')
|
||||
TextField(
|
||||
controller: controller.descriptionController,
|
||||
maxLines: null,
|
||||
controller: controller.titleController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'fieldPostDescription'.tr(),
|
||||
labelText: 'fieldPostTitle'.tr(),
|
||||
border: UnderlineInputBorder(),
|
||||
),
|
||||
onTapOutside: (_) =>
|
||||
FocusManager.instance.primaryFocus?.unfocus(),
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
).padding(horizontal: 24),
|
||||
const Gap(4),
|
||||
PostTagsField(
|
||||
@ -133,8 +122,7 @@ class PostMetaEditor extends StatelessWidget {
|
||||
helperMaxLines: 2,
|
||||
border: UnderlineInputBorder(),
|
||||
),
|
||||
onTapOutside: (_) =>
|
||||
FocusManager.instance.primaryFocus?.unfocus(),
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
).padding(horizontal: 24),
|
||||
const Gap(12),
|
||||
ListTile(
|
||||
@ -182,8 +170,7 @@ class PostMetaEditor extends StatelessWidget {
|
||||
leading: Icon(Symbols.person),
|
||||
trailing: Icon(Symbols.chevron_right),
|
||||
title: Text('postVisibleUsers').tr(),
|
||||
subtitle: Text('postSelectedUsers')
|
||||
.plural(controller.visibleUsers.length),
|
||||
subtitle: Text('postSelectedUsers').plural(controller.visibleUsers.length),
|
||||
onTap: () {
|
||||
_selectVisibleUser(context);
|
||||
},
|
||||
@ -194,8 +181,7 @@ class PostMetaEditor extends StatelessWidget {
|
||||
leading: Icon(Symbols.person),
|
||||
trailing: Icon(Symbols.chevron_right),
|
||||
title: Text('postInvisibleUsers').tr(),
|
||||
subtitle: Text('postSelectedUsers')
|
||||
.plural(controller.invisibleUsers.length),
|
||||
subtitle: Text('postSelectedUsers').plural(controller.invisibleUsers.length),
|
||||
onTap: () {
|
||||
_selectInvisibleUser(context);
|
||||
},
|
||||
@ -204,9 +190,7 @@ class PostMetaEditor extends StatelessWidget {
|
||||
leading: const Icon(Symbols.event_available),
|
||||
title: Text('postPublishedAt').tr(),
|
||||
subtitle: Text(
|
||||
controller.publishedAt != null
|
||||
? dateFormatter.format(controller.publishedAt!)
|
||||
: 'unset'.tr(),
|
||||
controller.publishedAt != null ? dateFormatter.format(controller.publishedAt!) : 'unset'.tr(),
|
||||
),
|
||||
trailing: controller.publishedAt != null
|
||||
? IconButton(
|
||||
@ -230,9 +214,7 @@ class PostMetaEditor extends StatelessWidget {
|
||||
leading: const Icon(Symbols.event_busy),
|
||||
title: Text('postPublishedUntil').tr(),
|
||||
subtitle: Text(
|
||||
controller.publishedUntil != null
|
||||
? dateFormatter.format(controller.publishedUntil!)
|
||||
: 'unset'.tr(),
|
||||
controller.publishedUntil != null ? dateFormatter.format(controller.publishedUntil!) : 'unset'.tr(),
|
||||
),
|
||||
trailing: controller.publishedUntil != null
|
||||
? IconButton(
|
||||
|
Loading…
x
Reference in New Issue
Block a user