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))
|
||||
return
|
||||
}
|
||||
result(true)
|
||||
result("")
|
||||
case "change_config_options":
|
||||
guard let options = call.arguments as? String else {
|
||||
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
|
||||
|
||||
@@ -86,158 +86,161 @@ class ProfileDetailsPage extends HookConsumerWidget with PresLogger {
|
||||
return Stack(
|
||||
children: [
|
||||
Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
title: Text(t.profile.detailsPageTitle),
|
||||
pinned: true,
|
||||
actions: [
|
||||
if (state.isEditing)
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
if (state.profile case RemoteProfileEntity())
|
||||
body: SafeArea(
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
title: Text(t.profile.detailsPageTitle),
|
||||
pinned: true,
|
||||
actions: [
|
||||
if (state.isEditing)
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
if (state.profile case RemoteProfileEntity())
|
||||
PopupMenuItem(
|
||||
child: Text(t.profile.update.buttonTxt),
|
||||
onTap: () async {
|
||||
await notifier.updateProfile();
|
||||
},
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(t.profile.update.buttonTxt),
|
||||
child: Text(t.profile.delete.buttonTxt),
|
||||
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) =>
|
||||
notifier.setField(name: value),
|
||||
validator: (value) => (value?.isEmpty ?? true)
|
||||
? t.profile.detailsForm.emptyNameMsg
|
||||
: null,
|
||||
label: t.profile.detailsForm.nameLabel,
|
||||
hint: t.profile.detailsForm.nameHint,
|
||||
),
|
||||
),
|
||||
if (state.profile
|
||||
case RemoteProfileEntity(
|
||||
:final url,
|
||||
:final options
|
||||
)) ...[
|
||||
],
|
||||
),
|
||||
Form(
|
||||
autovalidateMode: state.showErrorMessages
|
||||
? AutovalidateMode.always
|
||||
: AutovalidateMode.disabled,
|
||||
child: SliverList.list(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: CustomTextFormField(
|
||||
initialValue: url,
|
||||
initialValue: state.profile.name,
|
||||
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,
|
||||
notifier.setField(name: value),
|
||||
validator: (value) => (value?.isEmpty ?? true)
|
||||
? t.profile.detailsForm.emptyNameMsg
|
||||
: null,
|
||||
label: t.profile.detailsForm.nameLabel,
|
||||
hint: t.profile.detailsForm.nameHint,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.profile.detailsForm.updateInterval),
|
||||
subtitle: Text(
|
||||
options?.updateInterval.toApproximateTime(
|
||||
isRelativeToNow: false,
|
||||
) ??
|
||||
t.general.toggle.disabled,
|
||||
if (state.profile
|
||||
case RemoteProfileEntity(
|
||||
:final url,
|
||||
:final options
|
||||
)) ...[
|
||||
Padding(
|
||||
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),
|
||||
onTap: () async {
|
||||
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,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(t.profile.detailsForm.updateInterval),
|
||||
subtitle: Text(
|
||||
options?.updateInterval.toApproximateTime(
|
||||
isRelativeToNow: false,
|
||||
) ??
|
||||
t.general.toggle.disabled,
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: notifier.save,
|
||||
child: Text(t.profile.save.buttonText),
|
||||
),
|
||||
],
|
||||
),
|
||||
leading: const Icon(Icons.update),
|
||||
onTap: () async {
|
||||
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)
|
||||
|
||||
@@ -43,73 +43,75 @@ class ProfilesOverviewModal extends HookConsumerWidget {
|
||||
},
|
||||
);
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
switch (asyncProfiles) {
|
||||
AsyncData(value: final profiles) => SliverList.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final profile = profiles[index];
|
||||
return ProfileTile(profile: profile);
|
||||
},
|
||||
itemCount: profiles.length,
|
||||
return SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
switch (asyncProfiles) {
|
||||
AsyncData(value: final profiles) => SliverList.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final profile = profiles[index];
|
||||
return ProfileTile(profile: profile);
|
||||
},
|
||||
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