mirror of
https://gitlab.com/foxixus/neomovies_mobile.git
synced 2025-10-28 01:18:50 +05:00
fix: resolve gray screens and add automatic versioning
1. Fix Downloads screen gray screen issue: - Add DownloadsProvider to main.dart providers list - Remove @RoutePage() decorator from DownloadsScreen - Downloads screen now displays torrent list correctly 2. Fix movie detail screen gray screen issue: - Improve Movie.fromJson() with better error handling - Safe parsing of genres field (handles both Map and String formats) - Add fallback 'Untitled' for movies without title - Add detailed logging in MovieDetailProvider - Better error messages with stack traces 3. Add automatic version update from CI/CD tags: - GitLab CI: Update pubspec.yaml version from CI_COMMIT_TAG before build - GitHub Actions: Update pubspec.yaml version from GITHUB_REF before build - Version format: tag v0.0.18 becomes version 0.0.18+18 - Applies to all build jobs (arm64, arm32, x64) How versioning works: - When you create tag v0.0.18, CI automatically updates pubspec.yaml - Build uses version 0.0.18+18 (version+buildNumber) - APK shows correct version in About screen and Google Play - No manual pubspec.yaml updates needed Example: - Create tag: git tag v0.0.18 && git push origin v0.0.18 - CI reads tag, extracts '0.0.18' - Updates: version: 0.0.18+18 in pubspec.yaml - Builds APK with this version - Release created with proper version number Changes: - lib/main.dart: Add DownloadsProvider - lib/presentation/screens/downloads/downloads_screen.dart: Remove @RoutePage - lib/data/models/movie.dart: Safe JSON parsing with error handling - lib/presentation/providers/movie_detail_provider.dart: Add detailed logging - .gitlab-ci.yml: Add version update script in all build jobs - .github/workflows/release.yml: Add version update step in all build jobs Result: ✅ Downloads screen displays properly ✅ Movie details screen loads correctly ✅ Automatic versioning from tags (0.0.18, 0.0.19, etc.) ✅ No more gray screens!
This commit is contained in:
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -66,6 +66,14 @@ jobs:
|
|||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
|
- name: Update version from tag
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
VERSION_NAME=${GITHUB_REF#refs/tags/v}
|
||||||
|
BUILD_NUMBER=$(echo $VERSION_NAME | sed 's/[^0-9]//g')
|
||||||
|
echo "Updating version to $VERSION_NAME+$BUILD_NUMBER"
|
||||||
|
sed -i "s/^version: .*/version: $VERSION_NAME+$BUILD_NUMBER/" pubspec.yaml
|
||||||
|
|
||||||
- name: Get dependencies
|
- name: Get dependencies
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,15 @@ build:apk:arm64:
|
|||||||
build:apk:arm:
|
build:apk:arm:
|
||||||
stage: build
|
stage: build
|
||||||
image: ghcr.io/cirruslabs/flutter:${FLUTTER_VERSION}
|
image: ghcr.io/cirruslabs/flutter:${FLUTTER_VERSION}
|
||||||
|
before_script:
|
||||||
|
# Update version from tag if present
|
||||||
|
- |
|
||||||
|
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||||
|
VERSION_NAME="${CI_COMMIT_TAG#v}"
|
||||||
|
BUILD_NUMBER=$(echo $CI_COMMIT_TAG | sed 's/[^0-9]//g')
|
||||||
|
echo "Updating version to $VERSION_NAME+$BUILD_NUMBER"
|
||||||
|
sed -i "s/^version: .*/version: $VERSION_NAME+$BUILD_NUMBER/" pubspec.yaml
|
||||||
|
fi
|
||||||
script:
|
script:
|
||||||
- flutter pub get
|
- flutter pub get
|
||||||
- mkdir -p debug-symbols
|
- mkdir -p debug-symbols
|
||||||
|
|||||||
@@ -67,30 +67,47 @@ class Movie extends HiveObject {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Movie.fromJson(Map<String, dynamic> json) {
|
factory Movie.fromJson(Map<String, dynamic> json) {
|
||||||
return Movie(
|
try {
|
||||||
id: (json['id'] as num).toString(), // Ensure id is a string
|
// Parse genres safely
|
||||||
title: (json['title'] ?? json['name'] ?? '') as String,
|
List<String> genresList = [];
|
||||||
posterPath: json['poster_path'] as String?,
|
if (json['genres'] != null) {
|
||||||
backdropPath: json['backdrop_path'] as String?,
|
if (json['genres'] is List) {
|
||||||
overview: json['overview'] as String?,
|
genresList = (json['genres'] as List)
|
||||||
releaseDate: json['release_date'] != null && json['release_date'].isNotEmpty
|
.map((g) => g is Map ? (g['name'] as String?) ?? '' : g.toString())
|
||||||
? DateTime.tryParse(json['release_date'] as String)
|
.where((name) => name.isNotEmpty)
|
||||||
: json['first_air_date'] != null && json['first_air_date'].isNotEmpty
|
.toList();
|
||||||
? DateTime.tryParse(json['first_air_date'] as String)
|
}
|
||||||
: null,
|
}
|
||||||
genres: List<String>.from(json['genres']?.map((g) => g['name']) ?? []),
|
|
||||||
voteAverage: (json['vote_average'] as num?)?.toDouble() ?? 0.0,
|
return Movie(
|
||||||
popularity: (json['popularity'] as num?)?.toDouble() ?? 0.0,
|
id: (json['id'] as num).toString(), // Ensure id is a string
|
||||||
runtime: json['runtime'] is num
|
title: (json['title'] ?? json['name'] ?? 'Untitled') as String,
|
||||||
? (json['runtime'] as num).toInt()
|
posterPath: json['poster_path'] as String?,
|
||||||
: (json['episode_run_time'] is List && (json['episode_run_time'] as List).isNotEmpty)
|
backdropPath: json['backdrop_path'] as String?,
|
||||||
? ((json['episode_run_time'] as List).first as num).toInt()
|
overview: json['overview'] as String?,
|
||||||
: null,
|
releaseDate: json['release_date'] != null && json['release_date'].toString().isNotEmpty
|
||||||
seasonsCount: json['number_of_seasons'] as int?,
|
? DateTime.tryParse(json['release_date'].toString())
|
||||||
episodesCount: json['number_of_episodes'] as int?,
|
: json['first_air_date'] != null && json['first_air_date'].toString().isNotEmpty
|
||||||
tagline: json['tagline'] as String?,
|
? DateTime.tryParse(json['first_air_date'].toString())
|
||||||
mediaType: (json['media_type'] ?? (json['title'] != null ? 'movie' : 'tv')) as String,
|
: null,
|
||||||
);
|
genres: genresList,
|
||||||
|
voteAverage: (json['vote_average'] as num?)?.toDouble() ?? 0.0,
|
||||||
|
popularity: (json['popularity'] as num?)?.toDouble() ?? 0.0,
|
||||||
|
runtime: json['runtime'] is num
|
||||||
|
? (json['runtime'] as num).toInt()
|
||||||
|
: (json['episode_run_time'] is List && (json['episode_run_time'] as List).isNotEmpty)
|
||||||
|
? ((json['episode_run_time'] as List).first as num).toInt()
|
||||||
|
: null,
|
||||||
|
seasonsCount: json['number_of_seasons'] as int?,
|
||||||
|
episodesCount: json['number_of_episodes'] as int?,
|
||||||
|
tagline: json['tagline'] as String?,
|
||||||
|
mediaType: (json['media_type'] ?? (json['title'] != null ? 'movie' : 'tv')) as String,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print('Error parsing Movie from JSON: $e');
|
||||||
|
print('JSON data: $json');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$MovieToJson(this);
|
Map<String, dynamic> toJson() => _$MovieToJson(this);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import 'package:neomovies_mobile/presentation/providers/favorites_provider.dart'
|
|||||||
import 'package:neomovies_mobile/presentation/providers/home_provider.dart';
|
import 'package:neomovies_mobile/presentation/providers/home_provider.dart';
|
||||||
import 'package:neomovies_mobile/presentation/providers/movie_detail_provider.dart';
|
import 'package:neomovies_mobile/presentation/providers/movie_detail_provider.dart';
|
||||||
import 'package:neomovies_mobile/presentation/providers/reactions_provider.dart';
|
import 'package:neomovies_mobile/presentation/providers/reactions_provider.dart';
|
||||||
|
import 'package:neomovies_mobile/presentation/providers/downloads_provider.dart';
|
||||||
import 'package:neomovies_mobile/presentation/screens/main_screen.dart';
|
import 'package:neomovies_mobile/presentation/screens/main_screen.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,15 @@ class MovieDetailProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
print('Loading media: ID=$mediaId, type=$mediaType');
|
||||||
|
|
||||||
// Load movie/TV details
|
// Load movie/TV details
|
||||||
if (mediaType == 'movie') {
|
if (mediaType == 'movie') {
|
||||||
_movie = await _movieRepository.getMovieById(mediaId.toString());
|
_movie = await _movieRepository.getMovieById(mediaId.toString());
|
||||||
|
print('Movie loaded successfully: ${_movie?.title}');
|
||||||
} else {
|
} else {
|
||||||
_movie = await _movieRepository.getTvById(mediaId.toString());
|
_movie = await _movieRepository.getTvById(mediaId.toString());
|
||||||
|
print('TV show loaded successfully: ${_movie?.title}');
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
@@ -46,15 +50,18 @@ class MovieDetailProvider with ChangeNotifier {
|
|||||||
// Try to load IMDb ID (non-blocking)
|
// Try to load IMDb ID (non-blocking)
|
||||||
if (_movie != null) {
|
if (_movie != null) {
|
||||||
try {
|
try {
|
||||||
|
print('Loading IMDb ID for $mediaType $mediaId');
|
||||||
_imdbId = await _apiClient.getImdbId(mediaId.toString(), mediaType);
|
_imdbId = await _apiClient.getImdbId(mediaId.toString(), mediaType);
|
||||||
|
print('IMDb ID loaded: $_imdbId');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// IMDb ID loading failed, but don't fail the whole screen
|
// IMDb ID loading failed, but don't fail the whole screen
|
||||||
print('Failed to load IMDb ID: $e');
|
print('Failed to load IMDb ID: $e');
|
||||||
_imdbId = null;
|
_imdbId = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
print('Error loading media: $e');
|
print('Error loading media: $e');
|
||||||
|
print('Stack trace: $stackTrace');
|
||||||
_error = e.toString();
|
_error = e.toString();
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../../providers/downloads_provider.dart';
|
import '../../providers/downloads_provider.dart';
|
||||||
import '../../../data/models/torrent_info.dart';
|
import '../../../data/models/torrent_info.dart';
|
||||||
import 'torrent_detail_screen.dart';
|
import 'torrent_detail_screen.dart';
|
||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
|
|
||||||
@RoutePage()
|
|
||||||
class DownloadsScreen extends StatefulWidget {
|
class DownloadsScreen extends StatefulWidget {
|
||||||
const DownloadsScreen({super.key});
|
const DownloadsScreen({super.key});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user