Compare commits
3 Commits
6f8fed149c
...
b5bb5dc1ff
Author | SHA1 | Date |
---|---|---|
Filip Krawczyk | b5bb5dc1ff | |
Filip Krawczyk | f21c6b7c8c | |
Filip Krawczyk | b972e4485a |
|
@ -31,7 +31,6 @@ class CreatePostPage extends HookWidget {
|
|||
);
|
||||
|
||||
final titleFocusNode = useFocusNode();
|
||||
final bodyFocusNode = useFocusNode();
|
||||
|
||||
handleSubmit(Jwt token) async {
|
||||
if (formKey.currentState!.validate()) {
|
||||
|
@ -47,7 +46,6 @@ class CreatePostPage extends HookWidget {
|
|||
textCapitalization: TextCapitalization.sentences,
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: Validators.required(L10n.of(context).required_field),
|
||||
onFieldSubmitted: (_) => bodyFocusNode.requestFocus(),
|
||||
onChanged: (title) => store.title = title,
|
||||
minLines: 1,
|
||||
maxLines: 2,
|
||||
|
@ -61,7 +59,6 @@ class CreatePostPage extends HookWidget {
|
|||
final body = ObserverBuilder<CreatePostStore>(
|
||||
builder: (context, store) => Editor(
|
||||
controller: bodyController,
|
||||
focusNode: bodyFocusNode,
|
||||
onChanged: (body) => store.body = body,
|
||||
labelText: L10n.of(context).body,
|
||||
instanceHost: store.instanceHost,
|
||||
|
|
|
@ -1,31 +1,61 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
|
||||
import '../../util/extensions/spaced.dart';
|
||||
|
||||
class Reformat {
|
||||
final String text;
|
||||
final int selectionBeginningShift;
|
||||
final int selectionEndingShift;
|
||||
Reformat({
|
||||
required this.text,
|
||||
this.selectionBeginningShift = 0,
|
||||
this.selectionEndingShift = 0,
|
||||
});
|
||||
}
|
||||
|
||||
extension on TextEditingController {
|
||||
String get selectionText =>
|
||||
text.substring(selection.baseOffset, selection.extentOffset);
|
||||
String get beforeSelectionText => text.substring(0, selection.baseOffset);
|
||||
String get afterSelectionText => text.substring(selection.extentOffset);
|
||||
|
||||
/// surroungs selection with given strings. If nothing is selected, placeholder is used in the middle
|
||||
void surround(String before, String after,
|
||||
[String placeholder = '[write text here]']) {
|
||||
void surround(
|
||||
String before, [
|
||||
String? after,
|
||||
String placeholder = '[write text here]',
|
||||
]) {
|
||||
after ??= before;
|
||||
final beg = text.substring(0, selection.baseOffset);
|
||||
final mid = text.substring(selection.baseOffset, selection.extentOffset);
|
||||
final mid = () {
|
||||
final m = text.substring(selection.baseOffset, selection.extentOffset);
|
||||
if (m.isEmpty) return placeholder;
|
||||
return m;
|
||||
}();
|
||||
final end = text.substring(selection.extentOffset);
|
||||
|
||||
if (mid.isEmpty) {
|
||||
value = value.copyWith(
|
||||
text: '$beg$before$placeholder$after$end',
|
||||
value = value.copyWith(
|
||||
text: '$beg$before$mid$after$end',
|
||||
selection: selection.copyWith(
|
||||
baseOffset: selection.baseOffset + before.length,
|
||||
extentOffset:
|
||||
selection.baseOffset + before.length + placeholder.length,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
value = value.copyWith(
|
||||
text: '$beg$before$mid$after$end',
|
||||
selection: selection.copyWith(
|
||||
baseOffset: selection.baseOffset + before.length,
|
||||
extentOffset: selection.extentOffset + before.length,
|
||||
));
|
||||
}
|
||||
extentOffset: selection.baseOffset + before.length + mid.length,
|
||||
));
|
||||
}
|
||||
|
||||
void reformat(Reformat Function(String selection) reformatter) {
|
||||
final beg = beforeSelectionText;
|
||||
final mid = selectionText;
|
||||
final end = afterSelectionText;
|
||||
|
||||
final r = reformatter(mid);
|
||||
value = value.copyWith(
|
||||
text: '$beg${r.text}$end',
|
||||
selection: selection.copyWith(
|
||||
baseOffset: selection.baseOffset + r.selectionBeginningShift,
|
||||
extentOffset: selection.extentOffset + r.selectionEndingShift,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,31 +76,68 @@ class Toolbar extends HookWidget {
|
|||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
controller.surround('**', '**');
|
||||
},
|
||||
icon: const Icon(Icons.format_bold)),
|
||||
onPressed: () => controller.surround('**'),
|
||||
icon: const Icon(Icons.format_bold),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {}, icon: const Icon(Icons.format_italic)),
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.link)),
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.image)),
|
||||
onPressed: () => controller.surround('*'),
|
||||
icon: const Icon(Icons.format_italic),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {}, icon: const Icon(Icons.h_mobiledata)),
|
||||
onPressed: () async {
|
||||
final r = await AddLinkDialog.show(
|
||||
context, controller.selectionText);
|
||||
if (r != null) controller.reformat((_) => r);
|
||||
},
|
||||
icon: const Icon(Icons.link),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.format_strikethrough)),
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.image),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {}, icon: const Icon(Icons.format_quote)),
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.person),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
//
|
||||
},
|
||||
icon: const Icon(Icons.home),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.h_mobiledata),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => controller.surround('~~'),
|
||||
icon: const Icon(Icons.format_strikethrough),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.format_quote),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.format_list_bulleted)),
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.code)),
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.subscript)),
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.superscript)),
|
||||
// IconButton(onPressed: () {}, icon: const Icon(Icons.warning)),//spoiler
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.superscript)),
|
||||
IconButton(
|
||||
onPressed: () {}, icon: const Icon(Icons.question_mark)),
|
||||
onPressed: () => controller.surround('`'),
|
||||
icon: const Icon(Icons.code),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => controller.surround('~'),
|
||||
icon: const Icon(Icons.subscript),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => controller.surround('^'),
|
||||
icon: const Icon(Icons.superscript),
|
||||
),
|
||||
//spoiler
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.warning)),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.question_mark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -78,3 +145,65 @@ class Toolbar extends HookWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddLinkDialog extends HookWidget {
|
||||
final String title;
|
||||
final String url;
|
||||
final String selection;
|
||||
|
||||
AddLinkDialog(this.selection)
|
||||
: title = selection.startsWith('https?://') ? '' : selection,
|
||||
url = selection.startsWith('https?://') ? selection : '';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final titleController = useTextEditingController(text: title);
|
||||
final urlController = useTextEditingController(text: url);
|
||||
|
||||
void submit() {
|
||||
final finalString = '(${titleController.text})[${urlController.text}]';
|
||||
Navigator.of(context).pop(Reformat(
|
||||
text: finalString,
|
||||
selectionBeginningShift: finalString.length,
|
||||
selectionEndingShift: finalString.length - selection.length,
|
||||
));
|
||||
}
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text('Add link'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: titleController,
|
||||
decoration: const InputDecoration(hintText: 'title'),
|
||||
textInputAction: TextInputAction.next,
|
||||
autofocus: true,
|
||||
),
|
||||
TextField(
|
||||
controller: urlController,
|
||||
decoration: const InputDecoration(hintText: 'https://example.com'),
|
||||
onEditingComplete: submit,
|
||||
autocorrect: false,
|
||||
),
|
||||
].spaced(10),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Cancel')),
|
||||
ElevatedButton(
|
||||
onPressed: submit,
|
||||
child: const Text('Add link'),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Reformat?> show(BuildContext context, String selection) async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) => AddLinkDialog(selection),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue