diff --git a/README.md b/README.md index 34260fa..4f66745 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@
-
+
diff --git a/lib/app/app_version.dart b/lib/app/app_version.dart
index 2bc1374..18a0d70 100644
--- a/lib/app/app_version.dart
+++ b/lib/app/app_version.dart
@@ -1,3 +1,3 @@
-const appDisplayVersion = '3.1.3';
-const appBuildNumber = 64;
+const appDisplayVersion = '3.1.4';
+const appBuildNumber = 65;
const appReleaseChannel = 'Stable';
diff --git a/lib/app/changelog.dart b/lib/app/changelog.dart
index 47d77aa..3a7b8b4 100644
--- a/lib/app/changelog.dart
+++ b/lib/app/changelog.dart
@@ -1,4 +1,13 @@
const lunarisChangelog = [
+ LunarisChange(
+ version: '3.1.4',
+ title: 'Оптимизация видеоплеера — адаптивная высота и идеальное прилегание полосы воспроизведения',
+ bullets: [
+ 'Устранение зазора под видео: контейнер плеера на мобильных устройствах и ПК теперь динамически подстраивается под пропорции видео (aspect ratio) вместо жестко фиксированной высоты 62% экрана, устраняя огромные пустые черные полосы сверху и снизу.',
+ 'Точное позиционирование полосы воспроизведения: исправлен отступ снизу и ложное наложение системных insets на неполноэкранный плеер в карточке поста — ползунок воспроизведения, таймкоды и кнопка фуллскрина теперь аккуратно прилегают к нижнему краю видео без зависания в воздухе.',
+ 'Компактные элементы управления: выровнены буферный трек и слайдер прокрутки, оптимизирован градиент затенения для максимальной видимости и удобства управления.',
+ ],
+ ),
LunarisChange(
version: '3.1.3',
title: 'iPhone Liquid Glass — Источники, Проверка сети и Навигация назад по вкладкам',
diff --git a/lib/features/post/presentation/post_details_screen.dart b/lib/features/post/presentation/post_details_screen.dart
index 34fc22d..ff0565b 100644
--- a/lib/features/post/presentation/post_details_screen.dart
+++ b/lib/features/post/presentation/post_details_screen.dart
@@ -219,34 +219,71 @@ class PostDetailsScreen extends ConsumerWidget {
),
Expanded(
child: Center(
- child: SizedBox(
- width: double.infinity,
- height: 760,
- child: PostMediaViewer(
- key: ValueKey(post.cacheKey),
- post: post,
- localFilePath: localMedia?.savedPath,
- qualityMode: qualityMode,
- mediaHeaders: ref
- .watch(postMediaHeadersProvider(post))
- .value ??
- const {},
- initialPosition: Duration(
- milliseconds: settings.videoPlaybackPositions[
- post.cacheKey] ??
- 0,
- ),
- initialLoop: settings.videoPlayerLoop,
- initialMuted: settings.videoPlayerMuted,
- initialCoverVideo: settings.videoPlayerCover,
- initialHalfVolume:
- settings.videoPlayerHalfVolume,
- onPlaybackSnapshot: (snapshot) =>
- _saveVideoSnapshot(ref, post, snapshot),
- onPlaybackPreferencesChanged: (snapshot) =>
- _saveVideoPreferences(ref, snapshot),
- ),
- ),
+ child: MediaUrlSelector.isVideo(post)
+ ? ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxHeight: 760,
+ ),
+ child: AspectRatio(
+ aspectRatio:
+ (post.width > 0 && post.height > 0)
+ ? (post.width / post.height)
+ .clamp(0.45, 2.4)
+ : (16 / 9),
+ child: PostMediaViewer(
+ key: ValueKey(post.cacheKey),
+ post: post,
+ localFilePath: localMedia?.savedPath,
+ qualityMode: qualityMode,
+ mediaHeaders: ref
+ .watch(postMediaHeadersProvider(post))
+ .value ??
+ const {},
+ initialPosition: Duration(
+ milliseconds: settings.videoPlaybackPositions[
+ post.cacheKey] ??
+ 0,
+ ),
+ initialLoop: settings.videoPlayerLoop,
+ initialMuted: settings.videoPlayerMuted,
+ initialCoverVideo: settings.videoPlayerCover,
+ initialHalfVolume:
+ settings.videoPlayerHalfVolume,
+ onPlaybackSnapshot: (snapshot) =>
+ _saveVideoSnapshot(ref, post, snapshot),
+ onPlaybackPreferencesChanged: (snapshot) =>
+ _saveVideoPreferences(ref, snapshot),
+ ),
+ ),
+ )
+ : SizedBox(
+ width: double.infinity,
+ height: 760,
+ child: PostMediaViewer(
+ key: ValueKey(post.cacheKey),
+ post: post,
+ localFilePath: localMedia?.savedPath,
+ qualityMode: qualityMode,
+ mediaHeaders: ref
+ .watch(postMediaHeadersProvider(post))
+ .value ??
+ const {},
+ initialPosition: Duration(
+ milliseconds: settings.videoPlaybackPositions[
+ post.cacheKey] ??
+ 0,
+ ),
+ initialLoop: settings.videoPlayerLoop,
+ initialMuted: settings.videoPlayerMuted,
+ initialCoverVideo: settings.videoPlayerCover,
+ initialHalfVolume:
+ settings.videoPlayerHalfVolume,
+ onPlaybackSnapshot: (snapshot) =>
+ _saveVideoSnapshot(ref, post, snapshot),
+ onPlaybackPreferencesChanged: (snapshot) =>
+ _saveVideoPreferences(ref, snapshot),
+ ),
+ ),
),
),
IconButton.filledTonal(
@@ -409,33 +446,72 @@ class PostDetailsScreen extends ConsumerWidget {
? null
: () =>
_showMobileQuickActions(context, ref, post, favoriteKeys),
- child: SizedBox(
- width: double.infinity,
- height: MediaQuery.sizeOf(context).height * 0.62,
- child: PostMediaViewer(
- key: ValueKey(post.cacheKey),
- post: post,
- localFilePath: localMedia?.savedPath,
- qualityMode: qualityMode,
- mediaHeaders:
- ref.watch(postMediaHeadersProvider(post)).value ?? const {},
- initialPosition: Duration(
- milliseconds:
- settings.videoPlaybackPositions[post.cacheKey] ?? 0,
- ),
- initialLoop: settings.videoPlayerLoop,
- initialMuted: settings.videoPlayerMuted,
- initialCoverVideo: settings.videoPlayerCover,
- initialHalfVolume: settings.videoPlayerHalfVolume,
- onPlaybackSnapshot: (snapshot) =>
- _saveVideoSnapshot(ref, post, snapshot),
- onPlaybackPreferencesChanged: (snapshot) =>
- _saveVideoPreferences(ref, snapshot),
- onMediaGestureLockChanged: (locked) {
- onMediaGestureLockChanged?.call(locked);
- },
- ),
- ),
+ child: isVideo
+ ? Center(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ maxHeight: MediaQuery.sizeOf(context).height * 0.70,
+ ),
+ child: AspectRatio(
+ aspectRatio: (post.width > 0 && post.height > 0)
+ ? (post.width / post.height).clamp(0.45, 2.4)
+ : (16 / 9),
+ child: PostMediaViewer(
+ key: ValueKey(post.cacheKey),
+ post: post,
+ localFilePath: localMedia?.savedPath,
+ qualityMode: qualityMode,
+ mediaHeaders:
+ ref.watch(postMediaHeadersProvider(post)).value ??
+ const {},
+ initialPosition: Duration(
+ milliseconds:
+ settings.videoPlaybackPositions[post.cacheKey] ??
+ 0,
+ ),
+ initialLoop: settings.videoPlayerLoop,
+ initialMuted: settings.videoPlayerMuted,
+ initialCoverVideo: settings.videoPlayerCover,
+ initialHalfVolume: settings.videoPlayerHalfVolume,
+ onPlaybackSnapshot: (snapshot) =>
+ _saveVideoSnapshot(ref, post, snapshot),
+ onPlaybackPreferencesChanged: (snapshot) =>
+ _saveVideoPreferences(ref, snapshot),
+ onMediaGestureLockChanged: (locked) {
+ onMediaGestureLockChanged?.call(locked);
+ },
+ ),
+ ),
+ ),
+ )
+ : SizedBox(
+ width: double.infinity,
+ height: MediaQuery.sizeOf(context).height * 0.62,
+ child: PostMediaViewer(
+ key: ValueKey(post.cacheKey),
+ post: post,
+ localFilePath: localMedia?.savedPath,
+ qualityMode: qualityMode,
+ mediaHeaders:
+ ref.watch(postMediaHeadersProvider(post)).value ??
+ const {},
+ initialPosition: Duration(
+ milliseconds:
+ settings.videoPlaybackPositions[post.cacheKey] ?? 0,
+ ),
+ initialLoop: settings.videoPlayerLoop,
+ initialMuted: settings.videoPlayerMuted,
+ initialCoverVideo: settings.videoPlayerCover,
+ initialHalfVolume: settings.videoPlayerHalfVolume,
+ onPlaybackSnapshot: (snapshot) =>
+ _saveVideoSnapshot(ref, post, snapshot),
+ onPlaybackPreferencesChanged: (snapshot) =>
+ _saveVideoPreferences(ref, snapshot),
+ onMediaGestureLockChanged: (locked) {
+ onMediaGestureLockChanged?.call(locked);
+ },
+ ),
+ ),
),
const SizedBox(height: 12),
PostActionBar(
diff --git a/lib/features/post/presentation/widgets/post_media_viewer.dart b/lib/features/post/presentation/widgets/post_media_viewer.dart
index a194dd7..6c1ef12 100644
--- a/lib/features/post/presentation/widgets/post_media_viewer.dart
+++ b/lib/features/post/presentation/widgets/post_media_viewer.dart
@@ -1792,7 +1792,10 @@ class _VideoControls extends StatelessWidget {
),
),
child: SafeArea(
+ top: fullscreen,
bottom: false,
+ left: fullscreen,
+ right: fullscreen,
child: Row(
children: [
if (fullscreen) ...[
@@ -1910,7 +1913,12 @@ class _VideoControls extends StatelessWidget {
left: 0,
right: 0,
child: Container(
- padding: const EdgeInsets.fromLTRB(14, 12, 14, 10),
+ padding: EdgeInsets.fromLTRB(
+ 14,
+ 8,
+ 14,
+ fullscreen ? 10 : 4,
+ ),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
@@ -1920,6 +1928,9 @@ class _VideoControls extends StatelessWidget {
),
child: SafeArea(
top: false,
+ bottom: fullscreen,
+ left: fullscreen,
+ right: fullscreen,
child: StreamBuilder