273 lines
8.8 KiB
Dart
273 lines
8.8 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|
import 'package:matrix/matrix.dart';
|
|
|
|
import 'package:fluffychat/config/app_config.dart';
|
|
import 'package:fluffychat/config/themes.dart';
|
|
import 'package:fluffychat/pangea/activity_planner/activity_plan_generation_repo.dart';
|
|
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
|
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
|
import 'package:fluffychat/pangea/activity_planner/activity_plan_response.dart';
|
|
import 'package:fluffychat/pangea/activity_planner/activity_planner_page.dart';
|
|
import 'package:fluffychat/pangea/activity_planner/bookmarked_activities_repo.dart';
|
|
import 'package:fluffychat/pangea/activity_planner/list_request_schema.dart';
|
|
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
|
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
|
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
|
import 'package:fluffychat/utils/file_selector.dart';
|
|
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
|
import 'activity_plan_card.dart';
|
|
|
|
class ActivityListView extends StatefulWidget {
|
|
final Room? room;
|
|
|
|
/// if null, show saved activities
|
|
final ActivityPlanRequest? activityPlanRequest;
|
|
|
|
final ActivityPlannerPageState controller;
|
|
|
|
const ActivityListView({
|
|
super.key,
|
|
required this.room,
|
|
required this.activityPlanRequest,
|
|
required this.controller,
|
|
});
|
|
|
|
@override
|
|
ActivityListViewState createState() => ActivityListViewState();
|
|
}
|
|
|
|
class ActivityListViewState extends State<ActivityListView> {
|
|
List<ActivityPlanModel>? _activities;
|
|
List<ActivityPlanModel> get _bookmarkedActivities =>
|
|
BookmarkedActivitiesRepo.get();
|
|
|
|
bool _isLoading = true;
|
|
Object? _error;
|
|
|
|
Uint8List? _avatar;
|
|
String? _avatarURL;
|
|
String? _filename;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadActivities();
|
|
_setModeImageURL();
|
|
}
|
|
|
|
Future<void> _loadActivities() async {
|
|
setState(() {
|
|
_isLoading = true;
|
|
_error = null;
|
|
});
|
|
|
|
try {
|
|
if (widget.activityPlanRequest != null) {
|
|
final resp = await ActivityPlanGenerationRepo.get(
|
|
widget.activityPlanRequest!,
|
|
);
|
|
_activities = resp.activityPlans;
|
|
}
|
|
} catch (e, s) {
|
|
_error = e;
|
|
ErrorHandler.logError(
|
|
e: e,
|
|
s: s,
|
|
data: {
|
|
'room': widget.room,
|
|
'activityPlanRequest': widget.activityPlanRequest,
|
|
},
|
|
);
|
|
} finally {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> _onEdit(int index, ActivityPlanModel updatedActivity) async {
|
|
// in this case we're editing an activity plan that was generated recently
|
|
// via the repo and should be updated in the cached response
|
|
if (widget.activityPlanRequest != null && _activities != null) {
|
|
final activities = _activities;
|
|
activities?[index] = updatedActivity;
|
|
ActivityPlanGenerationRepo.set(
|
|
widget.activityPlanRequest!,
|
|
ActivityPlanResponse(activityPlans: _activities!),
|
|
);
|
|
}
|
|
|
|
setState(() {});
|
|
}
|
|
|
|
Future<void> _onLaunch(int index) => showFutureLoadingDialog(
|
|
context: context,
|
|
future: () async {
|
|
final activity = _activities![index];
|
|
|
|
await widget.room?.sendActivityPlan(
|
|
activity,
|
|
avatar: _avatar,
|
|
avatarURL: _avatarURL,
|
|
filename: _filename,
|
|
);
|
|
|
|
Navigator.of(context).pop();
|
|
},
|
|
);
|
|
|
|
Future<ActivitySettingResponseSchema?> get _selectedMode async {
|
|
final modes = await widget.controller.modeItems;
|
|
return modes.firstWhereOrNull(
|
|
(element) =>
|
|
element.name.toLowerCase() ==
|
|
widget.activityPlanRequest?.mode.toLowerCase(),
|
|
);
|
|
}
|
|
|
|
Future<void> _setModeImageURL() async {
|
|
final mode = await _selectedMode;
|
|
if (mode == null) return;
|
|
|
|
final modeName =
|
|
mode.defaultName.toLowerCase().replaceAll(RegExp(r'\s+'), '');
|
|
final filename =
|
|
"${ActivitySuggestionsConstants.modeImageFileStart}$modeName.jpg";
|
|
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_avatarURL = "${AppConfig.assetsBaseURL}/$filename";
|
|
_filename = filename;
|
|
});
|
|
}
|
|
|
|
void selectPhoto() async {
|
|
final resp = await selectFiles(
|
|
context,
|
|
type: FileSelectorType.images,
|
|
allowMultiple: false,
|
|
);
|
|
|
|
final photo = resp.singleOrNull;
|
|
if (photo == null) return;
|
|
final bytes = await photo.readAsBytes();
|
|
|
|
setState(() {
|
|
_avatar = bytes;
|
|
_filename = photo.name;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = L10n.of(context);
|
|
if (_isLoading) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
} else if (_error != null) {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(l10n.oopsSomethingWentWrong),
|
|
const SizedBox(height: 16),
|
|
ElevatedButton(
|
|
onPressed: _loadActivities,
|
|
child: Text(l10n.tryAgain),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
} else if (widget.activityPlanRequest != null &&
|
|
(_activities == null || _activities!.isEmpty)) {
|
|
return Center(child: Text(l10n.noDataFound));
|
|
} else if (widget.activityPlanRequest == null &&
|
|
(_bookmarkedActivities.isEmpty)) {
|
|
return Center(
|
|
child: Container(
|
|
constraints: const BoxConstraints(maxWidth: 200),
|
|
child: Text(
|
|
l10n.noBookmarkedActivities,
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.all(16),
|
|
itemCount: widget.activityPlanRequest == null
|
|
? _bookmarkedActivities.length
|
|
: _activities!.length + 1,
|
|
itemBuilder: (context, index) {
|
|
if (index == 0) {
|
|
return Center(
|
|
child: Stack(
|
|
alignment: Alignment.bottomCenter,
|
|
children: [
|
|
Column(
|
|
children: [
|
|
AnimatedSize(
|
|
duration: FluffyThemes.animationDuration,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12.0),
|
|
),
|
|
width: 400.0,
|
|
clipBehavior: Clip.hardEdge,
|
|
child: _avatarURL != null || _avatar != null
|
|
? ClipRRect(
|
|
child: _avatar == null
|
|
? CachedNetworkImage(
|
|
fit: BoxFit.cover,
|
|
imageUrl: _avatarURL!,
|
|
placeholder: (context, url) {
|
|
return const Center(
|
|
child:
|
|
CircularProgressIndicator(),
|
|
);
|
|
},
|
|
errorWidget: (context, url, error) =>
|
|
const SizedBox(),
|
|
)
|
|
: Image.memory(
|
|
_avatar!,
|
|
fit: BoxFit.cover,
|
|
),
|
|
)
|
|
: const Padding(
|
|
padding: EdgeInsets.all(16.0),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16.0),
|
|
],
|
|
),
|
|
InkWell(
|
|
borderRadius: BorderRadius.circular(90),
|
|
onTap: _isLoading ? null : selectPhoto,
|
|
child: const CircleAvatar(
|
|
radius: 32.0,
|
|
child: Icon(Icons.add_a_photo_outlined),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
index--;
|
|
|
|
return ActivityPlanCard(
|
|
activity: widget.activityPlanRequest == null
|
|
? _bookmarkedActivities[index]
|
|
: _activities![index],
|
|
onLaunch: () => _onLaunch(index),
|
|
onEdit: (updatedActivity) => _onEdit(index, updatedActivity),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|