mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
refactor: platform alert dialog for all dialogs
This commit is contained in:
parent
e086b520e7
commit
c201624f99
@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:platform_ui/platform_ui.dart';
|
||||
import 'package:spotube/components/Home/Sidebar.dart';
|
||||
import 'package:spotube/components/Home/SpotubeNavigationBar.dart';
|
||||
import 'package:spotube/components/Player/Player.dart';
|
||||
@ -35,8 +36,8 @@ class Shell extends HookConsumerWidget {
|
||||
useEffect(() {
|
||||
downloader.onFileExists = (track) async {
|
||||
if (!isMounted()) return false;
|
||||
return await showDialog<bool>(
|
||||
context: context,
|
||||
return await showPlatformAlertDialog<bool>(
|
||||
context,
|
||||
builder: (context) => ReplaceDownloadedFileDialog(
|
||||
track: track,
|
||||
),
|
||||
|
@ -31,14 +31,14 @@ class UserDownloads extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
PlatformFilledButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red[50],
|
||||
foregroundColor: Colors.red[400],
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.red[50]),
|
||||
foregroundColor: MaterialStatePropertyAll(Colors.red[400]),
|
||||
),
|
||||
onPressed: downloader.currentlyRunning > 0
|
||||
? downloader.cancelAll
|
||||
: null,
|
||||
macOSIsSecondary: true,
|
||||
isSecondary: true,
|
||||
child: const PlatformText("Cancel All"),
|
||||
),
|
||||
],
|
||||
@ -50,7 +50,7 @@ class UserDownloads extends HookConsumerWidget {
|
||||
itemBuilder: (context, index) {
|
||||
final track = downloader.inQueue.elementAt(index);
|
||||
return PlatformListTile(
|
||||
title: Text(track.name!),
|
||||
title: Text(track.name ?? ''),
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: ClipRRect(
|
||||
|
@ -16,15 +16,18 @@ class LyricDelayAdjustDialog extends HookConsumerWidget {
|
||||
double getValue() =>
|
||||
double.tryParse(controller.text.replaceAll("ms", "")) ?? 0;
|
||||
|
||||
return AlertDialog(
|
||||
return PlatformAlertDialog(
|
||||
title: const Center(child: Text("Adjust Lyrics Delay")),
|
||||
actions: [
|
||||
secondaryActions: [
|
||||
PlatformFilledButton(
|
||||
child: const Text("Cancel"),
|
||||
isSecondary: true,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
],
|
||||
primaryActions: [
|
||||
PlatformFilledButton(
|
||||
child: const Text("Done"),
|
||||
onPressed: () {
|
||||
|
@ -90,8 +90,8 @@ class SyncedLyrics extends HookConsumerWidget {
|
||||
tooltip: "Lyrics Delay",
|
||||
icon: const Icon(Icons.av_timer_rounded),
|
||||
onPressed: () async {
|
||||
final delay = await showDialog(
|
||||
context: context,
|
||||
final delay = await showPlatformAlertDialog(
|
||||
context,
|
||||
builder: (context) => const LyricDelayAdjustDialog(),
|
||||
);
|
||||
if (delay != null) {
|
||||
|
@ -15,8 +15,8 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
||||
|
||||
return PlatformTextButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
showPlatformAlertDialog(
|
||||
context,
|
||||
builder: (context) {
|
||||
return HookBuilder(builder: (context) {
|
||||
final playlistName = useTextEditingController();
|
||||
@ -24,13 +24,9 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
||||
final public = useState(false);
|
||||
final collaborative = useState(false);
|
||||
|
||||
return AlertDialog(
|
||||
return PlatformAlertDialog(
|
||||
title: const Text("Create a Playlist"),
|
||||
actions: [
|
||||
PlatformTextButton(
|
||||
child: const Text("Cancel"),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
primaryActions: [
|
||||
PlatformFilledButton(
|
||||
child: const Text("Create"),
|
||||
onPressed: () async {
|
||||
@ -53,6 +49,13 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
||||
},
|
||||
)
|
||||
],
|
||||
secondaryActions: [
|
||||
PlatformFilledButton(
|
||||
isSecondary: true,
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
],
|
||||
content: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
constraints: const BoxConstraints(maxWidth: 500),
|
||||
|
@ -66,15 +66,9 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
|
||||
},
|
||||
).key);
|
||||
|
||||
return AlertDialog(
|
||||
return PlatformAlertDialog(
|
||||
title: Text("Pick ${schemeType.name} color scheme"),
|
||||
actions: [
|
||||
PlatformTextButton(
|
||||
child: const Text("Cancel"),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
primaryActions: [
|
||||
PlatformFilledButton(
|
||||
child: const Text("Save"),
|
||||
onPressed: () {
|
||||
@ -91,6 +85,15 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
|
||||
},
|
||||
)
|
||||
],
|
||||
secondaryActions: [
|
||||
PlatformFilledButton(
|
||||
isSecondary: true,
|
||||
child: const Text("Cancel"),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
content: SizedBox(
|
||||
height: 200,
|
||||
width: 400,
|
||||
|
@ -28,9 +28,7 @@ class Settings extends HookConsumerWidget {
|
||||
final Auth auth = ref.watch(authProvider);
|
||||
|
||||
final pickColorScheme = useCallback((ColorSchemeType schemeType) {
|
||||
return () => showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return () => showPlatformAlertDialog(context, builder: (context) {
|
||||
return ColorSchemePickerDialog(
|
||||
schemeType: schemeType,
|
||||
);
|
||||
|
@ -34,11 +34,11 @@ class AdaptiveListTile extends HookWidget {
|
||||
onTap: breakpoint.isLessThan(breakOn)
|
||||
? () {
|
||||
onTap?.call();
|
||||
showDialog(
|
||||
context: context,
|
||||
showPlatformAlertDialog(
|
||||
context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(builder: (context, update) {
|
||||
return AlertDialog(
|
||||
return PlatformAlertDialog(
|
||||
title: title != null
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@ -50,7 +50,7 @@ class AdaptiveListTile extends HookWidget {
|
||||
Flexible(child: title!),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
: Container(),
|
||||
content: trailing?.call(context, update),
|
||||
);
|
||||
});
|
||||
|
@ -7,22 +7,24 @@ class DownloadConfirmationDialog extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
contentPadding: const EdgeInsets.all(15),
|
||||
title: Row(
|
||||
children: const [
|
||||
Text("Are you sure?"),
|
||||
SizedBox(width: 10),
|
||||
UniversalImage(
|
||||
path:
|
||||
"https://c.tenor.com/kHcmsxlKHEAAAAAM/rock-one-eyebrow-raised-rock-staring.gif",
|
||||
height: 40,
|
||||
width: 40,
|
||||
)
|
||||
],
|
||||
return PlatformAlertDialog(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Row(
|
||||
children: const [
|
||||
Text("Are you sure?"),
|
||||
SizedBox(width: 10),
|
||||
UniversalImage(
|
||||
path:
|
||||
"https://c.tenor.com/kHcmsxlKHEAAAAAM/rock-one-eyebrow-raised-rock-staring.gif",
|
||||
height: 40,
|
||||
width: 40,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -57,19 +59,22 @@ class DownloadConfirmationDialog extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
primaryActions: [
|
||||
PlatformFilledButton(
|
||||
style: const ButtonStyle(
|
||||
foregroundColor: MaterialStatePropertyAll(Colors.white),
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.red),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text("Accept"),
|
||||
)
|
||||
],
|
||||
secondaryActions: [
|
||||
PlatformFilledButton(
|
||||
isSecondary: true,
|
||||
child: const Text("Decline"),
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
),
|
||||
PlatformFilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
child: const Text("Accept"),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final groupValue = ref.watch(replaceDownloadedFileState);
|
||||
|
||||
return AlertDialog(
|
||||
return PlatformAlertDialog(
|
||||
title: Text("Track ${track.name} Already Exists"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -48,20 +48,23 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
PlatformTextButton(
|
||||
child: const Text("No"),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
),
|
||||
PlatformTextButton(
|
||||
primaryActions: [
|
||||
PlatformFilledButton(
|
||||
child: const Text("Yes"),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
)
|
||||
],
|
||||
secondaryActions: [
|
||||
PlatformFilledButton(
|
||||
isSecondary: true,
|
||||
child: const Text("No"),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -84,81 +84,79 @@ class TrackTile extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
Future<void> actionAddToPlaylist() async {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return FutureBuilder<Iterable<PlaylistSimple>>(
|
||||
future: spotify.playlists.me.all().then((playlists) async {
|
||||
final me = await spotify.me.get();
|
||||
return playlists.where((playlist) =>
|
||||
playlist.owner?.id != null &&
|
||||
playlist.owner!.id == me.id);
|
||||
}),
|
||||
builder: (context, snapshot) {
|
||||
return HookBuilder(builder: (context) {
|
||||
final playlistsCheck = useState(<String, bool>{});
|
||||
return AlertDialog(
|
||||
title: PlatformText(
|
||||
"Add `${track.value.name}` to following Playlists"),
|
||||
titleTextStyle:
|
||||
Theme.of(context).textTheme.bodyText1?.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
actions: [
|
||||
PlatformTextButton(
|
||||
child: const PlatformText("Cancel"),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
PlatformFilledButton(
|
||||
child: const PlatformText("Add"),
|
||||
onPressed: () async {
|
||||
final selectedPlaylists = playlistsCheck
|
||||
.value.entries
|
||||
.where((entry) => entry.value)
|
||||
.map((entry) => entry.key);
|
||||
showPlatformAlertDialog(context, builder: (context) {
|
||||
return FutureBuilder<Iterable<PlaylistSimple>>(
|
||||
future: spotify.playlists.me.all().then((playlists) async {
|
||||
final me = await spotify.me.get();
|
||||
return playlists.where((playlist) =>
|
||||
playlist.owner?.id != null && playlist.owner!.id == me.id);
|
||||
}),
|
||||
builder: (context, snapshot) {
|
||||
return HookBuilder(builder: (context) {
|
||||
final playlistsCheck = useState(<String, bool>{});
|
||||
return PlatformAlertDialog(
|
||||
title: PlatformText(
|
||||
"Add `${track.value.name}` to following Playlists",
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
secondaryActions: [
|
||||
PlatformFilledButton(
|
||||
isSecondary: true,
|
||||
child: const PlatformText("Cancel"),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
primaryActions: [
|
||||
PlatformFilledButton(
|
||||
child: const PlatformText("Add"),
|
||||
onPressed: () async {
|
||||
final selectedPlaylists = playlistsCheck.value.entries
|
||||
.where((entry) => entry.value)
|
||||
.map((entry) => entry.key);
|
||||
|
||||
await Future.wait(
|
||||
selectedPlaylists.map(
|
||||
(playlistId) => spotify.playlists
|
||||
.addTrack(track.value.uri!, playlistId),
|
||||
),
|
||||
).then((_) => Navigator.pop(context));
|
||||
},
|
||||
)
|
||||
],
|
||||
content: SizedBox(
|
||||
height: 300,
|
||||
width: 300,
|
||||
child: !snapshot.hasData
|
||||
? const Center(
|
||||
child: PlatformCircularProgressIndicator())
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final playlist =
|
||||
snapshot.data!.elementAt(index);
|
||||
return CheckboxListTile(
|
||||
title: PlatformText(playlist.name!),
|
||||
controlAffinity:
|
||||
ListTileControlAffinity.leading,
|
||||
value: playlistsCheck.value[playlist.id] ??
|
||||
false,
|
||||
onChanged: (val) {
|
||||
playlistsCheck.value = {
|
||||
...playlistsCheck.value,
|
||||
playlist.id!: val == true
|
||||
};
|
||||
},
|
||||
);
|
||||
await Future.wait(
|
||||
selectedPlaylists.map(
|
||||
(playlistId) => spotify.playlists
|
||||
.addTrack(track.value.uri!, playlistId),
|
||||
),
|
||||
).then((_) => Navigator.pop(context));
|
||||
},
|
||||
)
|
||||
],
|
||||
content: SizedBox(
|
||||
height: 300,
|
||||
width: 300,
|
||||
child: !snapshot.hasData
|
||||
? const Center(
|
||||
child: PlatformCircularProgressIndicator())
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final playlist = snapshot.data!.elementAt(index);
|
||||
return CheckboxListTile(
|
||||
title: PlatformText(playlist.name!),
|
||||
controlAffinity:
|
||||
ListTileControlAffinity.leading,
|
||||
value:
|
||||
playlistsCheck.value[playlist.id] ?? false,
|
||||
onChanged: (val) {
|
||||
playlistsCheck.value = {
|
||||
...playlistsCheck.value,
|
||||
playlist.id!: val == true
|
||||
};
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
final String thumbnailUrl = TypeConversionUtils.image_X_UrlString(
|
||||
|
@ -146,11 +146,10 @@ class TracksTableView extends HookConsumerWidget {
|
||||
switch (action) {
|
||||
case "download":
|
||||
{
|
||||
final isConfirmed = await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return const DownloadConfirmationDialog();
|
||||
});
|
||||
final isConfirmed = await showPlatformAlertDialog(
|
||||
context, builder: (context) {
|
||||
return const DownloadConfirmationDialog();
|
||||
});
|
||||
if (isConfirmed != true) return;
|
||||
for (final selectedTrack in selectedTracks) {
|
||||
downloader.addToQueue(selectedTrack);
|
||||
|
@ -52,41 +52,39 @@ void useUpdateChecker(WidgetRef ref) {
|
||||
final latestVersion = value.last;
|
||||
if (currentVersion == null || latestVersion == null) return;
|
||||
if (latestVersion <= currentVersion) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
const url =
|
||||
"https://spotube.netlify.app/other-downloads/stable-downloads";
|
||||
return AlertDialog(
|
||||
title: const Text("Spotube has an update"),
|
||||
actions: [
|
||||
PlatformFilledButton(
|
||||
child: const Text("Download Now"),
|
||||
onPressed: () => download(url),
|
||||
),
|
||||
],
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
showPlatformAlertDialog(context, builder: (context) {
|
||||
const url =
|
||||
"https://spotube.netlify.app/other-downloads/stable-downloads";
|
||||
return PlatformAlertDialog(
|
||||
title: const PlatformText("Spotube has an update"),
|
||||
primaryActions: [
|
||||
PlatformFilledButton(
|
||||
child: const Text("Download Now"),
|
||||
onPressed: () => download(url),
|
||||
),
|
||||
],
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("Spotube v${value.last} has been released"),
|
||||
Row(
|
||||
children: [
|
||||
Text("Spotube v${value.last} has been released"),
|
||||
Row(
|
||||
children: [
|
||||
const Text("Read the latest "),
|
||||
AnchorButton(
|
||||
"release notes",
|
||||
style: const TextStyle(color: Colors.blue),
|
||||
onTap: () => launchUrlString(
|
||||
url,
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
),
|
||||
],
|
||||
const PlatformText("Read the latest "),
|
||||
AnchorButton(
|
||||
"release notes",
|
||||
style: const TextStyle(color: Colors.blue),
|
||||
onTap: () => launchUrlString(
|
||||
url,
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
return null;
|
||||
}, [packageInfo, isCheckUpdateEnabled]);
|
||||
|
@ -109,8 +109,8 @@ void main() async {
|
||||
logger.v(
|
||||
"[onFileExists] download confirmation for ${track.name}",
|
||||
);
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
return showPlatformAlertDialog<bool>(
|
||||
context,
|
||||
builder: (_) =>
|
||||
ReplaceDownloadedFileDialog(track: track),
|
||||
).then((s) => s ?? false);
|
||||
@ -201,7 +201,7 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
||||
};
|
||||
}, []);
|
||||
|
||||
platform = TargetPlatform.macOS;
|
||||
platform = TargetPlatform.windows;
|
||||
|
||||
return PlatformApp.router(
|
||||
routeInformationParser: router.routeInformationParser,
|
||||
@ -217,7 +217,7 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
||||
accentMaterialColor: accentMaterialColor,
|
||||
backgroundMaterialColor: backgroundMaterialColor,
|
||||
),
|
||||
iosTheme: iosTheme,
|
||||
iosTheme: themeMode == ThemeMode.dark ? iosDarkTheme : iosTheme,
|
||||
windowsTheme: windowsTheme,
|
||||
windowsDarkTheme: windowsDarkTheme,
|
||||
macosTheme: macosTheme,
|
||||
|
@ -147,4 +147,5 @@ final macosDarkTheme = MacosThemeData.dark().copyWith(
|
||||
iconTheme: const MacosIconThemeData(size: 14),
|
||||
typography: MacosTypography(color: MacosColors.textColor),
|
||||
);
|
||||
final iosTheme = CupertinoThemeData();
|
||||
const iosTheme = CupertinoThemeData(brightness: Brightness.light);
|
||||
const iosDarkTheme = CupertinoThemeData(brightness: Brightness.dark);
|
||||
|
Loading…
Reference in New Issue
Block a user