fix: unescape html escape values #1300

This commit is contained in:
Kingkor Roy Tirtho 2024-07-09 22:13:05 +06:00
parent c7d8ed567a
commit 44861a9f5c
8 changed files with 65 additions and 30 deletions

View File

@ -8,18 +8,10 @@ import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/hover_builder.dart'; import 'package:spotube/components/hover_builder.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/string.dart';
import 'package:spotube/hooks/utils/use_breakpoint_value.dart'; import 'package:spotube/hooks/utils/use_breakpoint_value.dart';
import 'package:spotube/hooks/utils/use_brightness_value.dart'; import 'package:spotube/hooks/utils/use_brightness_value.dart';
final htmlTagRegexp = RegExp(r"<[^>]*>", caseSensitive: true);
String? useDescription(String? description) {
return useMemoized(() {
if (description == null) return null;
return description.replaceAll(htmlTagRegexp, '');
}, [description]);
}
class PlaybuttonCard extends HookWidget { class PlaybuttonCard extends HookWidget {
final void Function()? onTap; final void Function()? onTap;
final void Function()? onPlaybuttonPressed; final void Function()? onPlaybuttonPressed;
@ -66,8 +58,7 @@ class PlaybuttonCard extends HookWidget {
others: 15, others: 15,
); );
final cleanDescription = useDescription(description); var unescapeHtml = description?.unescapeHtml();
return Container( return Container(
constraints: BoxConstraints(maxWidth: size), constraints: BoxConstraints(maxWidth: size),
margin: margin, margin: margin,
@ -205,11 +196,11 @@ class PlaybuttonCard extends HookWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
if (cleanDescription != null) if (description != null)
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0), padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: AutoSizeText( child: AutoSizeText(
cleanDescription, unescapeHtml!,
maxLines: 2, maxLines: 2,
style: theme.textTheme.bodySmall?.copyWith( style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurface.withOpacity(.5), color: theme.colorScheme.onSurface.withOpacity(.5),

View File

@ -5,12 +5,12 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/components/playbutton_card.dart';
import 'package:spotube/components/tracks_view/sections/header/header_actions.dart'; import 'package:spotube/components/tracks_view/sections/header/header_actions.dart';
import 'package:spotube/components/tracks_view/sections/header/header_buttons.dart'; import 'package:spotube/components/tracks_view/sections/header/header_buttons.dart';
import 'package:spotube/components/tracks_view/track_view_props.dart'; import 'package:spotube/components/tracks_view/track_view_props.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/string.dart';
import 'package:spotube/hooks/utils/use_palette_color.dart'; import 'package:spotube/hooks/utils/use_palette_color.dart';
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
@ -24,8 +24,6 @@ class TrackViewFlexHeader extends HookConsumerWidget {
final defaultTextStyle = DefaultTextStyle.of(context); final defaultTextStyle = DefaultTextStyle.of(context);
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
final description = useDescription(props.description);
final palette = usePaletteColor(props.image, ref); final palette = usePaletteColor(props.image, ref);
return IconTheme( return IconTheme(
@ -127,10 +125,10 @@ class TrackViewFlexHeader extends HookConsumerWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
if (description != null && if (props.description != null &&
description.isNotEmpty) props.description!.isNotEmpty)
Text( Text(
description, props.description!.unescapeHtml(),
style: style:
defaultTextStyle.style.copyWith( defaultTextStyle.style.copyWith(
color: palette.bodyTextColor, color: palette.bodyTextColor,

View File

@ -7,7 +7,7 @@ extension UnescapeHtml on String {
} }
extension NullableUnescapeHtml on String? { extension NullableUnescapeHtml on String? {
String? unescapeHtml() => this == null ? null : htmlEscape.convert(this!); String? unescapeHtml() => this?.unescapeHtml();
} }
extension StringExtension on String { extension StringExtension on String {

View File

@ -15,6 +15,7 @@ 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/extensions/string.dart';
import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/spotify/spotify.dart';
import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/provider/spotify_provider.dart';
@ -54,7 +55,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
text: updatingPlaylist?.name, text: updatingPlaylist?.name,
); );
final description = useTextEditingController( final description = useTextEditingController(
text: updatingPlaylist?.description, text: updatingPlaylist?.description?.unescapeHtml(),
); );
final public = useState( final public = useState(
updatingPlaylist?.public ?? false, updatingPlaylist?.public ?? false,

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/components/playbutton_card.dart';
import 'package:spotube/extensions/image.dart'; import 'package:spotube/extensions/image.dart';
import 'package:spotube/extensions/string.dart';
import 'package:spotube/pages/playlist/playlist.dart'; import 'package:spotube/pages/playlist/playlist.dart';
import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/service_utils.dart';
@ -28,7 +28,7 @@ class StatsPlaylistItem extends StatelessWidget {
), ),
title: Text(playlist.name!), title: Text(playlist.name!),
subtitle: Text( subtitle: Text(
playlist.description!.replaceAll(htmlTagRegexp, ''), playlist.description?.unescapeHtml() ?? '',
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),

View File

@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart' hide Page; import 'package:flutter/material.dart' hide Page;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
@ -12,19 +13,33 @@ import 'package:spotube/provider/spotify/spotify.dart';
class PlaylistPage extends HookConsumerWidget { class PlaylistPage extends HookConsumerWidget {
static const name = "playlist"; static const name = "playlist";
final PlaylistSimple playlist; final PlaylistSimple _playlist;
const PlaylistPage({ const PlaylistPage({
super.key, super.key,
required this.playlist, required PlaylistSimple playlist,
}); }) : _playlist = playlist;
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final playlist = ref
.watch(
favoritePlaylistsProvider.select(
(value) => value.whenData(
(value) =>
value.items.firstWhereOrNull((s) => s.id == _playlist.id),
),
),
)
.asData
?.value ??
_playlist;
final tracks = ref.watch(playlistTracksProvider(playlist.id!)); final tracks = ref.watch(playlistTracksProvider(playlist.id!));
final tracksNotifier = final tracksNotifier =
ref.watch(playlistTracksProvider(playlist.id!).notifier); ref.watch(playlistTracksProvider(playlist.id!).notifier);
final isFavoritePlaylist = final isFavoritePlaylist =
ref.watch(isFavoritePlaylistProvider(playlist.id!)); ref.watch(isFavoritePlaylistProvider(playlist.id!));
final favoritePlaylistsNotifier = final favoritePlaylistsNotifier =
ref.watch(favoritePlaylistsProvider.notifier); ref.watch(favoritePlaylistsProvider.notifier);

View File

@ -51,6 +51,20 @@ class FavoritePlaylistsNotifier
); );
} }
void updatePlaylist(PlaylistSimple playlist) {
if (state.value == null) return;
if (state.value!.items.none((e) => e.id == playlist.id)) return;
state = AsyncData(
state.value!.copyWith(
items: state.value!.items
.map((element) => element.id == playlist.id ? playlist : element)
.toList(),
),
);
}
Future<void> addFavorite(PlaylistSimple playlist) async { Future<void> addFavorite(PlaylistSimple playlist) async {
await update((state) async { await update((state) async {
await spotify.playlists.followPlaylist(playlist.id!); await spotify.playlists.followPlaylist(playlist.id!);

View File

@ -71,16 +71,32 @@ class PlaylistNotifier extends FamilyAsyncNotifier<Playlist, String> {
state.id!, state.id!,
input.base64Image!, input.base64Image!,
); );
final playlist = await spotify.playlists.get(state.id!);
ref.read(favoritePlaylistsProvider.notifier).updatePlaylist(playlist);
return playlist;
} }
return spotify.playlists.get(state.id!); final playlist = Playlist.fromJson(
} catch (e) { {
...state.toJson(),
'name': input.playlistName,
'collaborative': input.collaborative,
'description': input.description,
'public': input.public,
},
);
ref.read(favoritePlaylistsProvider.notifier).updatePlaylist(playlist);
return playlist;
} catch (e, stack) {
onError?.call(e); onError?.call(e);
AppLogger.reportError(e, stack);
rethrow; rethrow;
} }
}); });
ref.invalidate(favoritePlaylistsProvider);
} }
} }