toolbar is now only present when textfield is focused

also added it to manage account page
This commit is contained in:
Filip Krawczyk 2022-08-23 19:29:01 +02:00
parent 55daacf221
commit cc8441dabc
5 changed files with 209 additions and 167 deletions

View File

@ -31,6 +31,7 @@ class CreatePostPage extends HookWidget {
);
final titleFocusNode = useFocusNode();
final editorFocusNode = useFocusNode();
handleSubmit(Jwt token) async {
if (formKey.currentState!.validate()) {
@ -63,6 +64,7 @@ class CreatePostPage extends HookWidget {
labelText: L10n.of(context).body,
instanceHost: store.instanceHost,
fancy: store.showFancy,
focusNode: editorFocusNode,
),
);
@ -146,6 +148,7 @@ class CreatePostPage extends HookWidget {
children: [
const Spacer(),
EditorToolbar(
editorFocusNode: editorFocusNode,
controller: bodyController,
instanceHost: context.read<CreatePostStore>().instanceHost,
),

View File

@ -234,150 +234,169 @@ class _ManageAccount extends HookWidget {
}
}
return ListView(
padding: const EdgeInsets.symmetric(horizontal: 15),
return Stack(
children: [
_ImagePicker(
user: user,
name: L10n.of(context).avatar,
initialUrl: avatar.value,
onChange: (value) => avatar.value = value,
informAcceptedRef: informAcceptedAvatarRef,
ListView(
padding: const EdgeInsets.symmetric(horizontal: 15),
children: [
_ImagePicker(
user: user,
name: L10n.of(context).avatar,
initialUrl: avatar.value,
onChange: (value) => avatar.value = value,
informAcceptedRef: informAcceptedAvatarRef,
),
const SizedBox(height: 8),
_ImagePicker(
user: user,
name: L10n.of(context).banner,
initialUrl: banner.value,
onChange: (value) => banner.value = value,
informAcceptedRef: informAcceptedBannerRef,
),
const SizedBox(height: 8),
Text(L10n.of(context).display_name,
style: theme.textTheme.headline6),
TextField(
controller: displayNameController,
onSubmitted: (_) => bioFocusNode.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),
Text(L10n.of(context).email, style: theme.textTheme.headline6),
TextField(
focusNode: emailFocusNode,
controller: emailController,
autofillHints: const [AutofillHints.email],
keyboardType: TextInputType.emailAddress,
onSubmitted: (_) => matrixUserFocusNode.requestFocus(),
),
const SizedBox(height: 8),
Text(L10n.of(context).matrix_user,
style: theme.textTheme.headline6),
TextField(
focusNode: matrixUserFocusNode,
controller: matrixUserController,
onSubmitted: (_) => newPasswordFocusNode.requestFocus(),
),
const SizedBox(height: 8),
// Text(L10n.of(context)!.new_password, style: theme.textTheme.headline6),
// TextField(
// focusNode: newPasswordFocusNode,
// controller: newPasswordController,
// autofillHints: const [AutofillHints.newPassword],
// keyboardType: TextInputType.visiblePassword,
// obscureText: true,
// onSubmitted: (_) => verifyPasswordFocusNode.requestFocus(),
// ),
// const SizedBox(height: 8),
// Text(L10n.of(context)!.verify_password,
// style: theme.textTheme.headline6),
// TextField(
// focusNode: verifyPasswordFocusNode,
// controller: newPasswordVerifyController,
// autofillHints: const [AutofillHints.newPassword],
// keyboardType: TextInputType.visiblePassword,
// obscureText: true,
// onSubmitted: (_) => oldPasswordFocusNode.requestFocus(),
// ),
// const SizedBox(height: 8),
// Text(L10n.of(context)!.old_password, style: theme.textTheme.headline6),
// TextField(
// focusNode: oldPasswordFocusNode,
// controller: oldPasswordController,
// autofillHints: const [AutofillHints.password],
// keyboardType: TextInputType.visiblePassword,
// obscureText: true,
// ),
// const SizedBox(height: 8),
SwitchListTile.adaptive(
value: showNsfw.value,
onChanged: (checked) {
showNsfw.value = checked;
},
title: Text(L10n.of(context).show_nsfw),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: botAccount.value,
onChanged: (checked) {
botAccount.value = checked;
},
title: Text(L10n.of(context).bot_account),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: showBotAccounts.value,
onChanged: (checked) {
showBotAccounts.value = checked;
},
title: Text(L10n.of(context).show_bot_accounts),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: showReadPosts.value,
onChanged: (checked) {
showReadPosts.value = checked;
},
title: Text(L10n.of(context).show_read_posts),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: sendNotificationsToEmail.value,
onChanged: (checked) {
sendNotificationsToEmail.value = checked;
},
title: Text(L10n.of(context).send_notifications_to_email),
dense: true,
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: saveDelayedLoading.loading ? null : handleSubmit,
child: saveDelayedLoading.loading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator.adaptive(),
)
: Text(L10n.of(context).save),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: deleteAccountDialog,
style: ElevatedButton.styleFrom(
primary: Colors.red,
),
child: Text(L10n.of(context).delete_account.toUpperCase()),
),
const BottomSafe(),
],
),
const SizedBox(height: 8),
_ImagePicker(
user: user,
name: L10n.of(context).banner,
initialUrl: banner.value,
onChange: (value) => banner.value = value,
informAcceptedRef: informAcceptedBannerRef,
),
const SizedBox(height: 8),
Text(L10n.of(context).display_name, style: theme.textTheme.headline6),
TextField(
controller: displayNameController,
onSubmitted: (_) => bioFocusNode.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),
Text(L10n.of(context).email, style: theme.textTheme.headline6),
TextField(
focusNode: emailFocusNode,
controller: emailController,
autofillHints: const [AutofillHints.email],
keyboardType: TextInputType.emailAddress,
onSubmitted: (_) => matrixUserFocusNode.requestFocus(),
),
const SizedBox(height: 8),
Text(L10n.of(context).matrix_user, style: theme.textTheme.headline6),
TextField(
focusNode: matrixUserFocusNode,
controller: matrixUserController,
onSubmitted: (_) => newPasswordFocusNode.requestFocus(),
),
const SizedBox(height: 8),
// Text(L10n.of(context)!.new_password, style: theme.textTheme.headline6),
// TextField(
// focusNode: newPasswordFocusNode,
// controller: newPasswordController,
// autofillHints: const [AutofillHints.newPassword],
// keyboardType: TextInputType.visiblePassword,
// obscureText: true,
// onSubmitted: (_) => verifyPasswordFocusNode.requestFocus(),
// ),
// const SizedBox(height: 8),
// Text(L10n.of(context)!.verify_password,
// style: theme.textTheme.headline6),
// TextField(
// focusNode: verifyPasswordFocusNode,
// controller: newPasswordVerifyController,
// autofillHints: const [AutofillHints.newPassword],
// keyboardType: TextInputType.visiblePassword,
// obscureText: true,
// onSubmitted: (_) => oldPasswordFocusNode.requestFocus(),
// ),
// const SizedBox(height: 8),
// Text(L10n.of(context)!.old_password, style: theme.textTheme.headline6),
// TextField(
// focusNode: oldPasswordFocusNode,
// controller: oldPasswordController,
// autofillHints: const [AutofillHints.password],
// keyboardType: TextInputType.visiblePassword,
// obscureText: true,
// ),
// const SizedBox(height: 8),
SwitchListTile.adaptive(
value: showNsfw.value,
onChanged: (checked) {
showNsfw.value = checked;
},
title: Text(L10n.of(context).show_nsfw),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: botAccount.value,
onChanged: (checked) {
botAccount.value = checked;
},
title: Text(L10n.of(context).bot_account),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: showBotAccounts.value,
onChanged: (checked) {
showBotAccounts.value = checked;
},
title: Text(L10n.of(context).show_bot_accounts),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: showReadPosts.value,
onChanged: (checked) {
showReadPosts.value = checked;
},
title: Text(L10n.of(context).show_read_posts),
dense: true,
),
const SizedBox(height: 8),
SwitchListTile.adaptive(
value: sendNotificationsToEmail.value,
onChanged: (checked) {
sendNotificationsToEmail.value = checked;
},
title: Text(L10n.of(context).send_notifications_to_email),
dense: true,
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: saveDelayedLoading.loading ? null : handleSubmit,
child: saveDelayedLoading.loading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator.adaptive(),
)
: Text(L10n.of(context).save),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: deleteAccountDialog,
style: ElevatedButton.styleFrom(
primary: Colors.red,
SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Spacer(),
EditorToolbar(
editorFocusNode: bioFocusNode,
controller: bioController,
instanceHost: user.instanceHost,
),
],
),
child: Text(L10n.of(context).delete_account.toUpperCase()),
),
const BottomSafe(),
],
);
}

