madari-oss/lib/features/widgetter/plugin_layout.dart
omkar a38b3d1235 fix: fix explore page
feat: added placeholder for empty page
fix: small toggle theme issue
feat: replaced by Cinemeta with Madari Catalog
2025-02-01 10:36:49 +05:30

224 lines
7.2 KiB
Dart

import 'package:cached_query/cached_query.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:logging/logging.dart';
import 'package:madari_client/features/settings/service/selected_profile.dart';
import 'package:madari_client/features/streamio_addons/extension/query_extension.dart';
import 'package:madari_client/features/widgetter/plugin_base.dart';
import 'package:madari_client/features/widgetter/plugins/stremio/widgets/catalog_featured_shimmer.dart';
import 'package:madari_client/features/widgetter/service/home_layout_service.dart';
import 'package:madari_client/features/widgetter/state/widget_state_provider.dart';
import 'package:madari_client/features/widgetter/types/home_layout_model.dart';
import 'package:provider/provider.dart';
import '../home/pages/home_page.dart';
import '../pocketbase/service/pocketbase.service.dart';
class LayoutManager extends StatefulWidget {
final bool hasSearch;
const LayoutManager({
super.key,
this.hasSearch = false,
});
@override
State<LayoutManager> createState() => LayoutManagerState();
}
class LayoutManagerState extends State<LayoutManager> {
final _logger = Logger('LayoutManager');
final ScrollController _scrollController = ScrollController();
List<HomeLayoutModel> _layouts = [];
List<HomeLayoutModel> _filteredLayouts = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadLayouts();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
Future<void> refresh() async {
final result = await _loadLayouts(
refresh: true,
);
HomeLayoutService.instance.refreshWidgets.add(true);
return result;
}
Future<void> _loadLayouts({
bool refresh = false,
}) async {
try {
_logger.info('Loading layouts');
final query = Query(
key:
"home_layout_${SelectedProfileService.instance.selectedProfileId}${widget.hasSearch}",
config: QueryConfig(
ignoreCacheDuration: refresh,
cacheDuration: const Duration(hours: 8),
refetchDuration: const Duration(minutes: 5),
),
queryFn: () async {
return await AppPocketBaseService.instance.pb
.collection('home_layout')
.getFullList(
sort: 'order',
filter:
"profiles = '${SelectedProfileService.instance.selectedProfileId}'",
);
},
);
if (refresh) {
await query.refetch();
}
final records = await query.queryFn();
setState(() {
_layouts = records
.map((record) => HomeLayoutModel.fromJson(record.toJson()))
.toList();
_filteredLayouts = _layouts;
_isLoading = false;
});
} catch (e) {
_logger.severe('Error loading layouts', e);
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
final search = context.watch<StateProvider>().search;
return RefreshIndicator(
onRefresh: () {
return refresh();
},
child: ListenableBuilder(
listenable: PluginRegistry.instance,
builder: (context, _) {
if (_isLoading) {
return const CatalogFeaturedShimmer();
}
if (_layouts.isEmpty) {
return Center(
child: Container(
padding: const EdgeInsets.all(24.0),
constraints: const BoxConstraints(maxWidth: 400),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.design_services,
size: 64,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 24),
Text(
"Configure Home Layout",
style:
Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text(
"You need to define your home before start",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color:
Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 32),
FilledButton.icon(
onPressed: () {
context.push("/layout");
},
icon: const Icon(Icons.edit_outlined),
label: const Text(
"Configure Layout",
style: TextStyle(
fontSize: 15,
),
),
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 16,
),
),
),
],
),
),
);
}
return Column(
children: [
if (widget.hasSearch)
const SearchBox(
hintText: 'Search...',
),
if (!widget.hasSearch || search.trim() != "")
Expanded(
child: CustomScrollView(
controller: _scrollController,
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final layout = _filteredLayouts[index];
if (widget.hasSearch) {
if (!(layout.pluginId == "stremio_catalog" &&
layout.type == "catalog_grid")) {
return const SizedBox.shrink();
}
}
return PluginWidget(
key: ValueKey(
'${layout.id}_${layout.pluginId}_${layout.type}_${search.trim()}',
),
layout: layout,
pluginContext: PluginContext(
index: index,
hasSearch: widget.hasSearch,
),
);
},
childCount: _filteredLayouts.length,
),
),
],
),
),
],
);
},
),
);
}
}