This commit is contained in:
problematicconsumer
2024-01-12 22:45:52 +03:30
parent 835668b869
commit 3c38d6a88f
3 changed files with 202 additions and 197 deletions

View File

@@ -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)

View File

@@ -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),
),
],
),
),
),
),
],
],
),
);
}
}