feat(interaction): 支持游客点赞收藏评论
This commit is contained in:
1 parent
188811fde6
commit
a3d50eb935
18 files changed
+364
-121
No files matched your search
@@ -58,14 +58,16 @@ class LikeToggleResult {
|
||||
|
||||
/// 控制器统一返回结果;失败消息可直接展示后端 `message`。
|
||||
class OperationResult<T> {
|
||||
const OperationResult._(this.data, this.errorMessage);
|
||||
const OperationResult._(this.data, this.errorMessage, this.errorCode);
|
||||
|
||||
const OperationResult.success([T? data]) : this._(data, null);
|
||||
const OperationResult.success([T? data]) : this._(data, null, null);
|
||||
|
||||
const OperationResult.failure(this.errorMessage) : data = null;
|
||||
const OperationResult.failure(this.errorMessage, {this.errorCode})
|
||||
: data = null;
|
||||
|
||||
final T? data;
|
||||
final String? errorMessage;
|
||||
final int? errorCode;
|
||||
|
||||
bool get isSuccess => errorMessage == null;
|
||||
}
|
||||
@@ -197,7 +197,10 @@ class CommentController extends ChangeNotifier {
|
||||
_insertPublished(comment, replyTo);
|
||||
return OperationResult.success(comment);
|
||||
} catch (failure) {
|
||||
return OperationResult.failure(_errorMessage(failure));
|
||||
return OperationResult.failure(
|
||||
_errorMessage(failure),
|
||||
errorCode: failure is ApiException ? failure.code : null,
|
||||
);
|
||||
} finally {
|
||||
submitting = false;
|
||||
_notify();
|
||||
@@ -217,7 +220,10 @@ class CommentController extends ChangeNotifier {
|
||||
return const OperationResult.success();
|
||||
} catch (failure) {
|
||||
_restoreComment(comment, location);
|
||||
return OperationResult.failure(_errorMessage(failure));
|
||||
return OperationResult.failure(
|
||||
_errorMessage(failure),
|
||||
errorCode: failure is ApiException ? failure.code : null,
|
||||
);
|
||||
} finally {
|
||||
_deletingCommentIds.remove(comment.id);
|
||||
_notify();
|
||||
@@ -253,7 +259,10 @@ class CommentController extends ChangeNotifier {
|
||||
return OperationResult.success(result);
|
||||
} catch (failure) {
|
||||
_replaceComment(current);
|
||||
return OperationResult.failure(_errorMessage(failure));
|
||||
return OperationResult.failure(
|
||||
_errorMessage(failure),
|
||||
errorCode: failure is ApiException ? failure.code : null,
|
||||
);
|
||||
} finally {
|
||||
_likingCommentIds.remove(comment.id);
|
||||
_notify();
|
||||
@@ -277,7 +286,10 @@ class CommentController extends ChangeNotifier {
|
||||
);
|
||||
return OperationResult.success(reportId);
|
||||
} catch (failure) {
|
||||
return OperationResult.failure(_errorMessage(failure));
|
||||
return OperationResult.failure(
|
||||
_errorMessage(failure),
|
||||
errorCode: failure is ApiException ? failure.code : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../models/drama.dart';
|
||||
import '../remote/api_client.dart';
|
||||
import '../repositories/favorite_repository.dart';
|
||||
|
||||
/// 收藏状态:**以服务端为准**。
|
||||
@@ -24,6 +25,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
bool hasMore = false;
|
||||
String? error;
|
||||
String? loadMoreError;
|
||||
int? lastErrorCode;
|
||||
int _pageIndex = 0;
|
||||
int _requestSeq = 0;
|
||||
bool _disposed = false;
|
||||
@@ -112,11 +114,12 @@ class FavoriteController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// 未登录时不要调用(服务端要求登录态)。
|
||||
/// 每次切换都以接口状态为准;匿名请求由设备号归属。
|
||||
///
|
||||
/// [card] 是当前剧的卡片(播放页/详情页手上就有):收藏成功时直接放进列表,
|
||||
/// 这样"收藏完打开我的收藏"能立刻看到,不必等下一次整表刷新。
|
||||
Future<bool> toggle(String dramaId, {Drama? card}) async {
|
||||
lastErrorCode = null;
|
||||
final wasSaved = _saved.contains(dramaId);
|
||||
final previousCards = cards;
|
||||
final previousTotal = total;
|
||||
@@ -157,7 +160,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
}
|
||||
notify();
|
||||
return true;
|
||||
} catch (_) {
|
||||
} catch (failure) {
|
||||
// 回滚
|
||||
cards = previousCards;
|
||||
total = previousTotal;
|
||||
@@ -168,6 +171,7 @@ class FavoriteController extends ChangeNotifier {
|
||||
_saved.remove(dramaId);
|
||||
}
|
||||
notify();
|
||||
if (failure is ApiException) lastErrorCode = failure.code;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,10 @@ class InteractionController extends ChangeNotifier {
|
||||
} else {
|
||||
likedIds.remove(id);
|
||||
}
|
||||
return OperationResult.failure(_errorMessage(failure));
|
||||
return OperationResult.failure(
|
||||
_errorMessage(failure),
|
||||
errorCode: failure is ApiException ? failure.code : null,
|
||||
);
|
||||
} finally {
|
||||
busyIds.remove(id);
|
||||
_notify();
|
||||
@@ -139,7 +142,10 @@ class InteractionController extends ChangeNotifier {
|
||||
);
|
||||
return OperationResult.success(reportId);
|
||||
} catch (failure) {
|
||||
return OperationResult.failure(_errorMessage(failure));
|
||||
return OperationResult.failure(
|
||||
_errorMessage(failure),
|
||||
errorCode: failure is ApiException ? failure.code : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Future<void> openAuthPage(BuildContext context, {bool signUpMode = false}) {
|
||||
|
||||
/// 受限动作的登录闸:已登录返回 true;未登录弹底部提示并引导登录,返回 false。
|
||||
///
|
||||
/// 首页/发现/详情保持匿名可看,只有收藏、点赞、评论这类"要对到具体账号"的动作才走这里。
|
||||
/// 首页/发现/详情与非付费互动保持匿名可用;解锁、权益与消耗类动作走这里。
|
||||
Future<bool> ensureSignedIn(BuildContext context) async {
|
||||
final auth = context.read<AuthController>();
|
||||
if (auth.isSignedIn) return true;
|
||||
@@ -76,6 +76,7 @@ Future<bool> ensureSignedIn(BuildContext context) async {
|
||||
|
||||
if (go == true && context.mounted) {
|
||||
await openAuthPage(context);
|
||||
if (context.mounted) return context.read<AuthController>().isSignedIn;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,9 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
||||
),
|
||||
),
|
||||
if (_tagValues(context, drama).isNotEmpty)
|
||||
SliverToBoxAdapter(child: DramaTags(tags: _tagValues(context, drama))),
|
||||
SliverToBoxAdapter(
|
||||
child: DramaTags(tags: _tagValues(context, drama)),
|
||||
),
|
||||
SliverToBoxAdapter(child: _episodes(l10n, drama, continueIndex)),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 24)),
|
||||
],
|
||||
@@ -396,11 +398,14 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
||||
}
|
||||
|
||||
Future<void> _toggleFavorite(AppLocalizations l10n) async {
|
||||
if (!await ensureSignedIn(context)) return;
|
||||
if (!mounted) return;
|
||||
unawaited(context.read<Haptics>().lightImpact());
|
||||
final ok = await logic.toggleFavorite();
|
||||
if (ok || !mounted) return;
|
||||
if (logic.favorites.lastErrorCode == 200007) {
|
||||
await ensureSignedIn(context);
|
||||
return;
|
||||
}
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(l10n.favoriteToggleFailed)));
|
||||
@@ -443,7 +448,9 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
||||
}
|
||||
|
||||
List<String> _tagValues(BuildContext context, Drama drama) {
|
||||
final localized = drama.localizedTypeLabels(Localizations.localeOf(context).languageCode);
|
||||
final localized = drama.localizedTypeLabels(
|
||||
Localizations.localeOf(context).languageCode,
|
||||
);
|
||||
final result = <String>[];
|
||||
if (localized.isNotEmpty) {
|
||||
result.addAll(localized.take(_maxTags));
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../core/data/models/drama.dart';
|
||||
import '../../core/data/services/auth_controller.dart';
|
||||
import '../../core/widgets/auth_gate.dart';
|
||||
|
||||
/// 评论入口统一登录闸:登录成功且页面仍存在时再打开评论面板。
|
||||
Future<void> openCommentsWithAuth(
|
||||
/// 评论列表与非付费互动均支持设备身份,不再把登录作为入口前置条件。
|
||||
Future<void> openComments(
|
||||
BuildContext context,
|
||||
Drama drama, {
|
||||
required Future<void> Function(BuildContext context, Drama drama) show,
|
||||
}) async {
|
||||
final auth = context.read<AuthController>();
|
||||
if (auth.isSignedIn) {
|
||||
await show(context, drama);
|
||||
return;
|
||||
}
|
||||
await ensureSignedIn(context);
|
||||
if (!context.mounted || !auth.isSignedIn) return;
|
||||
await show(context, drama);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ class PlayerBinding {
|
||||
required PlaybackPositionCache positionCache,
|
||||
WalletController? wallet,
|
||||
DownloadController? downloads,
|
||||
bool Function()? isSignedIn,
|
||||
}) => PlayerLogic(
|
||||
history: history,
|
||||
playback: playback,
|
||||
@@ -22,5 +23,6 @@ class PlayerBinding {
|
||||
pictureInPicture: AndroidPictureInPicture(),
|
||||
wallet: wallet,
|
||||
downloads: downloads,
|
||||
isSignedIn: isSignedIn,
|
||||
);
|
||||
}
|
||||
@@ -53,10 +53,12 @@ class PlayerLogic extends ChangeNotifier {
|
||||
PlaybackPositionCache? positionCache,
|
||||
Future<void> Function()? waitForFrame,
|
||||
DateTime Function()? clock,
|
||||
bool Function()? isSignedIn,
|
||||
}) : positionCache = positionCache ?? PlaybackPositionCache(),
|
||||
_waitForFrame =
|
||||
waitForFrame ?? (() => WidgetsBinding.instance.endOfFrame),
|
||||
_clock = clock ?? DateTime.now;
|
||||
_clock = clock ?? DateTime.now,
|
||||
_isSignedIn = isSignedIn ?? (() => true);
|
||||
|
||||
final WatchHistoryRepository history;
|
||||
final PlaybackRepository playback;
|
||||
@@ -66,6 +68,7 @@ class PlayerLogic extends ChangeNotifier {
|
||||
final PlaybackPositionCache positionCache;
|
||||
final Future<void> Function() _waitForFrame;
|
||||
final DateTime Function() _clock;
|
||||
final bool Function() _isSignedIn;
|
||||
|
||||
late PageController pages;
|
||||
late PlaybackQueue queue;
|
||||
@@ -797,7 +800,10 @@ class PlayerLogic extends ChangeNotifier {
|
||||
/// 金币解锁当前集;成功后强制刷新票据并直接起播。
|
||||
Future<bool> unlockEpisode(int index) async {
|
||||
final service = wallet;
|
||||
if (service == null || index < 0 || index >= sessionEpisodes.length) {
|
||||
if (service == null ||
|
||||
!_isSignedIn() ||
|
||||
index < 0 ||
|
||||
index >= sessionEpisodes.length) {
|
||||
return false;
|
||||
}
|
||||
final episode = sessionEpisodes[index];
|
||||
@@ -1232,6 +1238,7 @@ class PlayerLogic extends ChangeNotifier {
|
||||
int? revision,
|
||||
bool? userWantsPlay,
|
||||
}) async {
|
||||
if (index < 0 || index >= sessionEpisodes.length) return;
|
||||
final feedRevisionAtStart = revision ?? feedRevision;
|
||||
final request = ++pageRequest;
|
||||
cancelScrub();
|
||||
@@ -1257,6 +1264,10 @@ class PlayerLogic extends ChangeNotifier {
|
||||
!isMounted()) {
|
||||
return;
|
||||
}
|
||||
if (!_isSignedIn()) {
|
||||
unawaited(refreshEpisodeStats(index));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 起播前先要票据:拿不到就不起播,交给三态 UI 提示(失败可重试)
|
||||
final ready = await ensureTicket(index);
|
||||
|
||||
@@ -12,6 +12,7 @@ import '../../core/data/models/drama.dart';
|
||||
import '../../core/data/models/episode_stats.dart';
|
||||
import '../../core/data/repositories/comment_repository.dart';
|
||||
import '../../core/data/services/catalog_controller.dart';
|
||||
import '../../core/data/services/auth_controller.dart';
|
||||
import '../../core/data/services/favorite_controller.dart';
|
||||
import '../../core/data/services/download_controller.dart';
|
||||
import '../../core/data/services/interaction_controller.dart';
|
||||
@@ -168,6 +169,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
positionCache: context.read<PlaybackPositionCache>(),
|
||||
wallet: widget.wallet,
|
||||
downloads: widget.downloads,
|
||||
isSignedIn: () => context.read<AuthController>().isSignedIn,
|
||||
);
|
||||
logic.onEpisodeStatsUpdated = _applyEpisodeStats;
|
||||
logic.attach(widget, isMounted: () => mounted);
|
||||
@@ -590,7 +592,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
Episode episode,
|
||||
) async {
|
||||
final comments = context.read<CommentRepository>();
|
||||
await openCommentsWithAuth(
|
||||
await openComments(
|
||||
context,
|
||||
drama,
|
||||
show: (context, drama) =>
|
||||
@@ -617,6 +619,13 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
Widget _buildPlayer(BuildContext context) {
|
||||
final interactions = context.watch<InteractionController>();
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final indices = logic.visibleIndices;
|
||||
if (indices.isEmpty) {
|
||||
return const ColoredBox(
|
||||
color: AppColors.mediaBlack,
|
||||
child: SizedBox.expand(),
|
||||
);
|
||||
}
|
||||
if (kDebugMode && !_loggedStartConditions) {
|
||||
_loggedStartConditions = true;
|
||||
debugPrint(
|
||||
@@ -630,7 +639,6 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
'mayPlay=${logic.session.debugMayPlay}',
|
||||
);
|
||||
}
|
||||
final indices = logic.visibleIndices;
|
||||
final activePort = logic.session.active;
|
||||
final activeVideo = activePort is VideoPlayerPort ? activePort : null;
|
||||
final canWaitForVideoFrame =
|
||||
|
||||
@@ -65,6 +65,11 @@ extension _PlayerViewActions on _PlayerPageState {
|
||||
) async {
|
||||
final wallet = logic.wallet;
|
||||
if (wallet == null) return;
|
||||
if (!context.read<AuthController>().isSignedIn &&
|
||||
!await ensureSignedIn(context)) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
await showModalBottomSheet<bool>(
|
||||
context: context,
|
||||
backgroundColor: AppColors.transparent,
|
||||
@@ -81,7 +86,7 @@ extension _PlayerViewActions on _PlayerPageState {
|
||||
}
|
||||
|
||||
Future<bool> _unlockEpisode(BuildContext context, int index) async {
|
||||
if (!await ensureSignedIn(context) || !context.mounted) return false;
|
||||
if (!context.read<AuthController>().isSignedIn) return false;
|
||||
final ok = await logic.unlockEpisode(index);
|
||||
if (ok || !context.mounted) return ok;
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
@@ -119,27 +124,6 @@ extension _PlayerViewActions on _PlayerPageState {
|
||||
}
|
||||
return;
|
||||
}
|
||||
final signedIn = await ensureSignedIn(context);
|
||||
if (kDebugMode) {
|
||||
debugPrint(
|
||||
'[player-like] phase=auth-result '
|
||||
'episodeId=${episode.id} signedIn=$signedIn '
|
||||
'mounted=${context.mounted}',
|
||||
);
|
||||
}
|
||||
if (!signedIn || !context.mounted) return;
|
||||
final busyAfterAuth = interactions.isEpisodeBusy(episode.id);
|
||||
final likedAfterAuth = interactions.isEpisodeLiked(episode.id);
|
||||
if (busyAfterAuth || (likeOnly && likedAfterAuth)) {
|
||||
if (kDebugMode) {
|
||||
debugPrint(
|
||||
'[player-like] phase=toggle-skipped-after-auth '
|
||||
'episodeId=${episode.id} '
|
||||
'reason=${busyAfterAuth ? 'busy' : 'already-liked'}',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final result = await interactions.toggleEpisodeLike(episode.id);
|
||||
if (kDebugMode) {
|
||||
debugPrint(
|
||||
@@ -163,6 +147,10 @@ extension _PlayerViewActions on _PlayerPageState {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
if (result.errorCode == 200007) {
|
||||
await ensureSignedIn(context);
|
||||
return;
|
||||
}
|
||||
showCenteredToast(
|
||||
context,
|
||||
result.errorMessage ?? AppLocalizations.of(context)!.likeFailed,
|
||||
@@ -171,7 +159,6 @@ extension _PlayerViewActions on _PlayerPageState {
|
||||
|
||||
Future<void> _toggleSave(BuildContext context, Drama drama) async {
|
||||
final favorites = context.read<FavoriteController>();
|
||||
if (!await ensureSignedIn(context) || !context.mounted) return;
|
||||
unawaited(context.read<Haptics>().lightImpact());
|
||||
final wasSaved = favorites.isSaved(drama.id);
|
||||
final ok = await favorites.toggle(drama.id, card: drama);
|
||||
@@ -183,6 +170,10 @@ extension _PlayerViewActions on _PlayerPageState {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (favorites.lastErrorCode == 200007) {
|
||||
await ensureSignedIn(context);
|
||||
return;
|
||||
}
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.favoriteToggleFailed),
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../core/data/models/comment.dart';
|
||||
import '../../../core/data/services/auth_controller.dart';
|
||||
import '../../../core/data/services/comment_controller.dart';
|
||||
import '../../../core/platform/haptics.dart';
|
||||
import '../../../core/platform/keyboard_inset.dart';
|
||||
@@ -196,23 +195,20 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
return;
|
||||
}
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
if (result.errorCode == 200007) {
|
||||
await ensureSignedIn(context);
|
||||
return;
|
||||
}
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(result.errorMessage ?? l10n.commentPostFailed)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _authenticate() async {
|
||||
await ensureSignedIn(context);
|
||||
if (!mounted || !context.read<AuthController>().isSignedIn) return;
|
||||
_inputFocus.requestFocus();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final signedIn = context.watch<AuthController>().isSignedIn;
|
||||
final hasContent = _input.text.trim().isNotEmpty;
|
||||
final canPost = signedIn && hasContent && !widget.controller.submitting;
|
||||
final canPost = hasContent && !widget.controller.submitting;
|
||||
final multiline = _input.text.contains('\n');
|
||||
final bottomInset = _isAndroid
|
||||
? _pinnedKeyboardInset
|
||||
@@ -247,8 +243,8 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
key: const Key('comment-composer-input'),
|
||||
controller: _input,
|
||||
focusNode: _inputFocus,
|
||||
readOnly: !signedIn,
|
||||
canRequestFocus: signedIn,
|
||||
readOnly: false,
|
||||
canRequestFocus: true,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
maxLength: _maxCommentLength,
|
||||
@@ -262,9 +258,7 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
),
|
||||
cursorColor: AppColors.blue,
|
||||
decoration: InputDecoration(
|
||||
hintText: signedIn
|
||||
? l10n.writeComment
|
||||
: l10n.commentLoginHint,
|
||||
hintText: l10n.writeComment,
|
||||
hintStyle: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: 14,
|
||||
@@ -278,7 +272,7 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
),
|
||||
onTap: signedIn ? null : _authenticate,
|
||||
onTap: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
@@ -11,11 +11,9 @@ import '../../../core/data/models/comment.dart';
|
||||
import '../../../core/data/models/drama.dart';
|
||||
import '../../../core/data/repositories/comment_repository.dart';
|
||||
import '../../../core/data/repositories/interaction_repository.dart';
|
||||
import '../../../core/data/services/auth_controller.dart';
|
||||
import '../../../core/data/services/comment_controller.dart';
|
||||
import '../../../core/formatting/error_text.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/widgets/auth_gate.dart';
|
||||
import '../../../core/widgets/empty_state.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
import 'comment_composer_sheet.dart';
|
||||
@@ -29,7 +27,7 @@ const double _sheetRadius = 20;
|
||||
const double _headerHeight = 44;
|
||||
const double _composerTopGap = 8;
|
||||
|
||||
/// 服务端评论底部弹窗:匿名列表、游标翻页、回复与登录发布。
|
||||
/// 服务端评论底部弹窗:匿名列表、游标翻页、回复与设备身份发布。
|
||||
class DramaCommentsSheet extends StatefulWidget {
|
||||
const DramaCommentsSheet({super.key, required this.drama, this.episodeId});
|
||||
|
||||
@@ -92,8 +90,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
}
|
||||
|
||||
Future<void> _openComposer({DramaComment? replyTo}) async {
|
||||
await ensureSignedIn(context);
|
||||
if (!mounted || !context.read<AuthController>().isSignedIn) return;
|
||||
if (!mounted) return;
|
||||
_removeComposerOverlay();
|
||||
final entry = OverlayEntry(
|
||||
builder: (_) => CommentComposerSheet(
|
||||
@@ -222,7 +219,6 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
Widget _content(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final count = _serverCount ?? _controller.exactCount;
|
||||
final signedIn = context.watch<AuthController>().isSignedIn;
|
||||
final composerReserve = _composerHeight + _composerTopGap;
|
||||
return Column(
|
||||
key: const Key('comment-sheet-content'),
|
||||
@@ -269,7 +265,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
Padding(
|
||||
key: const Key('comment-composer-area'),
|
||||
padding: const EdgeInsets.only(top: _composerTopGap),
|
||||
child: _fakeComposer(l10n, signedIn: signedIn),
|
||||
child: _fakeComposer(l10n),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -352,7 +348,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
return const SizedBox(height: 8);
|
||||
}
|
||||
|
||||
Widget _fakeComposer(AppLocalizations l10n, {required bool signedIn}) {
|
||||
Widget _fakeComposer(AppLocalizations l10n) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: _openComposer,
|
||||
@@ -372,7 +368,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Text(
|
||||
signedIn ? l10n.writeComment : l10n.commentLoginHint,
|
||||
l10n.writeComment,
|
||||
key: const Key('comment-input-placeholder-label'),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
||||
@@ -49,7 +49,8 @@ class PlayerBlockedNotice extends StatelessWidget {
|
||||
|
||||
bool get _needsUnlock =>
|
||||
accessType == PlayTicket.coinRequired ||
|
||||
accessType == PlayTicket.vipRequired;
|
||||
accessType == PlayTicket.vipRequired ||
|
||||
(accessType == null && episode.locked);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
Reference in new issue
Block a user