mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
Merge pull request #1688 from KRTirtho/feat/desktop-webview
feat(desktop): webview based login support
This commit is contained in:
commit
15211123aa
2
.github/Dockerfile.flutter_distributor
vendored
2
.github/Dockerfile.flutter_distributor
vendored
@ -4,7 +4,7 @@ ARG FLUTTER_VERSION
|
|||||||
|
|
||||||
RUN apt-get clean &&\
|
RUN apt-get clean &&\
|
||||||
apt-get update &&\
|
apt-get update &&\
|
||||||
apt-get install -y bash curl file git unzip xz-utils zip libglu1-mesa cmake tar clang ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev rpm && \
|
apt-get install -y bash curl file git unzip xz-utils zip libglu1-mesa cmake tar clang ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev rpm libwebkit2gtk-4.1-0 libwebkit2gtk-4.1-dev libsoup-3.0-0 libsoup-3.0-dev && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /home/flutter
|
WORKDIR /home/flutter
|
||||||
|
2
.github/workflows/spotube-release-binary.yml
vendored
2
.github/workflows/spotube-release-binary.yml
vendored
@ -20,7 +20,7 @@ on:
|
|||||||
description: Dry run without uploading to release
|
description: Dry run without uploading to release
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FLUTTER_VERSION: 3.22.1
|
FLUTTER_VERSION: 3.19.6
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
@ -123,16 +123,16 @@ Do the following:
|
|||||||
- Install Development dependencies in linux
|
- Install Development dependencies in linux
|
||||||
- Debian (>=12/Bookworm)/Ubuntu
|
- Debian (>=12/Bookworm)/Ubuntu
|
||||||
```bash
|
```bash
|
||||||
$ apt-get install mpv libmpv-dev libappindicator3-1 gir1.2-appindicator3-0.1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev avahi-daemon avahi-discover avahi-utils libnss-mdns mdns-scan
|
$ apt-get install mpv libmpv-dev libappindicator3-1 gir1.2-appindicator3-0.1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev avahi-daemon avahi-discover avahi-utils libnss-mdns mdns-scan libwebkit2gtk-4.1-0 libwebkit2gtk-4.1-dev libsoup-3.0-0 libsoup-3.0-dev
|
||||||
```
|
```
|
||||||
- Use `libjsoncpp1` instead of `libjsoncpp25` (for Ubuntu < 22.04)
|
- Use `libjsoncpp1` instead of `libjsoncpp25` (for Ubuntu < 22.04)
|
||||||
- Arch/Manjaro
|
- Arch/Manjaro
|
||||||
```bash
|
```bash
|
||||||
yay -S mpv libappindicator-gtk3 libsecret jsoncpp libnotify avahi nss-mdns mdns-scan
|
yay -S mpv libappindicator-gtk3 libsecret jsoncpp libnotify avahi nss-mdns mdns-scan webkit2gtk-4.1 libsoup3
|
||||||
```
|
```
|
||||||
- Fedora
|
- Fedora
|
||||||
```bash
|
```bash
|
||||||
dnf install mpv mpv-devel libappindicator-gtk3 libappindicator-gtk3-devel libsecret libsecret-devel jsoncpp jsoncpp-devel libnotify libnotify-devel avahi mdns-scan nss-mdns
|
dnf install mpv mpv-devel libappindicator-gtk3 libappindicator-gtk3-devel libsecret libsecret-devel jsoncpp jsoncpp-devel libnotify libnotify-devel avahi mdns-scan nss-mdns webkit2gtk4.1 webkit2gtk4.1-devel libsoup3 libsoup3-devel
|
||||||
```
|
```
|
||||||
- Clone the Repo
|
- Clone the Repo
|
||||||
- Create a `.env` in root of the project following the `.env.example` template
|
- Create a `.env` in root of the project following the `.env.example` template
|
||||||
|
@ -37,7 +37,7 @@ class InstallDependenciesCommand extends Command {
|
|||||||
await shell.run(
|
await shell.run(
|
||||||
"""
|
"""
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y tar clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev
|
sudo apt-get install -y tar clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev libwebkit2gtk-4.1-0 libwebkit2gtk-4.1-dev libsoup-3.0-0 libsoup-3.0-dev
|
||||||
""",
|
""",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -34,12 +34,9 @@ import 'package:spotube/pages/stats/streams/streams.dart';
|
|||||||
import 'package:spotube/pages/track/track.dart';
|
import 'package:spotube/pages/track/track.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
|
||||||
import 'package:spotube/components/spotube_page_route.dart';
|
import 'package:spotube/components/spotube_page_route.dart';
|
||||||
import 'package:spotube/pages/artist/artist.dart';
|
import 'package:spotube/pages/artist/artist.dart';
|
||||||
import 'package:spotube/pages/library/library.dart';
|
import 'package:spotube/pages/library/library.dart';
|
||||||
import 'package:spotube/pages/desktop_login/login_tutorial.dart';
|
|
||||||
import 'package:spotube/pages/desktop_login/desktop_login.dart';
|
|
||||||
import 'package:spotube/pages/lyrics/lyrics.dart';
|
import 'package:spotube/pages/lyrics/lyrics.dart';
|
||||||
import 'package:spotube/pages/root/root_app.dart';
|
import 'package:spotube/pages/root/root_app.dart';
|
||||||
import 'package:spotube/pages/settings/settings.dart';
|
import 'package:spotube/pages/settings/settings.dart';
|
||||||
@ -313,16 +310,8 @@ final routerProvider = Provider((ref) {
|
|||||||
path: "/login",
|
path: "/login",
|
||||||
name: WebViewLogin.name,
|
name: WebViewLogin.name,
|
||||||
parentNavigatorKey: rootNavigatorKey,
|
parentNavigatorKey: rootNavigatorKey,
|
||||||
pageBuilder: (context, state) => SpotubePage(
|
|
||||||
child: kIsMobile ? const WebViewLogin() : const DesktopLoginPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: "/login-tutorial",
|
|
||||||
name: LoginTutorial.name,
|
|
||||||
parentNavigatorKey: rootNavigatorKey,
|
|
||||||
pageBuilder: (context, state) => const SpotubePage(
|
pageBuilder: (context, state) => const SpotubePage(
|
||||||
child: LoginTutorial(),
|
child: WebViewLogin(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:dart_discord_rpc/dart_discord_rpc.dart';
|
import 'package:dart_discord_rpc/dart_discord_rpc.dart';
|
||||||
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -46,6 +47,12 @@ import 'package:timezone/data/latest.dart' as tz;
|
|||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
Future<void> main(List<String> rawArgs) async {
|
Future<void> main(List<String> rawArgs) async {
|
||||||
|
if (rawArgs.contains("web_view_title_bar")) {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
if (runWebViewTitleBarWidget(rawArgs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
final arguments = await startCLI(rawArgs);
|
final arguments = await startCLI(rawArgs);
|
||||||
AppLogger.initialize(arguments["verbose"]);
|
AppLogger.initialize(arguments["verbose"]);
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ LazyDatabase _openConnection() {
|
|||||||
return LazyDatabase(() async {
|
return LazyDatabase(() async {
|
||||||
// put the database file, called db.sqlite here, into the documents folder
|
// put the database file, called db.sqlite here, into the documents folder
|
||||||
// for your app.
|
// for your app.
|
||||||
final dbFolder = await getApplicationDocumentsDirectory();
|
final dbFolder = await getApplicationSupportDirectory();
|
||||||
final file = File(join(dbFolder.path, 'db.sqlite'));
|
final file = File(join(dbFolder.path, 'db.sqlite'));
|
||||||
|
|
||||||
// Also work around limitations on old Android versions
|
// Also work around limitations on old Android versions
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:spotube/extensions/context.dart';
|
|
||||||
|
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
|
||||||
|
|
||||||
class TokenLoginForm extends HookConsumerWidget {
|
|
||||||
final void Function()? onDone;
|
|
||||||
const TokenLoginForm({
|
|
||||||
super.key,
|
|
||||||
this.onDone,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, ref) {
|
|
||||||
final authenticationNotifier = ref.watch(authenticationProvider.notifier);
|
|
||||||
final directCodeController = useTextEditingController();
|
|
||||||
|
|
||||||
final isLoading = useState(false);
|
|
||||||
|
|
||||||
return ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
maxWidth: 400,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
controller: directCodeController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: context.l10n.spotify_cookie("\"sp_dc\""),
|
|
||||||
labelText: context.l10n.cookie_name_cookie("sp_dc"),
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.visiblePassword,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: isLoading.value
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
try {
|
|
||||||
isLoading.value = true;
|
|
||||||
if (directCodeController.text.isEmpty) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(context.l10n.fill_in_all_fields),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final cookieHeader =
|
|
||||||
"sp_dc=${directCodeController.text.trim()}";
|
|
||||||
|
|
||||||
await authenticationNotifier.login(cookieHeader);
|
|
||||||
if (context.mounted) {
|
|
||||||
onDone?.call();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(context.l10n.submit),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
|
||||||
import 'package:spotube/modules/desktop_login/login_form.dart';
|
|
||||||
import 'package:spotube/components/titlebar/titlebar.dart';
|
|
||||||
import 'package:spotube/extensions/constrains.dart';
|
|
||||||
import 'package:spotube/extensions/context.dart';
|
|
||||||
import 'package:spotube/pages/mobile_login/mobile_login.dart';
|
|
||||||
|
|
||||||
class DesktopLoginPage extends HookConsumerWidget {
|
|
||||||
static const name = WebViewLogin.name;
|
|
||||||
const DesktopLoginPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, ref) {
|
|
||||||
final mediaQuery = MediaQuery.of(context);
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
final color = theme.colorScheme.surfaceVariant.withOpacity(.3);
|
|
||||||
|
|
||||||
return SafeArea(
|
|
||||||
child: Scaffold(
|
|
||||||
appBar: const PageWindowTitleBar(
|
|
||||||
leading: BackButton(),
|
|
||||||
),
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.all(10),
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: color,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Assets.spotubeLogoPng.image(
|
|
||||||
width: MediaQuery.of(context).size.width *
|
|
||||||
(mediaQuery.mdAndDown ? .5 : .3),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
context.l10n.add_spotify_credentials,
|
|
||||||
style: theme.textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
context.l10n.credentials_will_not_be_shared_disclaimer,
|
|
||||||
style: theme.textTheme.labelMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TokenLoginForm(
|
|
||||||
onDone: () => GoRouter.of(context).go("/"),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Wrap(
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(context.l10n.know_how_to_login),
|
|
||||||
TextButton(
|
|
||||||
child: Text(
|
|
||||||
context.l10n.follow_step_by_step_guide,
|
|
||||||
),
|
|
||||||
onPressed: () => GoRouter.of(context).push(
|
|
||||||
"/login-tutorial",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:introduction_screen/introduction_screen.dart';
|
|
||||||
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
|
||||||
import 'package:spotube/modules/desktop_login/login_form.dart';
|
|
||||||
import 'package:spotube/components/links/hyper_link.dart';
|
|
||||||
import 'package:spotube/components/titlebar/titlebar.dart';
|
|
||||||
import 'package:spotube/extensions/context.dart';
|
|
||||||
import 'package:spotube/pages/home/home.dart';
|
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
|
||||||
|
|
||||||
class LoginTutorial extends ConsumerWidget {
|
|
||||||
static const name = "login_tutorial";
|
|
||||||
const LoginTutorial({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, ref) {
|
|
||||||
final auth = ref.watch(authenticationProvider);
|
|
||||||
final key = GlobalKey<State<IntroductionScreen>>();
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
final pageDecoration = PageDecoration(
|
|
||||||
bodyTextStyle: theme.textTheme.bodyMedium!,
|
|
||||||
titleTextStyle: theme.textTheme.headlineMedium!,
|
|
||||||
);
|
|
||||||
return Scaffold(
|
|
||||||
appBar: PageWindowTitleBar(
|
|
||||||
leading: TextButton(
|
|
||||||
child: Text(context.l10n.exit),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: IntroductionScreen(
|
|
||||||
key: key,
|
|
||||||
globalBackgroundColor: theme.scaffoldBackgroundColor,
|
|
||||||
overrideBack: OutlinedButton(
|
|
||||||
child: Center(child: Text(context.l10n.previous)),
|
|
||||||
onPressed: () {
|
|
||||||
(key.currentState as IntroductionScreenState).previous();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
overrideNext: FilledButton(
|
|
||||||
child: Center(child: Text(context.l10n.next)),
|
|
||||||
onPressed: () {
|
|
||||||
(key.currentState as IntroductionScreenState).next();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
showBackButton: true,
|
|
||||||
overrideDone: FilledButton(
|
|
||||||
onPressed: auth.asData?.value != null
|
|
||||||
? () {
|
|
||||||
ServiceUtils.pushNamed(context, HomePage.name);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: Center(child: Text(context.l10n.done)),
|
|
||||||
),
|
|
||||||
pages: [
|
|
||||||
PageViewModel(
|
|
||||||
decoration: pageDecoration,
|
|
||||||
title: context.l10n.step_1,
|
|
||||||
image: Assets.tutorial.step1.image(),
|
|
||||||
bodyWidget: Wrap(
|
|
||||||
children: [
|
|
||||||
Text(context.l10n.first_go_to),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
const Hyperlink(
|
|
||||||
"accounts.spotify.com ",
|
|
||||||
"https://accounts.spotify.com",
|
|
||||||
),
|
|
||||||
Text(context.l10n.login_if_not_logged_in),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PageViewModel(
|
|
||||||
decoration: pageDecoration,
|
|
||||||
title: context.l10n.step_2,
|
|
||||||
image: Assets.tutorial.step2.image(),
|
|
||||||
bodyWidget:
|
|
||||||
Text(context.l10n.step_2_steps, textAlign: TextAlign.left),
|
|
||||||
),
|
|
||||||
PageViewModel(
|
|
||||||
decoration: pageDecoration,
|
|
||||||
title: context.l10n.step_3,
|
|
||||||
image: Assets.tutorial.step3.image(),
|
|
||||||
bodyWidget:
|
|
||||||
Text(context.l10n.step_3_steps, textAlign: TextAlign.left),
|
|
||||||
),
|
|
||||||
if (auth.asData?.value != null)
|
|
||||||
PageViewModel(
|
|
||||||
decoration: pageDecoration.copyWith(
|
|
||||||
bodyAlignment: Alignment.center,
|
|
||||||
),
|
|
||||||
title: context.l10n.success_emoji,
|
|
||||||
image: Assets.success.image(),
|
|
||||||
body: context.l10n.success_message,
|
|
||||||
)
|
|
||||||
else
|
|
||||||
PageViewModel(
|
|
||||||
decoration: pageDecoration,
|
|
||||||
title: context.l10n.step_4,
|
|
||||||
bodyWidget: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
context.l10n.step_4_steps,
|
|
||||||
style: theme.textTheme.labelMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TokenLoginForm(
|
|
||||||
onDone: () {
|
|
||||||
GoRouter.of(context).go("/");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,24 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/modules/settings/section_card_with_heading.dart';
|
import 'package:spotube/modules/settings/section_card_with_heading.dart';
|
||||||
import 'package:spotube/components/image/universal_image.dart';
|
import 'package:spotube/components/image/universal_image.dart';
|
||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
|
import 'package:spotube/pages/mobile_login/mobile_login.dart';
|
||||||
import 'package:spotube/pages/profile/profile.dart';
|
import 'package:spotube/pages/profile/profile.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
import 'package:spotube/provider/scrobbler/scrobbler.dart';
|
import 'package:spotube/provider/scrobbler/scrobbler.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
|
|
||||||
class SettingsAccountSection extends HookConsumerWidget {
|
class SettingsAccountSection extends HookConsumerWidget {
|
||||||
@ -23,6 +30,7 @@ class SettingsAccountSection extends HookConsumerWidget {
|
|||||||
final router = GoRouter.of(context);
|
final router = GoRouter.of(context);
|
||||||
|
|
||||||
final auth = ref.watch(authenticationProvider);
|
final auth = ref.watch(authenticationProvider);
|
||||||
|
final authNotifier = ref.watch(authenticationProvider.notifier);
|
||||||
final scrobbler = ref.watch(scrobblerProvider);
|
final scrobbler = ref.watch(scrobblerProvider);
|
||||||
final me = ref.watch(meProvider);
|
final me = ref.watch(meProvider);
|
||||||
final meData = me.asData?.value;
|
final meData = me.asData?.value;
|
||||||
@ -32,6 +40,52 @@ class SettingsAccountSection extends HookConsumerWidget {
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void onLogin() async {
|
||||||
|
if (kIsMobile) {
|
||||||
|
router.pushNamed(WebViewLogin.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final exp = RegExp(r"https:\/\/accounts.spotify.com\/.+\/status");
|
||||||
|
final applicationSupportDir = await getApplicationSupportDirectory();
|
||||||
|
final userDataFolder = Directory(
|
||||||
|
join(applicationSupportDir.path, "webview_window_Webview2"));
|
||||||
|
|
||||||
|
if (!await userDataFolder.exists()) {
|
||||||
|
await userDataFolder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
final webview = await WebviewWindow.create(
|
||||||
|
configuration: CreateConfiguration(
|
||||||
|
title: "Spotify Login",
|
||||||
|
titleBarTopPadding: kIsMacOS ? 20 : 0,
|
||||||
|
windowHeight: 720,
|
||||||
|
windowWidth: 1280,
|
||||||
|
userDataFolderWindows: userDataFolder.path,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
webview
|
||||||
|
..setBrightness(theme.colorScheme.brightness)
|
||||||
|
..launch("https://accounts.spotify.com/")
|
||||||
|
..setOnUrlRequestCallback((url) {
|
||||||
|
if (exp.hasMatch(url)) {
|
||||||
|
webview.getAllCookies().then((cookies) async {
|
||||||
|
final cookieHeader =
|
||||||
|
"sp_dc=${cookies.firstWhere((element) => element.name.contains("sp_dc")).value.replaceAll("\u0000", "")}";
|
||||||
|
|
||||||
|
await authNotifier.login(cookieHeader);
|
||||||
|
|
||||||
|
webview.close();
|
||||||
|
if (context.mounted) {
|
||||||
|
context.go("/");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return SectionCardWithHeading(
|
return SectionCardWithHeading(
|
||||||
heading: context.l10n.account,
|
heading: context.l10n.account,
|
||||||
children: [
|
children: [
|
||||||
@ -70,17 +124,11 @@ class SettingsAccountSection extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: constrains.mdAndUp
|
onTap: constrains.mdAndUp ? null : onLogin,
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
router.push("/login");
|
|
||||||
},
|
|
||||||
trailing: constrains.smAndDown
|
trailing: constrains.smAndDown
|
||||||
? null
|
? null
|
||||||
: FilledButton(
|
: FilledButton(
|
||||||
onPressed: () {
|
onPressed: onLogin,
|
||||||
router.push("/login");
|
|
||||||
},
|
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
shape: MaterialStateProperty.all(
|
shape: MaterialStateProperty.all(
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
|
@ -287,6 +287,10 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
|
|||||||
await ref.read(sourcedTrackProvider(intendedActiveTrack).future);
|
await ref.read(sourcedTrackProvider(intendedActiveTrack).future);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(medias.isEmpty) return;
|
||||||
|
|
||||||
|
await removeCollections(state.collections);
|
||||||
|
|
||||||
await audioPlayer.openPlaylist(
|
await audioPlayer.openPlaylist(
|
||||||
medias,
|
medias,
|
||||||
initialIndex: initialIndex,
|
initialIndex: initialIndex,
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio/io.dart';
|
import 'package:dio/io.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
@ -96,7 +97,7 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
|
|||||||
|
|
||||||
await database
|
await database
|
||||||
.into(database.authenticationTable)
|
.into(database.authenticationTable)
|
||||||
.insert(refreshedCredentials);
|
.insert(refreshedCredentials, mode: InsertMode.replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<AuthenticationTableCompanion> credentialsFromCookie(
|
Future<AuthenticationTableCompanion> credentialsFromCookie(
|
||||||
@ -160,6 +161,9 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
|
|||||||
WebStorageManager.instance().deleteAllData();
|
WebStorageManager.instance().deleteAllData();
|
||||||
CookieManager.instance().deleteAllCookies();
|
CookieManager.instance().deleteAllCookies();
|
||||||
}
|
}
|
||||||
|
if (kIsDesktop) {
|
||||||
|
await WebviewWindow.clearAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,11 +152,12 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cachedLyrics == null || cachedLyrics.lyrics.isEmpty) {
|
if (cachedLyrics == null || cachedLyrics.lyrics.isEmpty) {
|
||||||
await database.into(database.lyricsTable).insertOnConflictUpdate(
|
await database.into(database.lyricsTable).insert(
|
||||||
LyricsTableCompanion.insert(
|
LyricsTableCompanion.insert(
|
||||||
trackId: track.id!,
|
trackId: track.id!,
|
||||||
data: lyrics,
|
data: lyrics,
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ library spotify;
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/provider/database/database.dart';
|
import 'package:spotube/provider/database/database.dart';
|
||||||
import 'package:spotube/provider/spotify/utils/json_cast.dart';
|
import 'package:spotube/provider/spotify/utils/json_cast.dart';
|
||||||
|
@ -43,7 +43,12 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
|||||||
}) async {
|
}) async {
|
||||||
final database = ref.read(databaseProvider);
|
final database = ref.read(databaseProvider);
|
||||||
final cachedSource = await (database.select(database.sourceMatchTable)
|
final cachedSource = await (database.select(database.sourceMatchTable)
|
||||||
..where((s) => s.trackId.equals(track.id!)))
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
|
..limit(1)
|
||||||
|
..orderBy([
|
||||||
|
(s) =>
|
||||||
|
OrderingTerm(expression: s.createdAt, mode: OrderingMode.desc),
|
||||||
|
]))
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
|
|
||||||
if (cachedSource == null ||
|
if (cachedSource == null ||
|
||||||
@ -215,7 +220,11 @@ class JioSaavnSourcedTrack extends SourcedTrack {
|
|||||||
trackId: id!,
|
trackId: id!,
|
||||||
sourceId: info.id,
|
sourceId: info.id,
|
||||||
sourceType: const Value(SourceType.jiosaavn),
|
sourceType: const Value(SourceType.jiosaavn),
|
||||||
|
// Because we're sorting by createdAt in the query
|
||||||
|
// we have to update it to indicate priority
|
||||||
|
createdAt: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
);
|
);
|
||||||
|
|
||||||
return JioSaavnSourcedTrack(
|
return JioSaavnSourcedTrack(
|
||||||
|
@ -52,7 +52,12 @@ class PipedSourcedTrack extends SourcedTrack {
|
|||||||
}) async {
|
}) async {
|
||||||
final database = ref.read(databaseProvider);
|
final database = ref.read(databaseProvider);
|
||||||
final cachedSource = await (database.select(database.sourceMatchTable)
|
final cachedSource = await (database.select(database.sourceMatchTable)
|
||||||
..where((s) => s.trackId.equals(track.id!)))
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
|
..limit(1)
|
||||||
|
..orderBy([
|
||||||
|
(s) =>
|
||||||
|
OrderingTerm(expression: s.createdAt, mode: OrderingMode.desc),
|
||||||
|
]))
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
final preferences = ref.read(userPreferencesProvider);
|
final preferences = ref.read(userPreferencesProvider);
|
||||||
final pipedClient = ref.read(pipedProvider);
|
final pipedClient = ref.read(pipedProvider);
|
||||||
@ -278,7 +283,11 @@ class PipedSourcedTrack extends SourcedTrack {
|
|||||||
trackId: id!,
|
trackId: id!,
|
||||||
sourceId: newSourceInfo.id,
|
sourceId: newSourceInfo.id,
|
||||||
sourceType: const Value(SourceType.youtube),
|
sourceType: const Value(SourceType.youtube),
|
||||||
|
// Because we're sorting by createdAt in the query
|
||||||
|
// we have to update it to indicate priority
|
||||||
|
createdAt: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
);
|
);
|
||||||
|
|
||||||
return PipedSourcedTrack(
|
return PipedSourcedTrack(
|
||||||
|
@ -50,8 +50,14 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
}) async {
|
}) async {
|
||||||
final database = ref.read(databaseProvider);
|
final database = ref.read(databaseProvider);
|
||||||
final cachedSource = await (database.select(database.sourceMatchTable)
|
final cachedSource = await (database.select(database.sourceMatchTable)
|
||||||
..where((s) => s.trackId.equals(track.id!)))
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
.getSingleOrNull();
|
..limit(1)
|
||||||
|
..orderBy([
|
||||||
|
(s) =>
|
||||||
|
OrderingTerm(expression: s.createdAt, mode: OrderingMode.desc),
|
||||||
|
]))
|
||||||
|
.get()
|
||||||
|
.then((s) => s.firstOrNull);
|
||||||
|
|
||||||
if (cachedSource == null || cachedSource.sourceType != SourceType.youtube) {
|
if (cachedSource == null || cachedSource.sourceType != SourceType.youtube) {
|
||||||
final siblings = await fetchSiblings(ref: ref, track: track);
|
final siblings = await fetchSiblings(ref: ref, track: track);
|
||||||
@ -287,12 +293,17 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final database = ref.read(databaseProvider);
|
final database = ref.read(databaseProvider);
|
||||||
|
|
||||||
await database.into(database.sourceMatchTable).insert(
|
await database.into(database.sourceMatchTable).insert(
|
||||||
SourceMatchTableCompanion.insert(
|
SourceMatchTableCompanion.insert(
|
||||||
trackId: id!,
|
trackId: id!,
|
||||||
sourceId: newSourceInfo.id,
|
sourceId: newSourceInfo.id,
|
||||||
sourceType: const Value(SourceType.youtube),
|
sourceType: const Value(SourceType.youtube),
|
||||||
|
// Because we're sorting by createdAt in the query
|
||||||
|
// we have to update it to indicate priority
|
||||||
|
createdAt: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
);
|
);
|
||||||
|
|
||||||
return YoutubeSourcedTrack(
|
return YoutubeSourcedTrack(
|
||||||
|
@ -35,13 +35,14 @@ Future<void> migrateAuthenticationInfo() async {
|
|||||||
|
|
||||||
if (credentials == null) return;
|
if (credentials == null) return;
|
||||||
|
|
||||||
await _database.into(_database.authenticationTable).insertOnConflictUpdate(
|
await _database.into(_database.authenticationTable).insert(
|
||||||
AuthenticationTableCompanion.insert(
|
AuthenticationTableCompanion.insert(
|
||||||
accessToken: DecryptedText(credentials.accessToken),
|
accessToken: DecryptedText(credentials.accessToken),
|
||||||
cookie: DecryptedText(credentials.cookie),
|
cookie: DecryptedText(credentials.cookie),
|
||||||
expiration: credentials.expiration,
|
expiration: credentials.expiration,
|
||||||
id: const Value(0),
|
id: const Value(0),
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
);
|
);
|
||||||
|
|
||||||
AppLogger.log.i("✅ Migrated authentication info");
|
AppLogger.log.i("✅ Migrated authentication info");
|
||||||
@ -58,7 +59,7 @@ Future<void> migratePreferences() async {
|
|||||||
|
|
||||||
if (preferences == null) return;
|
if (preferences == null) return;
|
||||||
|
|
||||||
await _database.into(_database.preferencesTable).insertOnConflictUpdate(
|
await _database.into(_database.preferencesTable).insert(
|
||||||
PreferencesTableCompanion.insert(
|
PreferencesTableCompanion.insert(
|
||||||
id: const Value(0),
|
id: const Value(0),
|
||||||
accentColorScheme: Value(preferences.accentColorScheme),
|
accentColorScheme: Value(preferences.accentColorScheme),
|
||||||
@ -108,6 +109,7 @@ Future<void> migratePreferences() async {
|
|||||||
systemTitleBar: Value(preferences.systemTitleBar),
|
systemTitleBar: Value(preferences.systemTitleBar),
|
||||||
themeMode: Value(preferences.themeMode),
|
themeMode: Value(preferences.themeMode),
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
);
|
);
|
||||||
|
|
||||||
AppLogger.log.i("✅ Migrated preferences");
|
AppLogger.log.i("✅ Migrated preferences");
|
||||||
@ -235,12 +237,13 @@ Future<void> migrateLastFmCredentials() async {
|
|||||||
|
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
|
|
||||||
await _database.into(_database.scrobblerTable).insertOnConflictUpdate(
|
await _database.into(_database.scrobblerTable).insert(
|
||||||
ScrobblerTableCompanion.insert(
|
ScrobblerTableCompanion.insert(
|
||||||
id: const Value(0),
|
id: const Value(0),
|
||||||
passwordHash: DecryptedText(data.passwordHash),
|
passwordHash: DecryptedText(data.passwordHash),
|
||||||
username: data.username,
|
username: data.username,
|
||||||
),
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
);
|
);
|
||||||
|
|
||||||
AppLogger.log.i("✅ Migrated Last.fm credentials");
|
AppLogger.log.i("✅ Migrated Last.fm credentials");
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <dart_discord_rpc/dart_discord_rpc_plugin.h>
|
#include <dart_discord_rpc/dart_discord_rpc_plugin.h>
|
||||||
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
#include <gtk/gtk_plugin.h>
|
#include <gtk/gtk_plugin.h>
|
||||||
@ -24,6 +25,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||||||
g_autoptr(FlPluginRegistrar) dart_discord_rpc_registrar =
|
g_autoptr(FlPluginRegistrar) dart_discord_rpc_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DartDiscordRpcPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DartDiscordRpcPlugin");
|
||||||
dart_discord_rpc_plugin_register_with_registrar(dart_discord_rpc_registrar);
|
dart_discord_rpc_plugin_register_with_registrar(dart_discord_rpc_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
|
||||||
|
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
dart_discord_rpc
|
dart_discord_rpc
|
||||||
|
desktop_webview_window
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
gtk
|
gtk
|
||||||
|
@ -23,6 +23,8 @@ dependencies:
|
|||||||
- avahi-utils
|
- avahi-utils
|
||||||
- libnss-mdns
|
- libnss-mdns
|
||||||
- mdns-scan
|
- mdns-scan
|
||||||
|
- libwebkit2gtk-4.1-0 | libwebkit2gtk-4.0-0
|
||||||
|
- libsoup-3.0-0 | libsoup-2.4-0
|
||||||
|
|
||||||
essential: false
|
essential: false
|
||||||
icon: assets/spotube-logo.png
|
icon: assets/spotube-logo.png
|
||||||
|
@ -16,6 +16,8 @@ requires:
|
|||||||
- avahi
|
- avahi
|
||||||
- mdns-scan
|
- mdns-scan
|
||||||
- nss-mdns
|
- nss-mdns
|
||||||
|
- webkit2gtk4.1
|
||||||
|
- libsoup3
|
||||||
|
|
||||||
display_name: Spotube
|
display_name: Spotube
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import app_links
|
|||||||
import audio_service
|
import audio_service
|
||||||
import audio_session
|
import audio_session
|
||||||
import bonsoir_darwin
|
import bonsoir_darwin
|
||||||
|
import desktop_webview_window
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import flutter_inappwebview_macos
|
import flutter_inappwebview_macos
|
||||||
@ -32,6 +33,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
||||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin"))
|
SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin"))
|
||||||
|
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||||
|
@ -8,6 +8,8 @@ PODS:
|
|||||||
- bonsoir_darwin (0.0.1):
|
- bonsoir_darwin (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- desktop_webview_window (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- file_selector_macos (0.0.1):
|
- file_selector_macos (0.0.1):
|
||||||
@ -70,6 +72,7 @@ DEPENDENCIES:
|
|||||||
- audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`)
|
- audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`)
|
||||||
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
|
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
|
||||||
- bonsoir_darwin (from `Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin`)
|
- bonsoir_darwin (from `Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin`)
|
||||||
|
- desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`)
|
||||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||||
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
|
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
|
||||||
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
||||||
@ -105,6 +108,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos
|
||||||
bonsoir_darwin:
|
bonsoir_darwin:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin
|
||||||
|
desktop_webview_window:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
|
||||||
file_selector_macos:
|
file_selector_macos:
|
||||||
@ -151,6 +156,7 @@ SPEC CHECKSUMS:
|
|||||||
audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9
|
audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9
|
||||||
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
|
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
|
||||||
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
|
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
|
||||||
|
desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a
|
||||||
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
|
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
|
||||||
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
|
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
|
||||||
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
|
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
|
||||||
|
@ -474,6 +474,15 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.10"
|
version: "0.7.10"
|
||||||
|
desktop_webview_window:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "packages/desktop_webview_window"
|
||||||
|
ref: "feat/cookies"
|
||||||
|
resolved-ref: f20e433d4a948515b35089d40069f7dd9bced9e4
|
||||||
|
url: "https://github.com/KRTirtho/flutter-plugins.git"
|
||||||
|
source: git
|
||||||
|
version: "0.2.4"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -24,6 +24,11 @@ dependencies:
|
|||||||
collection: ^1.15.0
|
collection: ^1.15.0
|
||||||
curved_navigation_bar: ^1.0.3
|
curved_navigation_bar: ^1.0.3
|
||||||
dbus: ^0.7.8
|
dbus: ^0.7.8
|
||||||
|
desktop_webview_window:
|
||||||
|
git:
|
||||||
|
url: https://github.com/KRTirtho/flutter-plugins.git
|
||||||
|
ref: feat/cookies
|
||||||
|
path: packages/desktop_webview_window
|
||||||
device_info_plus: ^10.1.0
|
device_info_plus: ^10.1.0
|
||||||
dio: ^5.4.3+1
|
dio: ^5.4.3+1
|
||||||
disable_battery_optimization: ^1.1.1
|
disable_battery_optimization: ^1.1.1
|
||||||
@ -127,7 +132,7 @@ dependencies:
|
|||||||
encrypt: ^5.0.3
|
encrypt: ^5.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.11
|
build_runner: ^2.4.9
|
||||||
crypto: ^3.0.3
|
crypto: ^3.0.3
|
||||||
envied_generator: ^0.5.4+1
|
envied_generator: ^0.5.4+1
|
||||||
flutter_gen_runner: ^5.4.0
|
flutter_gen_runner: ^5.4.0
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <app_links/app_links_plugin_c_api.h>
|
#include <app_links/app_links_plugin_c_api.h>
|
||||||
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
||||||
#include <dart_discord_rpc/dart_discord_rpc_plugin.h>
|
#include <dart_discord_rpc/dart_discord_rpc_plugin.h>
|
||||||
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <local_notifier/local_notifier_plugin.h>
|
#include <local_notifier/local_notifier_plugin.h>
|
||||||
@ -29,6 +30,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
|
||||||
DartDiscordRpcPluginRegisterWithRegistrar(
|
DartDiscordRpcPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("DartDiscordRpcPlugin"));
|
registry->GetRegistrarForPlugin("DartDiscordRpcPlugin"));
|
||||||
|
DesktopWebviewWindowPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
|
@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
app_links
|
app_links
|
||||||
bonsoir_windows
|
bonsoir_windows
|
||||||
dart_discord_rpc
|
dart_discord_rpc
|
||||||
|
desktop_webview_window
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
local_notifier
|
local_notifier
|
||||||
|
Loading…
Reference in New Issue
Block a user