manager-app/lib/Screens/Resources/get_element_for_resource.dart
Thomas Fransolet c8947880ba Ecran VR, section Scene3D et medias immersifs
Canal VR
- L'entree VR affiche VrScreen au lieu de « non configuree » : sous-onglet
  Configuration (AppConfigurationLinkScreen reutilise) et sous-onglet Casques
  (pincode d'appairage, etat, batterie, version, dernier vu).
- Le dialogue de plan SuperAdmin porte aussi les canaux et les add-ons
  (assistant, contenu immersif). Activer un canal cree son
  ApplicationInstance s'il n'existe pas ; le desactiver ne supprime rien.

Medias immersifs
- Types Image360, Video360, Model3D : icones, libelles, extensions, apercu
  dans la Mediatheque et facettes.
- Au depot, la pastille de type bascule Image <-> Image 360 et
  Video <-> Video 360 : rien dans un .jpg ne dit qu'il est
  equirectangulaire. Un .glb est reconnu seul.
- ImageCompressor ne touche plus aux 360 ni aux GLB, sur les deux chemins
  d'upload : ramenee a 2560 px, une equirectangulaire devient illisible dans
  un casque.

Scene3D
- Nouveau type de section (famille des lieux) et son ecran de configuration :
  modele, mode objet/decor, points d'interet.
- Fond immersif d'une configuration, visible si l'instance a l'add-on : le
  type est deduit de la ressource choisie, avec une image de repli.

Client API edite a la main en miroir du backend : SectionScene3DApi,
Scene3DDTO, ImmersiveBackgroundDTO, Position3D, AppType sur les devices,
ResourceType etendu. Libelles FR/EN/NL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-13 16:54:13 +02:00

135 lines
4.9 KiB
Dart

import 'package:manager_app/Components/audio_player.dart';
import 'package:manager_app/l10n/app_localizations.dart';
import 'package:manager_app/Components/pdf_web_viewer.dart';
import 'package:manager_app/Components/video_viewer.dart';
import 'package:manager_app/Components/video_viewer_youtube.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_api_new/api.dart';
import 'package:flutter/material.dart';
getElementForResource(BuildContext context, dynamic resourceDTO, AppContext appContext) {
switch(resourceDTO.type) {
// Une 360 est une image equirectangulaire : l'apercu plat est deforme, mais
// il montre la bonne ressource. Sans ce cas, le panneau de la mediatheque
// restait vide sans rien dire.
case ResourceType.Image360:
case ResourceType.Image:
return Image.network(
resourceDTO.url,
fit:BoxFit.fill,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Center(
child: CircularProgressIndicator(
color: kPrimaryColor,
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
);
case ResourceType.ImageUrl:
return Image.network(
resourceDTO.url,
fit:BoxFit.fill,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Center(
child: CircularProgressIndicator(
color: kPrimaryColor,
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
);
case ResourceType.Audio:
return AudioPlayerFloatingContainer(audioBytes: null, resourceURl: resourceDTO.url, isAuto: true);
/*return FutureBuilder(
future: getAudio(resourceDTO.url, appContext),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
Size size = MediaQuery.of(context).size;
if (snapshot.connectionState == ConnectionState.done) {
var audioBytes;
if(snapshot.data != null) {
print("snapshot.data");
print(snapshot.data);
audioBytes = snapshot.data;
//this.player.playBytes(audiobytes);
}
return AudioPlayerFloatingContainer(audioBytes: audioBytes, resourceURl: resourceDTO.url, isAuto: true);
} else if (snapshot.connectionState == ConnectionState.none) {
return Text(AppLocalizations.of(context)!.noData);
} else {
return Center(
child: Container(
//height: size.height * 0.2,
width: size.width * 0.2,
child: LoadingCommon()
)
);
}
}
);*/
//return Text("Fichier audio - aucune visualisation possible");
case ResourceType.Video360:
case ResourceType.Video:
if(resourceDTO.url == null) {
return Center(child: Text(AppLocalizations.of(context)!.errorLoadingVideo));
} else {
return VideoViewer(videoUrl: resourceDTO.url!);
}
case ResourceType.VideoUrl:
if(resourceDTO.url == null) {
return Center(child: Text(AppLocalizations.of(context)!.errorLoadingVideo));
} else {
return VideoViewerYoutube(videoUrl: resourceDTO.url!);
}
case ResourceType.Pdf:
if (resourceDTO.url == null) {
return Center(child: Text(AppLocalizations.of(context)!.errorLoadingPdf));
}
return PdfWebViewer(url: resourceDTO.url!);
case ResourceType.Json:
return Text(AppLocalizations.of(context)!.jsonNoPreview);
case ResourceType.JsonUrl:
return SelectableText(resourceDTO.url!);
case ResourceType.Model3D:
return Center(
child: Text(AppLocalizations.of(context)!.mediaModel3DNoPreview,
textAlign: TextAlign.center));
}
}
/*Future<Uint8List?> getAudio(String resourceId, AppContext appContext) async {
try {
ManagerAppContext managerAppContext = appContext.getContext() as ManagerAppContext;
var url = managerAppContext.host! + "/api/Resource/" + resourceId; // TO TEST TODO UPDATE ROUTE
var test2 = await http.readBytes(Uri.parse(url));
final base64Str = base64.encode(test2);
Uint8List base64String = base64Decode(base64Str); // LOAD DATA
return base64String;
} catch (e) {
print(e);
print("IN CATCH");
return null;
}
}*/