mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: unescape html escape values #1300
This commit is contained in:
parent
c7d8ed567a
commit
44861a9f5c
@ -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),
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
),
|
),
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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!);
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user