Compare commits
4 Commits
8aefdfbf27
...
9ff62f86c5
Author | SHA1 | Date |
---|---|---|
Filip Krawczyk | 9ff62f86c5 | |
Filip Krawczyk | 2d7d2a64bd | |
Filip Krawczyk | c15cfdf02c | |
Filip Krawczyk | 87726f283b |
|
@ -14,10 +14,10 @@ extension Utilities on String {
|
|||
|
||||
int getEndOfTheLine(int from) {
|
||||
for (var i = from; i < length; i++) {
|
||||
if (this[i] == '\n') return i;
|
||||
if (this[i] == '\n') return i + 1;
|
||||
}
|
||||
|
||||
return length - 1;
|
||||
return length;
|
||||
}
|
||||
|
||||
/// returns the line that ends at endingIndex
|
||||
|
|
|
@ -30,8 +30,10 @@ class CreatePostPage extends HookWidget {
|
|||
useStore((CreatePostStore store) => store.instanceHost),
|
||||
);
|
||||
|
||||
final editorController = useEditorController(
|
||||
instanceHost: context.read<CreatePostStore>().instanceHost,
|
||||
text: context.read<CreatePostStore>().body);
|
||||
final titleFocusNode = useFocusNode();
|
||||
final editorFocusNode = useFocusNode();
|
||||
|
||||
handleSubmit(Jwt token) async {
|
||||
if (formKey.currentState!.validate()) {
|
||||
|
@ -54,17 +56,12 @@ class CreatePostPage extends HookWidget {
|
|||
),
|
||||
);
|
||||
|
||||
final bodyController =
|
||||
useTextEditingController(text: context.read<CreatePostStore>().body);
|
||||
|
||||
final body = ObserverBuilder<CreatePostStore>(
|
||||
builder: (context, store) => Editor(
|
||||
controller: bodyController,
|
||||
controller: editorController,
|
||||
onChanged: (body) => store.body = body,
|
||||
labelText: L10n.of(context).body,
|
||||
instanceHost: store.instanceHost,
|
||||
fancy: store.showFancy,
|
||||
focusNode: editorFocusNode,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -143,11 +140,7 @@ class CreatePostPage extends HookWidget {
|
|||
),
|
||||
),
|
||||
BottomSticky(
|
||||
child: EditorToolbar(
|
||||
editorFocusNode: editorFocusNode,
|
||||
controller: bodyController,
|
||||
instanceHost: context.read<CreatePostStore>().instanceHost,
|
||||
),
|
||||
child: EditorToolbar(editorController),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -99,7 +99,6 @@ class _ManageAccount extends HookWidget {
|
|||
|
||||
final displayNameController =
|
||||
useTextEditingController(text: user.person.displayName);
|
||||
final bioController = useTextEditingController(text: user.person.bio);
|
||||
final emailController =
|
||||
useTextEditingController(text: user.localUser.email);
|
||||
final matrixUserController =
|
||||
|
@ -122,13 +121,15 @@ class _ManageAccount extends HookWidget {
|
|||
|
||||
final deleteAccountPasswordController = useTextEditingController();
|
||||
|
||||
final bioFocusNode = useFocusNode();
|
||||
final emailFocusNode = useFocusNode();
|
||||
final matrixUserFocusNode = useFocusNode();
|
||||
final newPasswordFocusNode = useFocusNode();
|
||||
// final verifyPasswordFocusNode = useFocusNode();
|
||||
// final oldPasswordFocusNode = useFocusNode();
|
||||
|
||||
final bioController = useEditorController(
|
||||
instanceHost: user.instanceHost, text: user.person.bio);
|
||||
|
||||
final token =
|
||||
accountsStore.userDataFor(user.instanceHost, user.person.name)!.jwt;
|
||||
|
||||
|
@ -156,7 +157,9 @@ class _ManageAccount extends HookWidget {
|
|||
displayName: displayNameController.text.isEmpty
|
||||
? null
|
||||
: displayNameController.text,
|
||||
bio: bioController.text.isEmpty ? null : bioController.text,
|
||||
bio: bioController.textEditingController.text.isEmpty
|
||||
? null
|
||||
: bioController.textEditingController.text,
|
||||
email: emailController.text.isEmpty ? null : emailController.text,
|
||||
));
|
||||
|
||||
|
@ -259,15 +262,13 @@ class _ManageAccount extends HookWidget {
|
|||
style: theme.textTheme.headline6),
|
||||
TextField(
|
||||
controller: displayNameController,
|
||||
onSubmitted: (_) => bioFocusNode.requestFocus(),
|
||||
onSubmitted: (_) => bioController.focusNode.requestFocus(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context).bio, style: theme.textTheme.headline6),
|
||||
Editor(
|
||||
controller: bioController,
|
||||
focusNode: bioFocusNode,
|
||||
onSubmitted: (_) => emailFocusNode.requestFocus(),
|
||||
instanceHost: user.instanceHost,
|
||||
maxLines: 10,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
@ -385,11 +386,7 @@ class _ManageAccount extends HookWidget {
|
|||
],
|
||||
),
|
||||
BottomSticky(
|
||||
child: EditorToolbar(
|
||||
editorFocusNode: bioFocusNode,
|
||||
controller: bioController,
|
||||
instanceHost: user.instanceHost,
|
||||
),
|
||||
child: EditorToolbar(bioController),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
|
@ -6,10 +6,34 @@ import '../markdown_text.dart';
|
|||
|
||||
export 'editor_toolbar.dart';
|
||||
|
||||
class EditorController {
|
||||
final TextEditingController textEditingController;
|
||||
final FocusNode focusNode;
|
||||
final String instanceHost;
|
||||
|
||||
EditorController({
|
||||
required this.textEditingController,
|
||||
required this.focusNode,
|
||||
required this.instanceHost,
|
||||
});
|
||||
}
|
||||
|
||||
EditorController useEditorController({
|
||||
required String instanceHost,
|
||||
String? text,
|
||||
}) {
|
||||
final focusNode = useFocusNode();
|
||||
final textEditingController = useTextEditingController(text: text);
|
||||
return EditorController(
|
||||
textEditingController: textEditingController,
|
||||
focusNode: focusNode,
|
||||
instanceHost: instanceHost);
|
||||
}
|
||||
|
||||
/// A text field with added functionality for ease of editing
|
||||
class Editor extends HookWidget {
|
||||
final TextEditingController? controller;
|
||||
final FocusNode? focusNode;
|
||||
final EditorController controller;
|
||||
|
||||
final ValueChanged<String>? onSubmitted;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final int? minLines;
|
||||
|
@ -20,12 +44,10 @@ class Editor extends HookWidget {
|
|||
|
||||
/// Whether the editor should be preview the contents
|
||||
final bool fancy;
|
||||
final String instanceHost;
|
||||
|
||||
const Editor({
|
||||
super.key,
|
||||
this.controller,
|
||||
this.focusNode,
|
||||
required this.controller,
|
||||
this.onSubmitted,
|
||||
this.onChanged,
|
||||
this.minLines = 5,
|
||||
|
@ -33,28 +55,24 @@ class Editor extends HookWidget {
|
|||
this.labelText,
|
||||
this.initialValue,
|
||||
this.fancy = false,
|
||||
required this.instanceHost,
|
||||
this.autofocus = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final defaultController = useTextEditingController(text: initialValue);
|
||||
final actualController = controller ?? defaultController;
|
||||
|
||||
if (fancy) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: MarkdownText(
|
||||
actualController.text,
|
||||
instanceHost: instanceHost,
|
||||
controller.textEditingController.text,
|
||||
instanceHost: controller.instanceHost,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return TextField(
|
||||
focusNode: focusNode,
|
||||
controller: actualController,
|
||||
focusNode: controller.focusNode,
|
||||
controller: controller.textEditingController,
|
||||
autofocus: autofocus,
|
||||
keyboardType: TextInputType.multiline,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
|
|
|
@ -13,6 +13,7 @@ import '../../util/files.dart';
|
|||
import '../../util/mobx_provider.dart';
|
||||
import '../../util/observer_consumers.dart';
|
||||
import '../../util/text_lines_iterator.dart';
|
||||
import 'editor.dart';
|
||||
import 'editor_picking_dialog.dart';
|
||||
import 'editor_toolbar_store.dart';
|
||||
|
||||
|
@ -40,26 +41,21 @@ enum HeaderLevel {
|
|||
}
|
||||
|
||||
class EditorToolbar extends HookWidget {
|
||||
final TextEditingController controller;
|
||||
final String instanceHost;
|
||||
final FocusNode editorFocusNode;
|
||||
final EditorController controller;
|
||||
|
||||
static const _height = 50.0;
|
||||
|
||||
const EditorToolbar({
|
||||
required this.controller,
|
||||
required this.instanceHost,
|
||||
required this.editorFocusNode,
|
||||
});
|
||||
const EditorToolbar(this.controller);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final visible = useListenable(editorFocusNode).hasFocus;
|
||||
final visible = useListenable(controller.focusNode).hasFocus;
|
||||
|
||||
return MobxProvider(
|
||||
create: (context) => EditorToolbarStore(instanceHost),
|
||||
child: ObserverBuilder<EditorToolbarStore>(builder: (context, store) {
|
||||
create: (context) => EditorToolbarStore(controller.instanceHost),
|
||||
child: Builder(builder: (context) {
|
||||
return AsyncStoreListener(
|
||||
asyncStore: store.imageUploadState,
|
||||
asyncStore: context.read<EditorToolbarStore>().imageUploadState,
|
||||
child: AnimatedSwitcher(
|
||||
duration: kThemeAnimationDuration,
|
||||
transitionBuilder: (child, animation) {
|
||||
|
@ -73,15 +69,19 @@ class EditorToolbar extends HookWidget {
|
|||
);
|
||||
},
|
||||
child: visible
|
||||
? Container(
|
||||
height: _height,
|
||||
width: double.infinity,
|
||||
? Material(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: _ToolbarBody(
|
||||
controller: controller,
|
||||
instanceHost: instanceHost,
|
||||
child: SafeArea(
|
||||
child: SizedBox(
|
||||
height: _height,
|
||||
width: double.infinity,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: _ToolbarBody(
|
||||
controller: controller.textEditingController,
|
||||
instanceHost: controller.instanceHost,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -100,13 +100,11 @@ class BottomSticky extends StatelessWidget {
|
|||
const BottomSticky({required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Spacer(),
|
||||
child,
|
||||
],
|
||||
Widget build(BuildContext context) => Positioned(
|
||||
bottom: 0,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -214,15 +212,15 @@ class _ToolbarBody extends HookWidget {
|
|||
PopupMenuItem(
|
||||
value: h,
|
||||
child: Text(h.name.toUpperCase()),
|
||||
onTap: () {
|
||||
final header = '${'#' * h.value} ';
|
||||
|
||||
if (!controller.firstSelectedLine.startsWith(header)) {
|
||||
controller.insertAtBeginningOfFirstSelectedLine(header);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
onSelected: (val) {
|
||||
final header = '${'#' * val.value} ';
|
||||
|
||||
if (!controller.firstSelectedLine.startsWith(header)) {
|
||||
controller.insertAtBeginningOfFirstSelectedLine(header);
|
||||
}
|
||||
},
|
||||
tooltip: L10n.of(context).editor_header,
|
||||
child: const Icon(Icons.h_mobiledata),
|
||||
),
|
||||
|
@ -424,9 +422,12 @@ extension on TextEditingController {
|
|||
}
|
||||
|
||||
String get firstSelectedLine {
|
||||
if (text.isEmpty) return '';
|
||||
return text.substring(text.getBeginningOfTheLine(selection.start - 1),
|
||||
text.getEndOfTheLine(selection.end));
|
||||
if (text.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
final val = text.substring(text.getBeginningOfTheLine(selection.start - 1),
|
||||
text.getEndOfTheLine(selection.end) - 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
void insertAtBeginningOfFirstSelectedLine(String s) {
|
||||
|
|
|
@ -31,12 +31,14 @@ class WriteComment extends HookWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final controller =
|
||||
useTextEditingController(text: _isEdit ? comment?.content : null);
|
||||
final showFancy = useState(false);
|
||||
final delayed = useDelayedLoading();
|
||||
final loggedInAction = useLoggedInAction(post.instanceHost);
|
||||
final editorFocusNode = useFocusNode();
|
||||
|
||||
final editorController = useEditorController(
|
||||
instanceHost: post.instanceHost,
|
||||
text: _isEdit ? comment?.content : null,
|
||||
);
|
||||
|
||||
final preview = () {
|
||||
final body = () {
|
||||
|
@ -70,12 +72,12 @@ class WriteComment extends HookWidget {
|
|||
if (_isEdit) {
|
||||
return api.run(EditComment(
|
||||
commentId: comment!.id,
|
||||
content: controller.text,
|
||||
content: editorController.textEditingController.text,
|
||||
auth: token.raw,
|
||||
));
|
||||
} else {
|
||||
return api.run(CreateComment(
|
||||
content: controller.text,
|
||||
content: editorController.textEditingController.text,
|
||||
postId: post.id,
|
||||
parentId: comment?.id,
|
||||
auth: token.raw,
|
||||
|
@ -114,11 +116,9 @@ class WriteComment extends HookWidget {
|
|||
),
|
||||
const Divider(),
|
||||
Editor(
|
||||
instanceHost: post.instanceHost,
|
||||
controller: controller,
|
||||
controller: editorController,
|
||||
autofocus: true,
|
||||
fancy: showFancy.value,
|
||||
focusNode: editorFocusNode,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
|
@ -138,11 +138,7 @@ class WriteComment extends HookWidget {
|
|||
],
|
||||
),
|
||||
BottomSticky(
|
||||
child: EditorToolbar(
|
||||
editorFocusNode: editorFocusNode,
|
||||
controller: controller,
|
||||
instanceHost: post.instanceHost,
|
||||
),
|
||||
child: EditorToolbar(editorController),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue