From ccb3cee1d60798be006c4c27e8c124ecf7632956 Mon Sep 17 00:00:00 2001 From: JothishKamal Date: Mon, 30 Dec 2024 00:42:22 +0530 Subject: [PATCH] feat: accept/reject integrated --- lib/app/router/router.dart | 4 +- lib/providers/admin_screen_provider.dart | 43 +++++- lib/providers/playlist_provider.dart | 3 - lib/utils/api_util.dart | 3 - lib/view/screens/admin_screen.dart | 7 +- lib/view/screens/create_screen.dart | 38 ++++- lib/view/screens/home_screen.dart | 160 +--------------------- lib/view/screens/yay_screen.dart | 13 +- lib/view/widgets/custom_title.dart | 39 ++++++ lib/view/widgets/music_list_item.dart | 58 +++++++- lib/view/widgets/new_playlist_button.dart | 27 ++++ lib/view/widgets/playlist_card.dart | 96 +++++++++++++ 12 files changed, 303 insertions(+), 188 deletions(-) create mode 100644 lib/view/widgets/custom_title.dart create mode 100644 lib/view/widgets/new_playlist_button.dart create mode 100644 lib/view/widgets/playlist_card.dart diff --git a/lib/app/router/router.dart b/lib/app/router/router.dart index 2e89132..a2e2c6c 100644 --- a/lib/app/router/router.dart +++ b/lib/app/router/router.dart @@ -9,10 +9,12 @@ import 'package:spotify_collab_app/view/screens/signup_screen.dart'; import 'package:spotify_collab_app/view/screens/home_screen.dart'; import 'package:spotify_collab_app/view/screens/connect_screen.dart'; import 'package:spotify_collab_app/view/screens/splash_screen.dart'; -import 'package:spotify_collab_app/view/screens/yay_screen.dart'; final routerProvider = Provider((ref) { return GoRouter( + errorBuilder: (context, state) { + return const HomeScreen(); + }, initialLocation: '/', routes: [ GoRoute( diff --git a/lib/providers/admin_screen_provider.dart b/lib/providers/admin_screen_provider.dart index e19ef71..f37c265 100644 --- a/lib/providers/admin_screen_provider.dart +++ b/lib/providers/admin_screen_provider.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:developer'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -56,4 +55,46 @@ class SongsNotifier extends StateNotifier { success: emptySongs.success, ); } + + Future acceptSong(String uuid, String uri) async { + try { + final response = await apiUtil.post( + '/v1/songs/accept', + { + 'playlist_uuid': uuid, + 'song_uri': uri, + }, + ); + + log('Song accepted'); + if (response.statusCode == 200) { + fetchSongs(uuid); + return true; + } + } catch (e) { + log(e.toString()); + } + return false; + } + + Future rejectSong(String uuid, String uri) async { + try { + final response = await apiUtil.post( + '/v1/songs/reject', + { + 'playlist_uuid': uuid, + 'song_uri': uri, + }, + ); + + log('Song rejected'); + if (response.statusCode == 200) { + fetchSongs(uuid); + return true; + } + } catch (e) { + log(e.toString()); + } + return false; + } } diff --git a/lib/providers/playlist_provider.dart b/lib/providers/playlist_provider.dart index d67b26a..1169554 100644 --- a/lib/providers/playlist_provider.dart +++ b/lib/providers/playlist_provider.dart @@ -1,7 +1,4 @@ -import 'dart:developer'; - import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotify_collab_app/utils/api_util.dart'; import 'package:spotify_collab_app/view/models/playlist_success_response.dart'; diff --git a/lib/utils/api_util.dart b/lib/utils/api_util.dart index bb7f080..39df58c 100644 --- a/lib/utils/api_util.dart +++ b/lib/utils/api_util.dart @@ -1,6 +1,3 @@ -import 'dart:convert'; -import 'dart:developer'; - import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotify_collab_app/constants/constants.dart'; diff --git a/lib/view/screens/admin_screen.dart b/lib/view/screens/admin_screen.dart index a087b4f..eced647 100644 --- a/lib/view/screens/admin_screen.dart +++ b/lib/view/screens/admin_screen.dart @@ -39,8 +39,6 @@ class _AdminScreenState extends ConsumerState Future _fetchSongs() async { final playlistNotifier = ref.read(playlistProvider.notifier); final songsNotifier = ref.read(songsProvider.notifier); - await playlistNotifier.fetchPlaylists(); - await songsNotifier.fetchSongs(playlistNotifier.selectedPlaylistUuid!); try { await songsNotifier.fetchSongs(playlistNotifier.selectedPlaylistUuid!); @@ -225,7 +223,7 @@ class _AdminScreenState extends ConsumerState return (items.isEmpty) ? const Center( child: Text( - "No songs", + "No songs in playlist.", style: TextStyle( fontFamily: 'Gotham', color: Colors.white, @@ -250,6 +248,7 @@ class _AdminScreenState extends ConsumerState final item = items[index]; return isPlaylist ? MusicListItem( + id: item.track!.track!.id!, title: item.track!.track!.name!, subtitle: item.track!.track!.artists! .map((artist) => artist.name!) @@ -261,6 +260,7 @@ class _AdminScreenState extends ConsumerState isParticipant: isParticipant, ) : MusicListItem( + id: item.track!.track!.id!, title: item.track!.track!.album!.name!, subtitle: null, imageUrl: null, @@ -351,6 +351,7 @@ class _AdminScreenState extends ConsumerState itemBuilder: (context, index) { final item = items[index]; return MusicListItem( + id: item.id!, title: item.name!, subtitle: item.artists! .map((artist) => artist.name!) diff --git a/lib/view/screens/create_screen.dart b/lib/view/screens/create_screen.dart index f50812c..e401382 100644 --- a/lib/view/screens/create_screen.dart +++ b/lib/view/screens/create_screen.dart @@ -138,7 +138,15 @@ class CreateScreen extends ConsumerWidget { if (eventNameController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Event name cannot be empty'), + content: Text( + 'Event name cannot be empty', + style: TextStyle( + fontFamily: 'Gotham', + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), backgroundColor: Colors.red, ), ); @@ -151,10 +159,18 @@ class CreateScreen extends ConsumerWidget { if (response.statusCode == 200) { if (response.data["message"] == "playlist successfully created") { + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: - Text('Playlist successfully created'), + content: Text( + 'Playlist successfully created', + style: TextStyle( + fontFamily: 'Gotham', + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), backgroundColor: Colors.green, ), ); @@ -162,10 +178,20 @@ class CreateScreen extends ConsumerWidget { context.go('/home'); } } else { + if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Failed to create playlist'), - backgroundColor: Colors.red, + SnackBar( + content: const Text( + 'Failed to create playlist', + style: TextStyle( + fontFamily: 'Gotham', + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + backgroundColor: + Colors.red.withOpacity(0.8), ), ); } diff --git a/lib/view/screens/home_screen.dart b/lib/view/screens/home_screen.dart index ddbe220..639727c 100644 --- a/lib/view/screens/home_screen.dart +++ b/lib/view/screens/home_screen.dart @@ -3,6 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:spotify_collab_app/providers/playlist_provider.dart'; +import 'package:spotify_collab_app/view/widgets/custom_title.dart'; +import 'package:spotify_collab_app/view/widgets/new_playlist_button.dart'; +import 'package:spotify_collab_app/view/widgets/playlist_card.dart'; class HomeScreen extends ConsumerWidget { const HomeScreen({super.key}); @@ -106,161 +109,4 @@ class HomeScreen extends ConsumerWidget { } } -class CustomTitle extends StatelessWidget { - const CustomTitle({ - super.key, - required this.title, - }); - final String title; - - @override - Widget build(BuildContext context) { - return Row( - children: [ - const Spacer(), - const SizedBox(width: 20), - Text( - title, - style: const TextStyle( - fontFamily: "Gotham", - fontWeight: FontWeight.w700, - fontSize: 34, - shadows: [ - Shadow(offset: Offset(0, 1), color: Color(0xffDA84FE)), - ], - ), - ), - Column( - children: [ - SvgPicture.asset('assets/highlight.svg'), - const SizedBox(height: 30), - ], - ), - const Spacer(), - ], - ); - } -} - -class PlaylistCard extends ConsumerWidget { - const PlaylistCard({ - super.key, - this.isActive = false, - required this.name, - required this.id, - }); - - final bool isActive; - final String? name; - final String? id; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final playlistNotifier = ref.read(playlistProvider.notifier); - - return Row( - children: [ - Expanded( - child: Container( - height: 102, - decoration: BoxDecoration( - color: isActive ? const Color(0xff5822EE) : Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(8)), - ), - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Row( - children: [ - Container( - height: 57, - width: 57, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: Colors.grey, - ), - child: const Icon(Icons.music_note, size: 32), - ), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - name ?? '', - style: TextStyle( - fontFamily: "Gotham", - fontWeight: FontWeight.w700, - fontSize: 16, - color: isActive ? Colors.white : Colors.black, - ), - ), - ], - ), - const Spacer(), - Align( - alignment: Alignment.bottomRight, - child: Container( - width: 97, - height: 31, - decoration: BoxDecoration( - color: - !isActive ? const Color(0xff5822EE) : Colors.white, - borderRadius: - const BorderRadius.all(Radius.circular(8)), - ), - child: Center( - child: TextButton( - onPressed: () { - playlistNotifier.selectedPlaylistUuid = id ?? ''; - playlistNotifier.selectedPlaylistName = name ?? ''; - context.push("/admin"); - }, - child: Text( - isActive ? "Manage" : "View", - style: TextStyle( - color: isActive - ? const Color(0xff5822EE) - : Colors.white, - fontFamily: "Gotham", - fontWeight: FontWeight.w700, - fontSize: 16, - ), - ), - )), - ), - ), - ], - ), - ), - ), - ), - ], - ); - } -} - -class NewPlaylistButton extends StatelessWidget { - const NewPlaylistButton({super.key}); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Expanded( - child: Container( - height: 59, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(8)), - ), - child: const Icon( - Icons.add_circle, - color: Color(0xff5822EE), - size: 43, - ), - ), - ), - ], - ); - } -} diff --git a/lib/view/screens/yay_screen.dart b/lib/view/screens/yay_screen.dart index 3525ed1..a7da2b1 100644 --- a/lib/view/screens/yay_screen.dart +++ b/lib/view/screens/yay_screen.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:spotify_collab_app/constants/constants.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:text_marquee/text_marquee.dart'; class YayScreen extends ConsumerStatefulWidget { @@ -27,11 +25,6 @@ class ConnectScreenState extends ConsumerState { super.dispose(); } - Future _launchSpotifyLogin() async { - const url = '$devUrl/v1/auth/spotify/login/app'; - launchUrl(Uri.parse(url)); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -117,7 +110,7 @@ class PlaylistCard extends StatelessWidget { this.isActive = false, this.name = "DevJams' 24", this.host = "Souvik", - this.img = "assets/dino.png"}); + this.img = "assets/dino.png", String? id}); final bool isActive; final String name; @@ -163,7 +156,9 @@ class PlaylistCard extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(height: 10,), + const SizedBox( + height: 10, + ), Row(children: [ const Text( "Event:", diff --git a/lib/view/widgets/custom_title.dart b/lib/view/widgets/custom_title.dart new file mode 100644 index 0000000..4df5c4f --- /dev/null +++ b/lib/view/widgets/custom_title.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class CustomTitle extends StatelessWidget { + const CustomTitle({ + super.key, + required this.title, + }); + + final String title; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + const Spacer(), + const SizedBox(width: 20), + Text( + title, + style: const TextStyle( + fontFamily: "Gotham", + fontWeight: FontWeight.w700, + fontSize: 34, + shadows: [ + Shadow(offset: Offset(0, 1), color: Color(0xffDA84FE)), + ], + ), + ), + Column( + children: [ + SvgPicture.asset('assets/highlight.svg'), + const SizedBox(height: 30), + ], + ), + const Spacer(), + ], + ); + } +} diff --git a/lib/view/widgets/music_list_item.dart b/lib/view/widgets/music_list_item.dart index 13e17e3..39c309e 100644 --- a/lib/view/widgets/music_list_item.dart +++ b/lib/view/widgets/music_list_item.dart @@ -2,17 +2,20 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotify_collab_app/providers/admin_screen_provider.dart'; +import 'package:spotify_collab_app/providers/playlist_provider.dart'; class MusicListItem extends ConsumerWidget { + final String? id; final String title; final String? subtitle; final String? imageUrl; final bool isPlaylist; final bool isRequest; final bool isParticipant; - const MusicListItem({ super.key, + required this.id, required this.title, this.subtitle, this.imageUrl, @@ -33,6 +36,8 @@ class MusicListItem extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final songsNotifier = ref.read(songsProvider.notifier); + final playlistsProvider = ref.watch(playlistProvider.notifier); return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), leading: isParticipant @@ -99,12 +104,55 @@ class MusicListItem extends ConsumerWidget { children: [ IconButton( icon: const Icon(Icons.close, color: Colors.red), - onPressed: () {}, + onPressed: () async { + final success = await songsNotifier.rejectSong( + playlistsProvider.selectedPlaylistUuid!, id!); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + success ? 'Song rejected' : 'Failed to reject song', + style: const TextStyle( + fontFamily: 'Gotham', + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + backgroundColor: success + ? Colors.green + : Colors.red.withOpacity(0.8), + ), + ); + } + }, ), IconButton( - icon: const Icon(Icons.check, color: Colors.green), - onPressed: () {}, - ), + icon: const Icon(Icons.check, color: Colors.green), + onPressed: () async { + final success = await songsNotifier.acceptSong( + playlistsProvider.selectedPlaylistUuid!, id!); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + success + ? 'Song accepted' + : 'Failed to accept song', + style: const TextStyle( + fontFamily: 'Gotham', + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + backgroundColor: success + ? Colors.green + : Colors.red.withOpacity(0.8), + ), + ); + } + }), ], ) : isParticipant diff --git a/lib/view/widgets/new_playlist_button.dart b/lib/view/widgets/new_playlist_button.dart new file mode 100644 index 0000000..1a64131 --- /dev/null +++ b/lib/view/widgets/new_playlist_button.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class NewPlaylistButton extends StatelessWidget { + const NewPlaylistButton({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Container( + height: 59, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + child: const Icon( + Icons.add_circle, + color: Color(0xff5822EE), + size: 43, + ), + ), + ), + ], + ); + } +} diff --git a/lib/view/widgets/playlist_card.dart b/lib/view/widgets/playlist_card.dart new file mode 100644 index 0000000..d16b01f --- /dev/null +++ b/lib/view/widgets/playlist_card.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:spotify_collab_app/providers/playlist_provider.dart'; + +class PlaylistCard extends ConsumerWidget { + const PlaylistCard({ + super.key, + this.isActive = false, + required this.name, + required this.id, + }); + + final bool isActive; + final String? name; + final String? id; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final playlistNotifier = ref.read(playlistProvider.notifier); + + return Row( + children: [ + Expanded( + child: Container( + height: 102, + decoration: BoxDecoration( + color: isActive ? const Color(0xff5822EE) : Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + child: Row( + children: [ + Container( + height: 57, + width: 57, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey, + ), + child: const Icon(Icons.music_note, + size: 32, color: Colors.white), + ), + const SizedBox(width: 16), + Expanded( + child: Text( + name ?? '', + style: TextStyle( + fontFamily: "Gotham", + fontWeight: FontWeight.w700, + fontSize: 16, + color: isActive ? Colors.white : Colors.black, + ), + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ), + const SizedBox(width: 16), + Container( + width: 97, + height: 31, + decoration: BoxDecoration( + color: isActive ? Colors.white : const Color(0xff5822EE), + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + child: TextButton( + onPressed: () { + playlistNotifier.selectedPlaylistUuid = id ?? ''; + playlistNotifier.selectedPlaylistName = name ?? ''; + context.push("/admin"); + }, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + child: Text( + isActive ? "Manage" : "View", + style: TextStyle( + color: + isActive ? const Color(0xff5822EE) : Colors.white, + fontFamily: "Gotham", + fontWeight: FontWeight.w700, + fontSize: 14, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ], + ); + } +}