mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
chore: SafeArea
This commit is contained in:
parent
0a604a9ad5
commit
92dde7286f
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
@ -17,6 +17,17 @@
|
|||||||
"dev"
|
"dev"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "spotube (mobile-skia)",
|
||||||
|
"type": "dart",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "lib/main.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"dev",
|
||||||
|
"--no-enable-impeller"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "spotube (profile)",
|
"name": "spotube (profile)",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
|
@ -25,122 +25,127 @@ class SettingsMetadataProviderFormPage extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final formKey = useMemoized(() => GlobalKey<FormBuilderState>(), []);
|
final formKey = useMemoized(() => GlobalKey<FormBuilderState>(), []);
|
||||||
|
|
||||||
return Scaffold(
|
return SafeArea(
|
||||||
headers: [
|
bottom: false,
|
||||||
TitleBar(
|
child: Scaffold(
|
||||||
title: Text(title),
|
headers: [
|
||||||
),
|
TitleBar(
|
||||||
],
|
title: Text(title),
|
||||||
child: FormBuilder(
|
),
|
||||||
key: formKey,
|
],
|
||||||
child: Center(
|
child: FormBuilder(
|
||||||
child: Container(
|
key: formKey,
|
||||||
padding: const EdgeInsets.all(16),
|
child: Center(
|
||||||
constraints: const BoxConstraints(maxWidth: 600),
|
child: Container(
|
||||||
child: CustomScrollView(
|
padding: const EdgeInsets.all(16),
|
||||||
shrinkWrap: true,
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
slivers: [
|
child: CustomScrollView(
|
||||||
SliverToBoxAdapter(
|
shrinkWrap: true,
|
||||||
child: Text(
|
slivers: [
|
||||||
title,
|
SliverToBoxAdapter(
|
||||||
textAlign: TextAlign.center,
|
child: Text(
|
||||||
style: context.theme.typography.h2,
|
title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: context.theme.typography.h2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SliverGap(24),
|
||||||
const SliverGap(24),
|
SliverList.separated(
|
||||||
SliverList.separated(
|
itemCount: fields.length,
|
||||||
itemCount: fields.length,
|
separatorBuilder: (context, index) => const Gap(12),
|
||||||
separatorBuilder: (context, index) => const Gap(12),
|
itemBuilder: (context, index) {
|
||||||
itemBuilder: (context, index) {
|
if (fields[index] is MetadataFormFieldTextObject) {
|
||||||
if (fields[index] is MetadataFormFieldTextObject) {
|
final field =
|
||||||
final field =
|
fields[index] as MetadataFormFieldTextObject;
|
||||||
fields[index] as MetadataFormFieldTextObject;
|
return MarkdownBody(
|
||||||
return MarkdownBody(
|
data: field.text,
|
||||||
data: field.text,
|
onTapLink: (text, href, title) {
|
||||||
onTapLink: (text, href, title) {
|
// TODO: Confirm link opening behavior
|
||||||
// TODO: Confirm link opening behavior
|
if (href != null) {
|
||||||
if (href != null) {
|
launchUrlString(href);
|
||||||
launchUrlString(href);
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final field = fields[index] as MetadataFormFieldInputObject;
|
|
||||||
return FormBuilderField(
|
|
||||||
name: field.id,
|
|
||||||
initialValue: field.defaultValue,
|
|
||||||
validator: FormBuilderValidators.compose([
|
|
||||||
if (field.required == true)
|
|
||||||
FormBuilderValidators.required(
|
|
||||||
errorText: 'This field is required',
|
|
||||||
),
|
|
||||||
if (field.regex != null)
|
|
||||||
FormBuilderValidators.match(
|
|
||||||
RegExp(field.regex!),
|
|
||||||
errorText:
|
|
||||||
"Input doesn't match the required format",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
builder: (formField) {
|
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
spacing: 4,
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
placeholder: field.placeholder == null
|
|
||||||
? null
|
|
||||||
: Text(field.placeholder!),
|
|
||||||
initialValue: formField.value,
|
|
||||||
onChanged: (value) {
|
|
||||||
formField.didChange(value);
|
|
||||||
},
|
|
||||||
obscureText:
|
|
||||||
field.variant == FormFieldVariant.password,
|
|
||||||
keyboardType:
|
|
||||||
field.variant == FormFieldVariant.number
|
|
||||||
? TextInputType.number
|
|
||||||
: TextInputType.text,
|
|
||||||
features: [
|
|
||||||
if (field.variant == FormFieldVariant.password)
|
|
||||||
const InputFeature.passwordToggle(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (formField.hasError)
|
|
||||||
Text(
|
|
||||||
formField.errorText ?? '',
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.red, fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SliverGap(24),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: Button.primary(
|
|
||||||
onPressed: () {
|
|
||||||
if (formKey.currentState?.saveAndValidate() != true) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final data = formKey.currentState!.value.entries
|
final field =
|
||||||
.map((e) => <String, dynamic>{
|
fields[index] as MetadataFormFieldInputObject;
|
||||||
"id": e.key,
|
return FormBuilderField(
|
||||||
"value": e.value,
|
name: field.id,
|
||||||
})
|
initialValue: field.defaultValue,
|
||||||
.toList();
|
validator: FormBuilderValidators.compose([
|
||||||
|
if (field.required == true)
|
||||||
context.router.maybePop(data);
|
FormBuilderValidators.required(
|
||||||
|
errorText: 'This field is required',
|
||||||
|
),
|
||||||
|
if (field.regex != null)
|
||||||
|
FormBuilderValidators.match(
|
||||||
|
RegExp(field.regex!),
|
||||||
|
errorText:
|
||||||
|
"Input doesn't match the required format",
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
builder: (formField) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
placeholder: field.placeholder == null
|
||||||
|
? null
|
||||||
|
: Text(field.placeholder!),
|
||||||
|
initialValue: formField.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
formField.didChange(value);
|
||||||
|
},
|
||||||
|
obscureText:
|
||||||
|
field.variant == FormFieldVariant.password,
|
||||||
|
keyboardType:
|
||||||
|
field.variant == FormFieldVariant.number
|
||||||
|
? TextInputType.number
|
||||||
|
: TextInputType.text,
|
||||||
|
features: [
|
||||||
|
if (field.variant ==
|
||||||
|
FormFieldVariant.password)
|
||||||
|
const InputFeature.passwordToggle(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (formField.hasError)
|
||||||
|
Text(
|
||||||
|
formField.errorText ?? '',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.red, fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Text(context.l10n.submit),
|
|
||||||
),
|
),
|
||||||
),
|
const SliverGap(24),
|
||||||
const SliverGap(200)
|
SliverToBoxAdapter(
|
||||||
],
|
child: Button.primary(
|
||||||
|
onPressed: () {
|
||||||
|
if (formKey.currentState?.saveAndValidate() != true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = formKey.currentState!.value.entries
|
||||||
|
.map((e) => <String, dynamic>{
|
||||||
|
"id": e.key,
|
||||||
|
"value": e.value,
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
context.router.maybePop(data);
|
||||||
|
},
|
||||||
|
child: Text(context.l10n.submit),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverGap(200)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -52,189 +52,194 @@ class SettingsMetadataProviderPage extends HookConsumerWidget {
|
|||||||
[plugins.asData?.value.plugins, pluginReposSnapshot.asData?.value],
|
[plugins.asData?.value.plugins, pluginReposSnapshot.asData?.value],
|
||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return SafeArea(
|
||||||
headers: const [
|
bottom: false,
|
||||||
TitleBar(
|
child: Scaffold(
|
||||||
title: Text("Metadata provider plugin"),
|
headers: const [
|
||||||
)
|
TitleBar(
|
||||||
],
|
title: Text("Metadata provider plugin"),
|
||||||
child: Padding(
|
)
|
||||||
padding: const EdgeInsets.all(8),
|
],
|
||||||
child: CustomScrollView(
|
child: Padding(
|
||||||
slivers: [
|
padding: const EdgeInsets.all(8),
|
||||||
SliverToBoxAdapter(
|
child: CustomScrollView(
|
||||||
child: Row(
|
slivers: [
|
||||||
spacing: 8,
|
SliverToBoxAdapter(
|
||||||
children: [
|
child: Row(
|
||||||
Expanded(
|
spacing: 8,
|
||||||
child: FormBuilder(
|
children: [
|
||||||
key: formKey,
|
Expanded(
|
||||||
child: TextFormBuilderField(
|
child: FormBuilder(
|
||||||
name: "plugin_url",
|
key: formKey,
|
||||||
validator: FormBuilderValidators.url(
|
child: TextFormBuilderField(
|
||||||
protocols: ["http", "https"]),
|
name: "plugin_url",
|
||||||
placeholder: const Text(
|
validator: FormBuilderValidators.url(
|
||||||
"Add GitHub/Codeberg URL to plugin repository "
|
protocols: ["http", "https"]),
|
||||||
"or direct link to .smplug file",
|
placeholder: const Text(
|
||||||
|
"Add GitHub/Codeberg URL to plugin repository "
|
||||||
|
"or direct link to .smplug file",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Tooltip(
|
||||||
Tooltip(
|
tooltip: const TooltipContainer(
|
||||||
tooltip: const TooltipContainer(
|
child: Text("Download and install plugin from url"),
|
||||||
child: Text("Download and install plugin from url"),
|
).call,
|
||||||
).call,
|
child: IconButton.secondary(
|
||||||
child: IconButton.secondary(
|
icon: const Icon(SpotubeIcons.download),
|
||||||
icon: const Icon(SpotubeIcons.download),
|
onPressed: () async {
|
||||||
onPressed: () async {
|
if (formKey.currentState?.saveAndValidate() ??
|
||||||
if (formKey.currentState?.saveAndValidate() ?? false) {
|
false) {
|
||||||
final url = formKey.currentState?.fields["plugin_url"]
|
final url = formKey.currentState
|
||||||
?.value as String;
|
?.fields["plugin_url"]?.value as String;
|
||||||
|
|
||||||
if (url.isNotEmpty) {
|
if (url.isNotEmpty) {
|
||||||
final pluginConfig = await pluginsNotifier
|
final pluginConfig = await pluginsNotifier
|
||||||
.downloadAndCachePlugin(url);
|
.downloadAndCachePlugin(url);
|
||||||
|
|
||||||
await pluginsNotifier.addPlugin(pluginConfig);
|
await pluginsNotifier.addPlugin(pluginConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
Tooltip(
|
||||||
Tooltip(
|
tooltip: const TooltipContainer(
|
||||||
tooltip: const TooltipContainer(
|
child: Text("Upload plugin from file"),
|
||||||
child: Text("Upload plugin from file"),
|
).call,
|
||||||
).call,
|
child: IconButton.primary(
|
||||||
child: IconButton.primary(
|
icon: const Icon(SpotubeIcons.upload),
|
||||||
icon: const Icon(SpotubeIcons.upload),
|
onPressed: () async {
|
||||||
onPressed: () async {
|
final result = await FilePicker.platform.pickFiles(
|
||||||
final result = await FilePicker.platform.pickFiles(
|
type: kIsAndroid ? FileType.any : FileType.custom,
|
||||||
type: kIsAndroid ? FileType.any : FileType.custom,
|
allowedExtensions: kIsAndroid ? [] : ["smplug"],
|
||||||
allowedExtensions: kIsAndroid ? [] : ["smplug"],
|
withData: true,
|
||||||
withData: true,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
|
|
||||||
final file = result.files.first;
|
final file = result.files.first;
|
||||||
|
|
||||||
if (file.bytes == null) return;
|
if (file.bytes == null) return;
|
||||||
|
|
||||||
final pluginConfig = await pluginsNotifier
|
final pluginConfig = await pluginsNotifier
|
||||||
.extractPluginArchive(file.bytes!);
|
.extractPluginArchive(file.bytes!);
|
||||||
await pluginsNotifier.addPlugin(pluginConfig);
|
await pluginsNotifier.addPlugin(pluginConfig);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
const SliverGap(12),
|
||||||
const SliverGap(12),
|
if (plugins.asData?.value.plugins.isNotEmpty ?? false)
|
||||||
if (plugins.asData?.value.plugins.isNotEmpty ?? false)
|
SliverToBoxAdapter(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Gap(8),
|
||||||
|
const Text("Installed").h4,
|
||||||
|
const Gap(8),
|
||||||
|
const Expanded(child: Divider()),
|
||||||
|
const Gap(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverGap(20),
|
||||||
|
SliverList.separated(
|
||||||
|
itemCount: plugins.asData?.value.plugins.length ?? 0,
|
||||||
|
separatorBuilder: (context, index) => const Gap(12),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final plugin = plugins.asData!.value.plugins[index];
|
||||||
|
final isDefault =
|
||||||
|
plugins.asData!.value.defaultPlugin == index;
|
||||||
|
return MetadataInstalledPluginItem(
|
||||||
|
plugin: plugin,
|
||||||
|
isDefault: isDefault,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SliverGap(12),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
const Text("Installed").h4,
|
const Text("Available plugins").h4,
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SliverGap(20),
|
const SliverGap(12),
|
||||||
SliverList.separated(
|
SliverInfiniteList(
|
||||||
itemCount: plugins.asData?.value.plugins.length ?? 0,
|
isLoading: pluginReposSnapshot.isLoading &&
|
||||||
separatorBuilder: (context, index) => const Gap(12),
|
!pluginReposSnapshot.isLoadingNextPage,
|
||||||
itemBuilder: (context, index) {
|
itemCount: pluginRepos.length,
|
||||||
final plugin = plugins.asData!.value.plugins[index];
|
onFetchData: pluginReposNotifier.fetchMore,
|
||||||
final isDefault = plugins.asData!.value.defaultPlugin == index;
|
loadingBuilder: (context) {
|
||||||
return MetadataInstalledPluginItem(
|
return Skeletonizer(
|
||||||
plugin: plugin,
|
enabled: true,
|
||||||
isDefault: isDefault,
|
child: MetadataPluginRepositoryItem(
|
||||||
);
|
pluginRepo: MetadataPluginRepository(
|
||||||
},
|
name: "Loading...",
|
||||||
),
|
description: "Loading...",
|
||||||
const SliverGap(12),
|
repoUrl: "",
|
||||||
SliverToBoxAdapter(
|
owner: "",
|
||||||
child: Row(
|
),
|
||||||
children: [
|
|
||||||
const Gap(8),
|
|
||||||
const Text("Available plugins").h4,
|
|
||||||
const Gap(8),
|
|
||||||
const Expanded(child: Divider()),
|
|
||||||
const Gap(8),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SliverGap(12),
|
|
||||||
SliverInfiniteList(
|
|
||||||
isLoading: pluginReposSnapshot.isLoading &&
|
|
||||||
!pluginReposSnapshot.isLoadingNextPage,
|
|
||||||
itemCount: pluginRepos.length,
|
|
||||||
onFetchData: pluginReposNotifier.fetchMore,
|
|
||||||
loadingBuilder: (context) {
|
|
||||||
return Skeletonizer(
|
|
||||||
enabled: true,
|
|
||||||
child: MetadataPluginRepositoryItem(
|
|
||||||
pluginRepo: MetadataPluginRepository(
|
|
||||||
name: "Loading...",
|
|
||||||
description: "Loading...",
|
|
||||||
repoUrl: "",
|
|
||||||
owner: "",
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
itemBuilder: (context, index) {
|
||||||
itemBuilder: (context, index) {
|
final pluginRepo = pluginRepos[index];
|
||||||
final pluginRepo = pluginRepos[index];
|
|
||||||
|
|
||||||
return MetadataPluginRepositoryItem(
|
return MetadataPluginRepositoryItem(
|
||||||
pluginRepo: pluginRepo,
|
pluginRepo: pluginRepo,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SliverCrossAxisConstrained(
|
SliverCrossAxisConstrained(
|
||||||
maxCrossAxisExtent: 720,
|
maxCrossAxisExtent: 720,
|
||||||
child: SliverFillRemaining(
|
child: SliverFillRemaining(
|
||||||
hasScrollBody: false,
|
hasScrollBody: false,
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: 20),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Card(
|
child: Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 12,
|
spacing: 12,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
const Icon(SpotubeIcons.warning, size: 16),
|
const Icon(SpotubeIcons.warning, size: 16),
|
||||||
const Text(
|
const Text(
|
||||||
"Disclaimer",
|
"Disclaimer",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
).bold,
|
).bold,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Text(
|
const Text(
|
||||||
"The Spotube team does not hold any responsibility (including legal) for any \"Third-party\" plugins.\n"
|
"The Spotube team does not hold any responsibility (including legal) for any \"Third-party\" plugins.\n"
|
||||||
"Please use them at your own risk. For any bugs/issues, please report them to the plugin repository."
|
"Please use them at your own risk. For any bugs/issues, please report them to the plugin repository."
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"If any \"Third-party\" plugin is breaking ToS/DMCA of any service/legal entity, "
|
"If any \"Third-party\" plugin is breaking ToS/DMCA of any service/legal entity, "
|
||||||
"please ask the \"Third-party\" plugin author or the hosting platform .e.g GitHub/Codeberg to take action. "
|
"please ask the \"Third-party\" plugin author or the hosting platform .e.g GitHub/Codeberg to take action. "
|
||||||
"Above listed (\"Third-party\" labelled) are all public/community maintained plugins. We're not curating them, "
|
"Above listed (\"Third-party\" labelled) are all public/community maintained plugins. We're not curating them, "
|
||||||
"so we cannot take any action on them.\n\n",
|
"so we cannot take any action on them.\n\n",
|
||||||
).muted.xSmall,
|
).muted.xSmall,
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
|
import 'package:spotube/provider/metadata_plugin/core/auth.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
|
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
|
||||||
|
|
||||||
class MetadataPluginAlbumReleasesNotifier
|
class MetadataPluginAlbumReleasesNotifier
|
||||||
@ -17,7 +17,7 @@ class MetadataPluginAlbumReleasesNotifier
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
build() async {
|
build() async {
|
||||||
ref.watch(metadataPluginProvider);
|
ref.watch(metadataPluginAuthenticatedProvider);
|
||||||
return await fetch(0, 20);
|
return await fetch(0, 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
|
import 'package:spotube/provider/metadata_plugin/core/auth.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/utils/family_paginated.dart';
|
import 'package:spotube/provider/metadata_plugin/utils/family_paginated.dart';
|
||||||
|
|
||||||
class MetadataPluginBrowseSectionItemsNotifier
|
class MetadataPluginBrowseSectionItemsNotifier
|
||||||
@ -19,7 +19,7 @@ class MetadataPluginBrowseSectionItemsNotifier
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
build(arg) async {
|
build(arg) async {
|
||||||
ref.watch(metadataPluginProvider);
|
ref.watch(metadataPluginAuthenticatedProvider);
|
||||||
return await fetch(0, 20);
|
return await fetch(0, 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
|
import 'package:spotube/provider/metadata_plugin/core/auth.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
|
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
|
||||||
|
|
||||||
class MetadataPluginBrowseSectionsNotifier
|
class MetadataPluginBrowseSectionsNotifier
|
||||||
@ -19,7 +19,7 @@ class MetadataPluginBrowseSectionsNotifier
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
build() async {
|
build() async {
|
||||||
ref.watch(metadataPluginProvider);
|
ref.watch(metadataPluginAuthenticatedProvider);
|
||||||
return await fetch(0, 20);
|
return await fetch(0, 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,9 +69,8 @@ abstract class ServiceUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return "$title ${artists.map((e) => e.replaceAll(",", " ")).join(", ")}"
|
return "$title ${artists.map((e) => e.replaceAll(",", " ")).join(", ")}"
|
||||||
.toLowerCase()
|
|
||||||
.replaceAll(RegExp(r"\s*\[[^\]]*]"), ' ')
|
.replaceAll(RegExp(r"\s*\[[^\]]*]"), ' ')
|
||||||
.replaceAll(RegExp(r"\sfeat\.|\sft\."), ' ')
|
.replaceAll(RegExp(r"\sfeat\.|\sft\.", caseSensitive: false), ' ')
|
||||||
.replaceAll(RegExp(r"\s+"), ' ')
|
.replaceAll(RegExp(r"\s+"), ' ')
|
||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user