View File

@ -52,18 +52,22 @@ class Editor extends HookWidget {
);
}
return TextField(
controller: actualController,
focusNode: focusNode,
autofocus: autofocus,
keyboardType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
onChanged: onChanged,
onSubmitted: onSubmitted,
maxLines: maxLines,
minLines: minLines,
decoration: InputDecoration(labelText: labelText),
inputFormatters: [MarkdownFormatter()],
return FocusScope(
child: Focus(
focusNode: focusNode,
child: TextField(
controller: actualController,
autofocus: autofocus,
keyboardType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
onChanged: onChanged,
onSubmitted: onSubmitted,
maxLines: maxLines,
minLines: minLines,
decoration: InputDecoration(labelText: labelText),
inputFormatters: [MarkdownFormatter()],
),
),
);
}
}

View File

@ -39,37 +39,50 @@ enum HeaderLevel {
final int value;
}
class EditorToolbar extends StatelessWidget {
class EditorToolbar extends HookWidget {
final TextEditingController controller;
final String instanceHost;
final EditorToolbarStore store;
final FocusNode editorFocusNode;
static const _height = 50.0;
EditorToolbar({
required this.controller,
required this.instanceHost,
required this.editorFocusNode,
}) : store = EditorToolbarStore(instanceHost);
@override
Widget build(BuildContext context) {
final visible = useState(editorFocusNode.hasFocus);
useEffect(() {
editorFocusNode.addListener(() {
visible.value = editorFocusNode.hasFocus;
});
return;
}, [editorFocusNode]);
return MobxProvider(
create: (context) => store,
child: AsyncStoreListener(
asyncStore: store.imageUploadState,
child: Container(
height: _height,
width: double.infinity,
color: Theme.of(context).cardColor,
child: Material(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: _ToolbarBody(
controller: controller,
instanceHost: instanceHost,
),
),
),
),
child: visible.value
? Container(
height: _height,
width: double.infinity,
color: Theme.of(context).cardColor,
child: Material(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: _ToolbarBody(
controller: controller,
instanceHost: instanceHost,
),
),
),
)
: const SizedBox.shrink(),
),
);
}

View File

@ -36,6 +36,7 @@ class WriteComment extends HookWidget {
final showFancy = useState(false);
final delayed = useDelayedLoading();
final loggedInAction = useLoggedInAction(post.instanceHost);
final editorFocusNode = useFocusNode();
final preview = () {
final body = () {
@ -117,6 +118,7 @@ class WriteComment extends HookWidget {
controller: controller,
autofocus: true,
fancy: showFancy.value,
focusNode: editorFocusNode,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
@ -141,6 +143,7 @@ class WriteComment extends HookWidget {
children: [
const Spacer(),
EditorToolbar(
editorFocusNode: editorFocusNode,
controller: controller,
instanceHost: post.instanceHost,
),