modificacion de colores en el login
This commit is contained in:
@@ -232,7 +232,7 @@ class _LoginPageState extends State<LoginPage> with TickerProviderStateMixin {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF0F172A),
|
color: const Color.fromARGB(255, 25, 26, 28),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.1),
|
color: Colors.black.withOpacity(0.1),
|
||||||
@@ -278,13 +278,11 @@ class _LoginPageState extends State<LoginPage> with TickerProviderStateMixin {
|
|||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: [
|
colors: [
|
||||||
Color(0xFF4EC9F5), // Cyan principal
|
Color(0xFF42BCEE),
|
||||||
Color(0xFFFFB733), // Yellow
|
Color(0xFF5865B5),
|
||||||
Color(0xFF6B2F8A), // Purple
|
Color(0xFF653093)
|
||||||
Color(0xFFFF7A3D), // Orange
|
|
||||||
Color(0xFF4EC9F5), // Cyan de vuelta
|
|
||||||
],
|
],
|
||||||
stops: [0.0, 0.25, 0.5, 0.75, 1.0],
|
stops: [0.25, 0.5, 1.0],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ class _LoginFormState extends State<LoginForm> with TickerProviderStateMixin {
|
|||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: const BorderSide(
|
borderSide: const BorderSide(
|
||||||
color: Color(0xFF10B981),
|
color: Color(0xFF4EC9F5),
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -364,6 +364,7 @@ class _GestorVideosPageState extends State<GestorVideosPage> {
|
|||||||
: (video.fileUrl != null && video.fileUrl!.isNotEmpty)
|
: (video.fileUrl != null && video.fileUrl!.isNotEmpty)
|
||||||
? VideoThumbnailWidget(
|
? VideoThumbnailWidget(
|
||||||
videoUrl: video.fileUrl!,
|
videoUrl: video.fileUrl!,
|
||||||
|
height: 100,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
: _buildThumbnailPlaceholder(),
|
: _buildThumbnailPlaceholder(),
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ class _VideosLayoutState extends State<VideosLayout> {
|
|||||||
colors: [
|
colors: [
|
||||||
Color(0xFF42BCEE),
|
Color(0xFF42BCEE),
|
||||||
Color(0xFF5865B5),
|
Color(0xFF5865B5),
|
||||||
Color(0xFF653093),
|
Color(0xFF653093)
|
||||||
],
|
],
|
||||||
stops: const [0.0, 0.5, 1.0],
|
stops: const [0.0, 0.5, 1.0],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class _EditVideoDialogState extends State<EditVideoDialog> {
|
|||||||
late TextEditingController tagsController;
|
late TextEditingController tagsController;
|
||||||
Uint8List? newPosterBytes;
|
Uint8List? newPosterBytes;
|
||||||
String? newPosterFileName;
|
String? newPosterFileName;
|
||||||
|
bool _deletePoster = false;
|
||||||
VideoPlayerController? _videoPlayerController;
|
VideoPlayerController? _videoPlayerController;
|
||||||
ChewieController? _chewieController;
|
ChewieController? _chewieController;
|
||||||
bool _isVideoLoading = false;
|
bool _isVideoLoading = false;
|
||||||
@@ -162,8 +163,12 @@ class _EditVideoDialogState extends State<EditVideoDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar portada si se seleccionó una nueva
|
// Eliminar portada si se solicitó
|
||||||
if (newPosterBytes != null && newPosterFileName != null) {
|
if (_deletePoster) {
|
||||||
|
await widget.provider.deletePoster(widget.video.mediaFileId);
|
||||||
|
}
|
||||||
|
// Actualizar portada si se seleccionó una nueva (solo si no se eliminó)
|
||||||
|
else if (newPosterBytes != null && newPosterFileName != null) {
|
||||||
await widget.provider.updateVideoPoster(
|
await widget.provider.updateVideoPoster(
|
||||||
widget.video.mediaFileId,
|
widget.video.mediaFileId,
|
||||||
newPosterBytes!,
|
newPosterBytes!,
|
||||||
@@ -437,10 +442,100 @@ class _EditVideoDialogState extends State<EditVideoDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPosterSection() {
|
Widget _buildPosterSection() {
|
||||||
|
final hasPoster = widget.video.posterUrl != null && !_deletePoster;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (widget.video.posterUrl != null)
|
if (_deletePoster)
|
||||||
|
Container(
|
||||||
|
height: 140,
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppTheme.of(context).tertiaryBackground,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xFFFF2D2D).withOpacity(0.3),
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.delete_outline,
|
||||||
|
size: 48,
|
||||||
|
color: const Color(0xFFFF2D2D).withOpacity(0.7),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'Portada marcada para eliminar',
|
||||||
|
style: AppTheme.of(context).bodyText2.override(
|
||||||
|
fontFamily: 'Poppins',
|
||||||
|
color: const Color(0xFFFF2D2D),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else if (newPosterBytes != null)
|
||||||
|
// Mostrar preview de nueva portada seleccionada
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Image.memory(
|
||||||
|
newPosterBytes!,
|
||||||
|
height: 140,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
child: Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppTheme.of(context).success,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.2),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.fiber_new,
|
||||||
|
size: 14,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
'Nueva',
|
||||||
|
style: AppTheme.of(context).bodyText2.override(
|
||||||
|
fontFamily: 'Poppins',
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else if (hasPoster)
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
@@ -458,30 +553,89 @@ class _EditVideoDialogState extends State<EditVideoDialog> {
|
|||||||
color: AppTheme.of(context).tertiaryBackground,
|
color: AppTheme.of(context).tertiaryBackground,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Column(
|
||||||
Icons.image,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
size: 48,
|
children: [
|
||||||
color: AppTheme.of(context).tertiaryText,
|
Icon(
|
||||||
|
Icons.video_library,
|
||||||
|
size: 48,
|
||||||
|
color: AppTheme.of(context).tertiaryText,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'Sin portada',
|
||||||
|
style: AppTheme.of(context).bodyText2.override(
|
||||||
|
fontFamily: 'Poppins',
|
||||||
|
color: AppTheme.of(context).tertiaryText,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
ElevatedButton.icon(
|
Row(
|
||||||
onPressed: _selectPoster,
|
children: [
|
||||||
icon: const Icon(Icons.image, size: 18),
|
Expanded(
|
||||||
label: Text(
|
child: ElevatedButton.icon(
|
||||||
newPosterBytes != null ? 'Portada Seleccionada' : 'Cambiar Portada',
|
onPressed: _deletePoster ? null : _selectPoster,
|
||||||
),
|
icon: const Icon(Icons.image, size: 18),
|
||||||
style: ElevatedButton.styleFrom(
|
label: Text(
|
||||||
backgroundColor: newPosterBytes != null
|
newPosterBytes != null
|
||||||
? AppTheme.of(context).success
|
? 'Portada Seleccionada'
|
||||||
: AppTheme.of(context).primaryColor,
|
: 'Cambiar Portada',
|
||||||
foregroundColor: Colors.white,
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
style: ElevatedButton.styleFrom(
|
||||||
shape: RoundedRectangleBorder(
|
backgroundColor: newPosterBytes != null
|
||||||
borderRadius: BorderRadius.circular(10),
|
? AppTheme.of(context).success
|
||||||
|
: AppTheme.of(context).primaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
disabledBackgroundColor:
|
||||||
|
AppTheme.of(context).tertiaryText.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
elevation: 0,
|
if (hasPoster || _deletePoster) ...[
|
||||||
),
|
const Gap(8),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_deletePoster = !_deletePoster;
|
||||||
|
if (_deletePoster) {
|
||||||
|
// Si se marca para eliminar, limpiar nueva portada seleccionada
|
||||||
|
newPosterBytes = null;
|
||||||
|
newPosterFileName = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
_deletePoster ? Icons.undo : Icons.delete_outline,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
_deletePoster ? 'Deshacer' : 'Eliminar',
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: _deletePoster
|
||||||
|
? AppTheme.of(context).warning
|
||||||
|
: const Color(0xFFFF2D2D),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
if (newPosterBytes != null) ...[
|
if (newPosterBytes != null) ...[
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
|||||||
@@ -580,6 +580,57 @@ class VideosProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete video poster only (not the video itself)
|
||||||
|
Future<bool> deletePoster(int mediaFileId) async {
|
||||||
|
try {
|
||||||
|
isLoading = true;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
// Get current metadata
|
||||||
|
final response = await supabaseML
|
||||||
|
.from('media_files')
|
||||||
|
.select('metadata_json')
|
||||||
|
.eq('media_file_id', mediaFileId)
|
||||||
|
.eq('organization_fk', organizationId)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
final metadata = response['metadata_json'] as Map<String, dynamic>? ?? {};
|
||||||
|
final posterUrl = metadata['poster_url'] as String?;
|
||||||
|
|
||||||
|
// Delete poster from storage if exists
|
||||||
|
if (posterUrl != null && posterUrl.isNotEmpty) {
|
||||||
|
try {
|
||||||
|
final uri = Uri.parse(posterUrl);
|
||||||
|
final pathSegments = uri.pathSegments;
|
||||||
|
final bucketIndex = pathSegments.indexOf('energymedia');
|
||||||
|
if (bucketIndex != -1 && bucketIndex < pathSegments.length - 1) {
|
||||||
|
final posterPath = pathSegments.sublist(bucketIndex + 1).join('/');
|
||||||
|
await supabaseML.storage.from('energymedia').remove([posterPath]);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error eliminando poster del storage: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove poster references from metadata
|
||||||
|
metadata.remove('poster_url');
|
||||||
|
metadata.remove('poster_file_name');
|
||||||
|
|
||||||
|
// Update metadata
|
||||||
|
await updateVideoMetadata(mediaFileId, metadata);
|
||||||
|
|
||||||
|
isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
errorMessage = 'Error eliminando portada: $e';
|
||||||
|
isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
print('Error en deletePoster: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========== DELETE METHODS ==========
|
// ========== DELETE METHODS ==========
|
||||||
|
|
||||||
/// Delete video and its storage files
|
/// Delete video and its storage files
|
||||||
|
|||||||
Reference in New Issue
Block a user