Compare commits
7 Commits
c91cf7c813
...
7d56c5ef31
Author | SHA1 | Date | |
---|---|---|---|
7d56c5ef31 | |||
c2df1af16d | |||
a8143c6453 | |||
04065061e0 | |||
226eb452e5 | |||
a6715b0872 | |||
43e3404dbb |
@ -15,7 +15,7 @@ body:json {
|
|||||||
"client_id": "alphabot",
|
"client_id": "alphabot",
|
||||||
"client_secret": "_uR0sVnHTh",
|
"client_secret": "_uR0sVnHTh",
|
||||||
"remark": "新年红包",
|
"remark": "新年红包",
|
||||||
"amount": 9705,
|
"amount": 150,
|
||||||
"payee_id": 2
|
"payee_id": 18
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -638,5 +638,17 @@
|
|||||||
"pollVotes": {
|
"pollVotes": {
|
||||||
"one": "{} vote",
|
"one": "{} vote",
|
||||||
"other": "{} votes"
|
"other": "{} votes"
|
||||||
}
|
},
|
||||||
|
"publisherDelete": "Delete Publisher {}",
|
||||||
|
"publisherDeleteDescription": "Are you sure you want to delete this publisher? This operation is irreversible.",
|
||||||
|
"channelIsPublic": "Public Channel",
|
||||||
|
"channelIsPublicDescription": "The channel is public, anyone can join.",
|
||||||
|
"channelIsCommunity": "Community Channel",
|
||||||
|
"channelIsCommunityDescription": "Currently, community channel has nothing special yet.",
|
||||||
|
"realmIsPublic": "Public Realm",
|
||||||
|
"realmIsPublicDescription": "The realm is public, anyone can join.",
|
||||||
|
"realmIsCommunity": "Community Realm",
|
||||||
|
"realmIsCommunityDescription": "Community realm will be displayed on the discover page.",
|
||||||
|
"realmLeave": "Leave Realm",
|
||||||
|
"realmLeaveDescription": "Leave the current realm and delete the realm's identity."
|
||||||
}
|
}
|
||||||
|
@ -637,5 +637,17 @@
|
|||||||
"pollVotes": {
|
"pollVotes": {
|
||||||
"one": "{} 票",
|
"one": "{} 票",
|
||||||
"other": "{} 票"
|
"other": "{} 票"
|
||||||
}
|
},
|
||||||
|
"publisherDelete": "删除发布者 {}",
|
||||||
|
"publisherDeleteDescription": "你确定要删除这个发布者吗?该操作不可撤销。",
|
||||||
|
"channelIsPublic": "公开频道",
|
||||||
|
"channelIsPublicDescription": "该频道是公开的,任何人都可以加入。",
|
||||||
|
"channelIsCommunity": "社区频道",
|
||||||
|
"channelIsCommunityDescription": "目前来说,社区频道还没有什么特别之处。",
|
||||||
|
"realmIsPublic": "公开领域",
|
||||||
|
"realmIsPublicDescription": "该领域是公开的,任何人都可以加入。",
|
||||||
|
"realmIsCommunity": "社区领域",
|
||||||
|
"realmIsCommunityDescription": "社区领域会显示在发现页面上。",
|
||||||
|
"realmLeave": "离开领域",
|
||||||
|
"realmLeaveDescription": "离开当前领域,并且删除领域中的身份。"
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,33 @@ class _PublisherScreenState extends State<PublisherScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _deletePublisher(SnPublisher publisher) async {
|
||||||
|
final confirm = await context.showConfirmDialog(
|
||||||
|
'publisherDelete'.tr(args: ['#${publisher.name}']),
|
||||||
|
'publisherDeleteDescription'.tr(),
|
||||||
|
);
|
||||||
|
if (!confirm) return;
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await context
|
||||||
|
.read<SnNetworkProvider>()
|
||||||
|
.client
|
||||||
|
.delete('/cgi/co/publishers/${publisher.name}');
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showSnackbar('publisherDeleted'.tr(args: ['#${publisher.name}']));
|
||||||
|
_publishers.remove(publisher);
|
||||||
|
_fetchPublishers();
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -118,6 +145,18 @@ class _PublisherScreenState extends State<PublisherScreen> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.delete),
|
||||||
|
const Gap(16),
|
||||||
|
Text('delete').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_deletePublisher(publisher);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -37,6 +37,9 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
|
|
||||||
SnChannel? _editingChannel;
|
SnChannel? _editingChannel;
|
||||||
|
|
||||||
|
bool _isPublic = false;
|
||||||
|
bool _isCommunity = false;
|
||||||
|
|
||||||
Future<void> _fetchRealms() async {
|
Future<void> _fetchRealms() async {
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
try {
|
try {
|
||||||
@ -67,6 +70,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
_aliasController.text = _editingChannel!.alias;
|
_aliasController.text = _editingChannel!.alias;
|
||||||
_nameController.text = _editingChannel!.name;
|
_nameController.text = _editingChannel!.name;
|
||||||
_descriptionController.text = _editingChannel!.description;
|
_descriptionController.text = _editingChannel!.description;
|
||||||
|
_isPublic = _editingChannel!.isPublic;
|
||||||
|
_isCommunity = _editingChannel!.isCommunity;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -88,6 +93,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
: uuid.v4().replaceAll('-', '').substring(0, 12),
|
: uuid.v4().replaceAll('-', '').substring(0, 12),
|
||||||
'name': _nameController.text,
|
'name': _nameController.text,
|
||||||
'description': _descriptionController.text,
|
'description': _descriptionController.text,
|
||||||
|
'is_public': _isPublic,
|
||||||
|
'is_community': _isCommunity,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -271,6 +278,23 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
|
CheckboxListTile(
|
||||||
|
value: _isPublic,
|
||||||
|
title: Text('channelIsPublic'.tr()),
|
||||||
|
subtitle: Text('channelIsPublicDescription'.tr()),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _isPublic = value ?? false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
CheckboxListTile(
|
||||||
|
value: _isCommunity,
|
||||||
|
title: Text('channelIsCommunity'.tr()),
|
||||||
|
subtitle: Text('channelIsCommunityDescription'.tr()),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _isCommunity = value ?? false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
@ -91,9 +91,8 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
|
||||||
);
|
);
|
||||||
final beforeId = config.prefs.getInt('int_last_publisher_id');
|
final beforeId = config.prefs.getInt('int_last_publisher_id');
|
||||||
_writeController.setPublisher(
|
_writeController
|
||||||
_publishers?.where((ele) => ele.id == beforeId).firstOrNull ??
|
.setPublisher(_publishers?.where((ele) => ele.id == beforeId).firstOrNull ?? _publishers?.firstOrNull);
|
||||||
_publishers?.firstOrNull);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -138,6 +137,9 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
builder: (context) => _PostPublisherPopup(
|
builder: (context) => _PostPublisherPopup(
|
||||||
controller: _writeController,
|
controller: _writeController,
|
||||||
publishers: _publishers,
|
publishers: _publishers,
|
||||||
|
onUpdate: () {
|
||||||
|
_fetchPublishers();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -162,8 +164,9 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_writeController.dispose();
|
_writeController.dispose();
|
||||||
if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS))
|
if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS)) {
|
||||||
hotKeyManager.unregister(_pasteHotKey);
|
hotKeyManager.unregister(_pasteHotKey);
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,8 +190,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
if (widget.extraProps != null) {
|
if (widget.extraProps != null) {
|
||||||
_writeController.contentController.text = widget.extraProps!.text ?? '';
|
_writeController.contentController.text = widget.extraProps!.text ?? '';
|
||||||
_writeController.titleController.text = widget.extraProps!.title ?? '';
|
_writeController.titleController.text = widget.extraProps!.title ?? '';
|
||||||
_writeController.descriptionController.text =
|
_writeController.descriptionController.text = widget.extraProps!.description ?? '';
|
||||||
widget.extraProps!.description ?? '';
|
|
||||||
_writeController.addAttachments(widget.extraProps!.attachments ?? []);
|
_writeController.addAttachments(widget.extraProps!.attachments ?? []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,9 +211,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
text: TextSpan(children: [
|
text: TextSpan(children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: _writeController.title.isNotEmpty
|
text: _writeController.title.isNotEmpty ? _writeController.title : 'untitled'.tr(),
|
||||||
? _writeController.title
|
|
||||||
: 'untitled'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||||
color: Theme.of(context).appBarTheme.foregroundColor!,
|
color: Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
),
|
),
|
||||||
@ -238,8 +238,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
children: [
|
children: [
|
||||||
if (_writeController.editingPost != null)
|
if (_writeController.editingPost != null)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(top: 4, bottom: 4, left: 20, right: 20),
|
||||||
top: 4, bottom: 4, left: 20, right: 20),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
@ -253,9 +252,63 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.edit, size: 16),
|
const Icon(Icons.edit, size: 16),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
Text('postEditingNotice').tr(args: [
|
Text('postEditingNotice').tr(args: ['@${_writeController.editingPost!.publisher.name}']),
|
||||||
'@${_writeController.editingPost!.publisher.name}'
|
],
|
||||||
]),
|
),
|
||||||
|
),
|
||||||
|
if (_writeController.replyingPost != 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.reply, size: 16),
|
||||||
|
const Gap(10),
|
||||||
|
Text('@${_writeController.replyingPost!.publisher.name}').bold(),
|
||||||
|
const Gap(4),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_writeController.replyingPost!.body['content'],
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_writeController.repostingPost != 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.forward, size: 16),
|
||||||
|
const Gap(10),
|
||||||
|
Text('@${_writeController.repostingPost!.publisher.name}').bold(),
|
||||||
|
const Gap(4),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_writeController.repostingPost!.body['content'],
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -285,8 +338,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
})
|
})
|
||||||
.padding(top: 8),
|
.padding(top: 8),
|
||||||
),
|
),
|
||||||
if (_writeController.attachments.isNotEmpty ||
|
if (_writeController.attachments.isNotEmpty || _writeController.thumbnail != null)
|
||||||
_writeController.thumbnail != null)
|
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
@ -296,8 +348,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
attachments: _writeController.attachments,
|
attachments: _writeController.attachments,
|
||||||
isBusy: _writeController.isBusy,
|
isBusy: _writeController.isBusy,
|
||||||
onUpload: (int idx) async {
|
onUpload: (int idx) async {
|
||||||
await _writeController.uploadSingleAttachment(
|
await _writeController.uploadSingleAttachment(context, idx);
|
||||||
context, idx);
|
|
||||||
},
|
},
|
||||||
onPostSetThumbnail: (int? idx) {
|
onPostSetThumbnail: (int? idx) {
|
||||||
_writeController.setThumbnail(idx);
|
_writeController.setThumbnail(idx);
|
||||||
@ -306,12 +357,10 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
_writeController.contentController.text +=
|
_writeController.contentController.text +=
|
||||||
'\n';
|
'\n';
|
||||||
},
|
},
|
||||||
onUpdate:
|
onUpdate: (int idx, PostWriteMedia updatedMedia) async {
|
||||||
(int idx, PostWriteMedia updatedMedia) async {
|
|
||||||
_writeController.setIsBusy(true);
|
_writeController.setIsBusy(true);
|
||||||
try {
|
try {
|
||||||
_writeController.setAttachmentAt(
|
_writeController.setAttachmentAt(idx, updatedMedia);
|
||||||
idx, updatedMedia);
|
|
||||||
} finally {
|
} finally {
|
||||||
_writeController.setIsBusy(false);
|
_writeController.setIsBusy(false);
|
||||||
}
|
}
|
||||||
@ -324,8 +373,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
_writeController.setIsBusy(false);
|
_writeController.setIsBusy(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onUpdateBusy: (state) =>
|
onUpdateBusy: (state) => _writeController.setIsBusy(state),
|
||||||
_writeController.setIsBusy(state),
|
|
||||||
).padding(bottom: 8),
|
).padding(bottom: 8),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -336,13 +384,11 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (_writeController.isBusy &&
|
if (_writeController.isBusy && _writeController.progress != null)
|
||||||
_writeController.progress != null)
|
|
||||||
TweenAnimationBuilder<double>(
|
TweenAnimationBuilder<double>(
|
||||||
tween: Tween(begin: 0, end: _writeController.progress),
|
tween: Tween(begin: 0, end: _writeController.progress),
|
||||||
duration: Duration(milliseconds: 300),
|
duration: Duration(milliseconds: 300),
|
||||||
builder: (context, value, _) =>
|
builder: (context, value, _) => LinearProgressIndicator(value: value, minHeight: 2),
|
||||||
LinearProgressIndicator(value: value, minHeight: 2),
|
|
||||||
)
|
)
|
||||||
else if (_writeController.isBusy)
|
else if (_writeController.isBusy)
|
||||||
const LinearProgressIndicator(value: null, minHeight: 2),
|
const LinearProgressIndicator(value: null, minHeight: 2),
|
||||||
@ -351,14 +397,12 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
Container(
|
Container(
|
||||||
child: _writeController.temporaryRestored
|
child: _writeController.temporaryRestored
|
||||||
? Container(
|
? Container(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(top: 4, bottom: 4, left: 28, right: 22),
|
||||||
top: 4, bottom: 4, left: 28, right: 22),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
color: Theme.of(context).dividerColor,
|
color: Theme.of(context).dividerColor,
|
||||||
width: 1 /
|
width: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||||
MediaQuery.of(context).devicePixelRatio,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -367,9 +411,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.restore, size: 20),
|
const Icon(Icons.restore, size: 20),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Expanded(
|
Expanded(child: Text('postLocalDraftRestored').tr()),
|
||||||
child:
|
|
||||||
Text('postLocalDraftRestored').tr()),
|
|
||||||
InkWell(
|
InkWell(
|
||||||
child: Text('dialogDismiss').tr(),
|
child: Text('dialogDismiss').tr(),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -380,10 +422,8 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
))
|
))
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
)
|
)
|
||||||
.height(_writeController.temporaryRestored ? 32 : 0,
|
.height(_writeController.temporaryRestored ? 32 : 0, animate: true)
|
||||||
animate: true)
|
.animate(const Duration(milliseconds: 300), Curves.fastLinearToSlowEaseIn),
|
||||||
.animate(const Duration(milliseconds: 300),
|
|
||||||
Curves.fastLinearToSlowEaseIn),
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -403,18 +443,11 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
),
|
),
|
||||||
if (_writeController.mode == 'stories')
|
if (_writeController.mode == 'stories')
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Symbols.poll,
|
icon: Icon(Symbols.poll, color: Theme.of(context).colorScheme.primary),
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary),
|
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor:
|
backgroundColor: _writeController.poll == null
|
||||||
_writeController.poll == null
|
? null
|
||||||
? null
|
: WidgetStatePropertyAll(Theme.of(context).colorScheme.surfaceContainer),
|
||||||
: WidgetStatePropertyAll(
|
|
||||||
Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.surfaceContainer),
|
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_showPollEditorDialog();
|
_showPollEditorDialog();
|
||||||
@ -426,8 +459,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: (_writeController.isBusy ||
|
onPressed: (_writeController.isBusy || _writeController.publisher == null)
|
||||||
_writeController.publisher == null)
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
_writeController.sendPost(context).then((_) {
|
_writeController.sendPost(context).then((_) {
|
||||||
@ -464,8 +496,9 @@ class _PostEditorActionScrollBehavior extends MaterialScrollBehavior {
|
|||||||
class _PostPublisherPopup extends StatelessWidget {
|
class _PostPublisherPopup extends StatelessWidget {
|
||||||
final PostWriteController controller;
|
final PostWriteController controller;
|
||||||
final List<SnPublisher>? publishers;
|
final List<SnPublisher>? publishers;
|
||||||
|
final Function onUpdate;
|
||||||
|
|
||||||
const _PostPublisherPopup({required this.controller, this.publishers});
|
const _PostPublisherPopup({required this.controller, this.publishers, required this.onUpdate});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -477,9 +510,7 @@ class _PostPublisherPopup extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Symbols.face, size: 24),
|
const Icon(Symbols.face, size: 24),
|
||||||
const Gap(16),
|
const Gap(16),
|
||||||
Text('accountPublishers',
|
Text('accountPublishers', style: Theme.of(context).textTheme.titleLarge).tr(),
|
||||||
style: Theme.of(context).textTheme.titleLarge)
|
|
||||||
.tr(),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 20, top: 16, bottom: 12),
|
).padding(horizontal: 20, top: 16, bottom: 12),
|
||||||
ListTile(
|
ListTile(
|
||||||
@ -488,7 +519,11 @@ class _PostPublisherPopup extends StatelessWidget {
|
|||||||
subtitle: Text('publisherNewSubtitle').tr(),
|
subtitle: Text('publisherNewSubtitle').tr(),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed('accountPublisherNew');
|
GoRouter.of(context).pushNamed('accountPublisherNew').then((value) {
|
||||||
|
if (value == true) {
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
@ -551,8 +586,7 @@ class _PostStoryEditor extends StatelessWidget {
|
|||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
).padding(horizontal: 16),
|
).padding(horizontal: 16),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
TextField(
|
TextField(
|
||||||
@ -567,8 +601,7 @@ class _PostStoryEditor extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -658,8 +691,7 @@ class _PostArticleEditor extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@ -738,8 +770,7 @@ class _PostQuestionEditor extends StatelessWidget {
|
|||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
).padding(horizontal: 16),
|
).padding(horizontal: 16),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
TextField(
|
TextField(
|
||||||
@ -750,8 +781,7 @@ class _PostQuestionEditor extends StatelessWidget {
|
|||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
isCollapsed: true,
|
isCollapsed: true,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
).padding(horizontal: 16),
|
).padding(horizontal: 16),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
TextField(
|
TextField(
|
||||||
@ -766,8 +796,7 @@ class _PostQuestionEditor extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) =>
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -803,8 +832,7 @@ class _PostVideoEditor extends StatelessWidget {
|
|||||||
|
|
||||||
final result = await showDialog<SnAttachment?>(
|
final result = await showDialog<SnAttachment?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => PendingAttachmentAltDialog(
|
builder: (context) => PendingAttachmentAltDialog(media: PostWriteMedia(controller.videoAttachment)),
|
||||||
media: PostWriteMedia(controller.videoAttachment)),
|
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
|
|
||||||
@ -816,8 +844,7 @@ class _PostVideoEditor extends StatelessWidget {
|
|||||||
|
|
||||||
final result = await showDialog<SnAttachmentBoost?>(
|
final result = await showDialog<SnAttachmentBoost?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => PendingAttachmentBoostDialog(
|
builder: (context) => PendingAttachmentBoostDialog(media: PostWriteMedia(controller.videoAttachment)),
|
||||||
media: PostWriteMedia(controller.videoAttachment)),
|
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
|
|
||||||
@ -860,8 +887,7 @@ class _PostVideoEditor extends StatelessWidget {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
await sn.client
|
await sn.client.delete('/cgi/uc/attachments/${controller.videoAttachment!.id}');
|
||||||
.delete('/cgi/uc/attachments/${controller.videoAttachment!.id}');
|
|
||||||
controller.setVideoAttachment(null);
|
controller.setVideoAttachment(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
@ -953,8 +979,7 @@ class _PostVideoEditor extends StatelessWidget {
|
|||||||
label: 'attachmentCopyRandomId'.tr(),
|
label: 'attachmentCopyRandomId'.tr(),
|
||||||
icon: Symbols.content_copy,
|
icon: Symbols.content_copy,
|
||||||
onSelected: () {
|
onSelected: () {
|
||||||
Clipboard.setData(
|
Clipboard.setData(ClipboardData(text: controller.videoAttachment!.rid));
|
||||||
ClipboardData(text: controller.videoAttachment!.rid));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuItem(
|
MenuItem(
|
||||||
@ -973,9 +998,7 @@ class _PostVideoEditor extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
onTap: controller.videoAttachment == null
|
onTap: controller.videoAttachment == null ? () => _selectVideo(context) : null,
|
||||||
? () => _selectVideo(context)
|
|
||||||
: null,
|
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
child: controller.videoAttachment == null
|
child: controller.videoAttachment == null
|
||||||
|
@ -50,6 +50,8 @@ class _RealmManageScreenState extends State<RealmManageScreen> {
|
|||||||
_aliasController.text = out.alias;
|
_aliasController.text = out.alias;
|
||||||
_nameController.text = out.name;
|
_nameController.text = out.name;
|
||||||
_descriptionController.text = out.description;
|
_descriptionController.text = out.description;
|
||||||
|
_isPublic = out.isPublic;
|
||||||
|
_isCommunity = out.isCommunity;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
if (context.mounted) context.showErrorDialog(err);
|
if (context.mounted) context.showErrorDialog(err);
|
||||||
@ -67,6 +69,9 @@ class _RealmManageScreenState extends State<RealmManageScreen> {
|
|||||||
|
|
||||||
final _imagePicker = ImagePicker();
|
final _imagePicker = ImagePicker();
|
||||||
|
|
||||||
|
bool _isPublic = false;
|
||||||
|
bool _isCommunity = false;
|
||||||
|
|
||||||
Future<void> _updateImage(String place) async {
|
Future<void> _updateImage(String place) async {
|
||||||
final image = await _imagePicker.pickImage(source: ImageSource.gallery);
|
final image = await _imagePicker.pickImage(source: ImageSource.gallery);
|
||||||
if (image == null) return;
|
if (image == null) return;
|
||||||
@ -138,6 +143,8 @@ class _RealmManageScreenState extends State<RealmManageScreen> {
|
|||||||
'description': _descriptionController.text,
|
'description': _descriptionController.text,
|
||||||
'avatar': _avatar,
|
'avatar': _avatar,
|
||||||
'banner': _banner,
|
'banner': _banner,
|
||||||
|
'is_public': _isPublic,
|
||||||
|
'is_community': _isCommunity,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -293,6 +300,23 @@ class _RealmManageScreenState extends State<RealmManageScreen> {
|
|||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
|
CheckboxListTile(
|
||||||
|
value: _isPublic,
|
||||||
|
title: Text('realmIsPublic'.tr()),
|
||||||
|
subtitle: Text('realmIsPublicDescription'.tr()),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _isPublic = value ?? false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
CheckboxListTile(
|
||||||
|
value: _isCommunity,
|
||||||
|
title: Text('realmIsCommunity'.tr()),
|
||||||
|
subtitle: Text('realmIsCommunityDescription'.tr()),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _isCommunity = value ?? false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
@ -357,6 +357,28 @@ class _RealmSettingsWidgetState extends State<_RealmSettingsWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _leaveRealm() async {
|
||||||
|
final confirm = await context.showConfirmDialog(
|
||||||
|
'realmLeave'.tr(),
|
||||||
|
'realmLeaveDescription'.tr(),
|
||||||
|
);
|
||||||
|
if (!confirm) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sn.client.delete('/cgi/id/realms/${widget.realm!.alias}/members/me');
|
||||||
|
if (!mounted) return;
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
@ -367,22 +389,31 @@ class _RealmSettingsWidgetState extends State<_RealmSettingsWidget> {
|
|||||||
children: [
|
children: [
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Symbols.edit),
|
leading: const Icon(Symbols.logout),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
title: Text('realmEdit').tr(),
|
title: Text('realmLeave').tr(),
|
||||||
subtitle: Text('realmEditDescription').tr(),
|
subtitle: Text('realmLeaveDescription').tr(),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
onTap: () {
|
onTap: _isBusy ? null : () => _leaveRealm(),
|
||||||
GoRouter.of(context).pushNamed(
|
|
||||||
'realmManage',
|
|
||||||
queryParameters: {'editing': widget.realm!.alias},
|
|
||||||
).then((value) {
|
|
||||||
if (value != null) {
|
|
||||||
widget.onUpdate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
if (isOwned)
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Symbols.edit),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
title: Text('realmEdit').tr(),
|
||||||
|
subtitle: Text('realmEditDescription').tr(),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed(
|
||||||
|
'realmManage',
|
||||||
|
queryParameters: {'editing': widget.realm!.alias},
|
||||||
|
).then((value) {
|
||||||
|
if (value != null) {
|
||||||
|
widget.onUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
if (isOwned)
|
if (isOwned)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Symbols.delete),
|
leading: const Icon(Symbols.delete),
|
||||||
|
@ -48,6 +48,11 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
modifiers: [(!kIsWeb && Platform.isMacOS) ? HotKeyModifier.meta : HotKeyModifier.control],
|
modifiers: [(!kIsWeb && Platform.isMacOS) ? HotKeyModifier.meta : HotKeyModifier.control],
|
||||||
scope: HotKeyScope.inapp,
|
scope: HotKeyScope.inapp,
|
||||||
);
|
);
|
||||||
|
final HotKey _newLineHotKey = HotKey(
|
||||||
|
key: PhysicalKeyboardKey.enter,
|
||||||
|
modifiers: [(!kIsWeb && Platform.isMacOS) ? HotKeyModifier.meta : HotKeyModifier.control],
|
||||||
|
scope: HotKeyScope.inapp,
|
||||||
|
);
|
||||||
|
|
||||||
void _registerHotKey() {
|
void _registerHotKey() {
|
||||||
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
|
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
|
||||||
@ -61,6 +66,10 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
));
|
));
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
|
hotKeyManager.register(_newLineHotKey, keyDownHandler: (_) async {
|
||||||
|
if (_contentController.text.isEmpty) return;
|
||||||
|
_contentController.text += '\n';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -205,7 +214,10 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
_contentController.dispose();
|
_contentController.dispose();
|
||||||
_focusNode.dispose();
|
_focusNode.dispose();
|
||||||
_dismissEmojiPicker();
|
_dismissEmojiPicker();
|
||||||
if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS)) hotKeyManager.unregister(_pasteHotKey);
|
if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS)) {
|
||||||
|
hotKeyManager.unregister(_pasteHotKey);
|
||||||
|
hotKeyManager.unregister(_newLineHotKey);
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,6 +357,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
_sendMessage();
|
_sendMessage();
|
||||||
_focusNode.requestFocus();
|
_focusNode.requestFocus();
|
||||||
},
|
},
|
||||||
|
maxLines: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
@ -965,7 +965,7 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'postEditor',
|
'postEditor',
|
||||||
pathParameters: {'mode': data.typePlural},
|
pathParameters: {'mode': 'stories'},
|
||||||
queryParameters: {'replying': data.id.toString()},
|
queryParameters: {'replying': data.id.toString()},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -981,7 +981,7 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'postEditor',
|
'postEditor',
|
||||||
pathParameters: {'mode': data.typePlural},
|
pathParameters: {'mode': 'stories'},
|
||||||
queryParameters: {'reposting': data.id.toString()},
|
queryParameters: {'reposting': data.id.toString()},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user