Fix bugs
This commit is contained in:
@@ -48,7 +48,7 @@ public class MethodHandler: NSObject, FlutterPlugin {
|
|||||||
result(FlutterError(code: String(error.code), message: error.description, details: nil))
|
result(FlutterError(code: String(error.code), message: error.description, details: nil))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result(true)
|
result("")
|
||||||
case "change_config_options":
|
case "change_config_options":
|
||||||
guard let options = call.arguments as? String else {
|
guard let options = call.arguments as? String else {
|
||||||
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
|
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
|
||||||
|
|||||||
@@ -86,158 +86,161 @@ class ProfileDetailsPage extends HookConsumerWidget with PresLogger {
|
|||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Scaffold(
|
Scaffold(
|
||||||
body: CustomScrollView(
|
body: SafeArea(
|
||||||
slivers: [
|
child: CustomScrollView(
|
||||||
SliverAppBar(
|
slivers: [
|
||||||
title: Text(t.profile.detailsPageTitle),
|
SliverAppBar(
|
||||||
pinned: true,
|
title: Text(t.profile.detailsPageTitle),
|
||||||
actions: [
|
pinned: true,
|
||||||
if (state.isEditing)
|
actions: [
|
||||||
PopupMenuButton(
|
if (state.isEditing)
|
||||||
itemBuilder: (context) {
|
PopupMenuButton(
|
||||||
return [
|
itemBuilder: (context) {
|
||||||
if (state.profile case RemoteProfileEntity())
|
return [
|
||||||
|
if (state.profile case RemoteProfileEntity())
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Text(t.profile.update.buttonTxt),
|
||||||
|
onTap: () async {
|
||||||
|
await notifier.updateProfile();
|
||||||
|
},
|
||||||
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(t.profile.update.buttonTxt),
|
child: Text(t.profile.delete.buttonTxt),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await notifier.updateProfile();
|
final deleteConfirmed =
|
||||||
|
await showConfirmationDialog(
|
||||||
|
context,
|
||||||
|
title: t.profile.delete.buttonTxt,
|
||||||
|
message: t.profile.delete.confirmationMsg,
|
||||||
|
);
|
||||||
|
if (deleteConfirmed) {
|
||||||
|
await notifier.delete();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
];
|
||||||
child: Text(t.profile.delete.buttonTxt),
|
},
|
||||||
onTap: () async {
|
|
||||||
final deleteConfirmed =
|
|
||||||
await showConfirmationDialog(
|
|
||||||
context,
|
|
||||||
title: t.profile.delete.buttonTxt,
|
|
||||||
message: t.profile.delete.confirmationMsg,
|
|
||||||
);
|
|
||||||
if (deleteConfirmed) {
|
|
||||||
await notifier.delete();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Form(
|
|
||||||
autovalidateMode: state.showErrorMessages
|
|
||||||
? AutovalidateMode.always
|
|
||||||
: AutovalidateMode.disabled,
|
|
||||||
child: SliverList.list(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
),
|
||||||
child: CustomTextFormField(
|
],
|
||||||
initialValue: state.profile.name,
|
),
|
||||||
onChanged: (value) =>
|
Form(
|
||||||
notifier.setField(name: value),
|
autovalidateMode: state.showErrorMessages
|
||||||
validator: (value) => (value?.isEmpty ?? true)
|
? AutovalidateMode.always
|
||||||
? t.profile.detailsForm.emptyNameMsg
|
: AutovalidateMode.disabled,
|
||||||
: null,
|
child: SliverList.list(
|
||||||
label: t.profile.detailsForm.nameLabel,
|
children: [
|
||||||
hint: t.profile.detailsForm.nameHint,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (state.profile
|
|
||||||
case RemoteProfileEntity(
|
|
||||||
:final url,
|
|
||||||
:final options
|
|
||||||
)) ...[
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
vertical: 8,
|
vertical: 8,
|
||||||
),
|
),
|
||||||
child: CustomTextFormField(
|
child: CustomTextFormField(
|
||||||
initialValue: url,
|
initialValue: state.profile.name,
|
||||||
onChanged: (value) =>
|
onChanged: (value) =>
|
||||||
notifier.setField(url: value),
|
notifier.setField(name: value),
|
||||||
validator: (value) =>
|
validator: (value) => (value?.isEmpty ?? true)
|
||||||
(value != null && !isUrl(value))
|
? t.profile.detailsForm.emptyNameMsg
|
||||||
? t.profile.detailsForm.invalidUrlMsg
|
: null,
|
||||||
: null,
|
label: t.profile.detailsForm.nameLabel,
|
||||||
label: t.profile.detailsForm.urlLabel,
|
hint: t.profile.detailsForm.nameHint,
|
||||||
hint: t.profile.detailsForm.urlHint,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
if (state.profile
|
||||||
title: Text(t.profile.detailsForm.updateInterval),
|
case RemoteProfileEntity(
|
||||||
subtitle: Text(
|
:final url,
|
||||||
options?.updateInterval.toApproximateTime(
|
:final options
|
||||||
isRelativeToNow: false,
|
)) ...[
|
||||||
) ??
|
Padding(
|
||||||
t.general.toggle.disabled,
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: CustomTextFormField(
|
||||||
|
initialValue: url,
|
||||||
|
onChanged: (value) =>
|
||||||
|
notifier.setField(url: value),
|
||||||
|
validator: (value) =>
|
||||||
|
(value != null && !isUrl(value))
|
||||||
|
? t.profile.detailsForm.invalidUrlMsg
|
||||||
|
: null,
|
||||||
|
label: t.profile.detailsForm.urlLabel,
|
||||||
|
hint: t.profile.detailsForm.urlHint,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
leading: const Icon(Icons.update),
|
ListTile(
|
||||||
onTap: () async {
|
title: Text(t.profile.detailsForm.updateInterval),
|
||||||
final intervalInHours = await SettingsInputDialog(
|
subtitle: Text(
|
||||||
title: t.profile.detailsForm
|
options?.updateInterval.toApproximateTime(
|
||||||
.updateIntervalDialogTitle,
|
isRelativeToNow: false,
|
||||||
initialValue: options?.updateInterval.inHours,
|
) ??
|
||||||
optionalAction: (
|
t.general.toggle.disabled,
|
||||||
t.general.state.disable,
|
|
||||||
() =>
|
|
||||||
notifier.setField(updateInterval: none()),
|
|
||||||
),
|
|
||||||
validator: isPort,
|
|
||||||
mapTo: int.tryParse,
|
|
||||||
digitsOnly: true,
|
|
||||||
).show(context);
|
|
||||||
if (intervalInHours == null) return;
|
|
||||||
notifier.setField(
|
|
||||||
updateInterval: optionOf(intervalInHours),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
if (state.isEditing)
|
|
||||||
ListTile(
|
|
||||||
title: Text(t.profile.detailsForm.lastUpdate),
|
|
||||||
subtitle: Text(state.profile.lastUpdate.format()),
|
|
||||||
dense: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SliverFillRemaining(
|
|
||||||
hasScrollBody: false,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
OverflowBar(
|
|
||||||
spacing: 12,
|
|
||||||
overflowAlignment: OverflowBarAlignment.end,
|
|
||||||
children: [
|
|
||||||
OutlinedButton(
|
|
||||||
onPressed: context.pop,
|
|
||||||
child: Text(
|
|
||||||
MaterialLocalizations.of(context)
|
|
||||||
.cancelButtonLabel,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
FilledButton(
|
leading: const Icon(Icons.update),
|
||||||
onPressed: notifier.save,
|
onTap: () async {
|
||||||
child: Text(t.profile.save.buttonText),
|
final intervalInHours =
|
||||||
),
|
await SettingsInputDialog(
|
||||||
],
|
title: t.profile.detailsForm
|
||||||
),
|
.updateIntervalDialogTitle,
|
||||||
|
initialValue: options?.updateInterval.inHours,
|
||||||
|
optionalAction: (
|
||||||
|
t.general.state.disable,
|
||||||
|
() => notifier.setField(
|
||||||
|
updateInterval: none()),
|
||||||
|
),
|
||||||
|
validator: isPort,
|
||||||
|
mapTo: int.tryParse,
|
||||||
|
digitsOnly: true,
|
||||||
|
).show(context);
|
||||||
|
if (intervalInHours == null) return;
|
||||||
|
notifier.setField(
|
||||||
|
updateInterval: optionOf(intervalInHours),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (state.isEditing)
|
||||||
|
ListTile(
|
||||||
|
title: Text(t.profile.detailsForm.lastUpdate),
|
||||||
|
subtitle: Text(state.profile.lastUpdate.format()),
|
||||||
|
dense: true,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
SliverFillRemaining(
|
||||||
],
|
hasScrollBody: false,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
OverflowBar(
|
||||||
|
spacing: 12,
|
||||||
|
overflowAlignment: OverflowBarAlignment.end,
|
||||||
|
children: [
|
||||||
|
OutlinedButton(
|
||||||
|
onPressed: context.pop,
|
||||||
|
child: Text(
|
||||||
|
MaterialLocalizations.of(context)
|
||||||
|
.cancelButtonLabel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: notifier.save,
|
||||||
|
child: Text(t.profile.save.buttonText),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (showLoadingOverlay)
|
if (showLoadingOverlay)
|
||||||
|
|||||||
@@ -43,73 +43,75 @@ class ProfilesOverviewModal extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return Stack(
|
return SafeArea(
|
||||||
children: [
|
child: Stack(
|
||||||
CustomScrollView(
|
children: [
|
||||||
controller: scrollController,
|
CustomScrollView(
|
||||||
slivers: [
|
controller: scrollController,
|
||||||
switch (asyncProfiles) {
|
slivers: [
|
||||||
AsyncData(value: final profiles) => SliverList.builder(
|
switch (asyncProfiles) {
|
||||||
itemBuilder: (context, index) {
|
AsyncData(value: final profiles) => SliverList.builder(
|
||||||
final profile = profiles[index];
|
itemBuilder: (context, index) {
|
||||||
return ProfileTile(profile: profile);
|
final profile = profiles[index];
|
||||||
},
|
return ProfileTile(profile: profile);
|
||||||
itemCount: profiles.length,
|
},
|
||||||
|
itemCount: profiles.length,
|
||||||
|
),
|
||||||
|
AsyncError(:final error) => SliverErrorBodyPlaceholder(
|
||||||
|
t.presentShortError(error),
|
||||||
|
),
|
||||||
|
AsyncLoading() => const SliverLoadingBodyPlaceholder(),
|
||||||
|
_ => const SliverToBoxAdapter(),
|
||||||
|
},
|
||||||
|
const SliverGap(48),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
FilledButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
const AddProfileRoute().push(context);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: Text(t.profile.add.shortBtnTxt),
|
||||||
|
),
|
||||||
|
FilledButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return const ProfilesSortModal();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.sort),
|
||||||
|
label: Text(t.general.sort),
|
||||||
|
),
|
||||||
|
FilledButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
await ref
|
||||||
|
.read(
|
||||||
|
foregroundProfilesUpdateNotifierProvider.notifier,
|
||||||
|
)
|
||||||
|
.trigger();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.update),
|
||||||
|
label: Text(t.profile.update.updateSubscriptions),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
AsyncError(:final error) => SliverErrorBodyPlaceholder(
|
|
||||||
t.presentShortError(error),
|
|
||||||
),
|
|
||||||
AsyncLoading() => const SliverLoadingBodyPlaceholder(),
|
|
||||||
_ => const SliverToBoxAdapter(),
|
|
||||||
},
|
|
||||||
const SliverGap(48),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Positioned.fill(
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: Wrap(
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
FilledButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
const AddProfileRoute().push(context);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
label: Text(t.profile.add.shortBtnTxt),
|
|
||||||
),
|
|
||||||
FilledButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return const ProfilesSortModal();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.sort),
|
|
||||||
label: Text(t.general.sort),
|
|
||||||
),
|
|
||||||
FilledButton.icon(
|
|
||||||
onPressed: () async {
|
|
||||||
await ref
|
|
||||||
.read(
|
|
||||||
foregroundProfilesUpdateNotifierProvider.notifier,
|
|
||||||
)
|
|
||||||
.trigger();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.update),
|
|
||||||
label: Text(t.profile.update.updateSubscriptions),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user