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/image/universal_image.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_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 {
final void Function()? onTap;
final void Function()? onPlaybuttonPressed;
@ -66,8 +58,7 @@ class PlaybuttonCard extends HookWidget {
others: 15,
);
final cleanDescription = useDescription(description);
var unescapeHtml = description?.unescapeHtml();
return Container(
constraints: BoxConstraints(maxWidth: size),
margin: margin,
@ -205,11 +196,11 @@ class PlaybuttonCard extends HookWidget {
overflow: TextOverflow.ellipsis,
),
),
if (cleanDescription != null)
if (description != null)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: AutoSizeText(
cleanDescription,
unescapeHtml!,
maxLines: 2,
style: theme.textTheme.bodySmall?.copyWith(
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:spotube/collections/assets.gen.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_buttons.dart';
import 'package:spotube/components/tracks_view/track_view_props.dart';
import 'package:gap/gap.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/utils/platform.dart';
@ -24,8 +24,6 @@ class TrackViewFlexHeader extends HookConsumerWidget {
final defaultTextStyle = DefaultTextStyle.of(context);
final mediaQuery = MediaQuery.of(context);
final description = useDescription(props.description);
final palette = usePaletteColor(props.image, ref);
return IconTheme(
@ -127,10 +125,10 @@ class TrackViewFlexHeader extends HookConsumerWidget {
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 10),
if (description != null &&
description.isNotEmpty)
if (props.description != null &&
props.description!.isNotEmpty)
Text(
description,
props.description!.unescapeHtml(),
style:
defaultTextStyle.style.copyWith(
color: palette.bodyTextColor,

View File

@ -7,7 +7,7 @@ extension UnescapeHtml on String {
}
extension NullableUnescapeHtml on String? {
String? unescapeHtml() => this == null ? null : htmlEscape.convert(this!);
String? unescapeHtml() => this?.unescapeHtml();
}
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/context.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_provider.dart';
@ -54,7 +55,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
text: updatingPlaylist?.name,
);
final description = useTextEditingController(
text: updatingPlaylist?.description,
text: updatingPlaylist?.description?.unescapeHtml(),
);
final public = useState(
updatingPlaylist?.public ?? false,

View File

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

View File

@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart' hide Page;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
@ -12,19 +13,33 @@ import 'package:spotube/provider/spotify/spotify.dart';
class PlaylistPage extends HookConsumerWidget {
static const name = "playlist";
final PlaylistSimple playlist;
final PlaylistSimple _playlist;
const PlaylistPage({
super.key,
required this.playlist,
});
required PlaylistSimple playlist,
}) : _playlist = playlist;
@override
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 tracksNotifier =
ref.watch(playlistTracksProvider(playlist.id!).notifier);
final isFavoritePlaylist =
ref.watch(isFavoritePlaylistProvider(playlist.id!));
final favoritePlaylistsNotifier =
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 {
await update((state) async {
await spotify.playlists.followPlaylist(playlist.id!);

View File

@ -71,16 +71,32 @@ class PlaylistNotifier extends FamilyAsyncNotifier<Playlist, String> {
state.id!,
input.base64Image!,
);
final playlist = await spotify.playlists.get(state.id!);
ref.read(favoritePlaylistsProvider.notifier).updatePlaylist(playlist);
return playlist;
}
return spotify.playlists.get(state.id!);
} catch (e) {
final playlist = Playlist.fromJson(
{
...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);
AppLogger.reportError(e, stack);
rethrow;
}
});
ref.invalidate(favoritePlaylistsProvider);
}
}