import 'dart:async'; import 'package:feet/pages/login.dart'; import 'package:feet/widgets/centered_page_hint.dart'; import 'package:feet/widgets/homepage_post.dart'; import 'package:feet/widgets/login_prompt.dart'; import 'package:flutter/material.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'api/api.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { final api = FeverAPI(); SharedPreferences? prefs; MyApp({super.key}); Future _loadPrefs() async { var prefs = await SharedPreferences.getInstance(); this.prefs = prefs; var apiKey = prefs.getString('apiKey'), apiUrl = prefs.getString('apiUrl'); if (apiUrl != null && apiKey != null) { api.apiUrl = apiUrl; api.apiKey = apiKey; return true; } return false; } @override Widget build(BuildContext context) { return DynamicColorBuilder( builder: ((lightDynamic, darkDynamic) => MaterialApp( title: 'Feet', theme: ThemeData( brightness: Brightness.light, useMaterial3: true, colorScheme: lightDynamic, ), darkTheme: ThemeData( brightness: Brightness.dark, useMaterial3: true, colorScheme: darkDynamic, ), themeMode: ThemeMode.system, home: FutureBuilder( builder: (context, snapshot) { if (snapshot.hasData && prefs != null) { return MyHomePage(title: 'Feet', api: api, prefs: prefs!); } return const SizedBox(); }, future: _loadPrefs(), ), )), ); } } class MyHomePage extends StatefulWidget { final String title; final FeverAPI api; final SharedPreferences prefs; const MyHomePage( {super.key, required this.title, required this.api, required this.prefs}); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { var _index = 0; var _fetched = false; Future refresh() => widget.api.request().withFeeds().withGroups().withItems().execute(); @override void initState() { super.initState(); refresh().then((_) => setState(() { _fetched = true; })); } @override Widget build(BuildContext context) { var loggedIn = widget.api.loggedIn(); var posts = widget.api.cache.items.getAll().values; var savedPosts = posts.where((element) => element.isSaved); var unreadPosts = posts.where((element) => !element.isRead); final pages = [ /* Posts list */ posts.isNotEmpty ? ListView( children: posts.map((value) => HomepagePost(post: value)).toList() ..sort((a, b) => b.post.createdOnTime.millisecondsSinceEpoch .compareTo(a.post.createdOnTime.millisecondsSinceEpoch)), ) : const CenteredPageHint( icon: Icons.coffee, text: "Your feed is currently empty."), /* Unread */ unreadPosts.isNotEmpty ? ListView( children: unreadPosts .map((value) => HomepagePost(post: value)) .toList() ..sort((a, b) => b.post.createdOnTime.millisecondsSinceEpoch .compareTo(a.post.createdOnTime.millisecondsSinceEpoch)), ) : const CenteredPageHint( icon: Icons.check, text: "Nothing new here!"), /* Saved posts */ savedPosts.isNotEmpty ? ListView( children: savedPosts .map((value) => HomepagePost(post: value)) .toList() ..sort((a, b) => b.post.createdOnTime.millisecondsSinceEpoch .compareTo(a.post.createdOnTime.millisecondsSinceEpoch)), ) : const CenteredPageHint( icon: Icons.bookmarks_outlined, text: "Nothing here yet. Try saving some posts!"), ]; // I'm aware that this is stupid, but I'm sick and tired and really don't care as long as it works if (!loggedIn) { Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { loggedIn = widget.api.loggedIn(); }); if (loggedIn) { timer.cancel(); refresh().then((_) => setState(() => _fetched = true)); } }); } return Scaffold( appBar: AppBar( title: Text(widget.title), actions: [ IconButton( onPressed: () { var theme = Theme.of(context); HapticFeedback.heavyImpact(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Refreshing feeds', style: TextStyle(color: theme.colorScheme.onBackground)), duration: const Duration(seconds: 2), backgroundColor: theme.colorScheme.secondaryContainer, ), ); refresh().then((_) { setState(() {}); }); }, icon: const Icon(Icons.replay_outlined), ), // Refresh PopupMenuButton( itemBuilder: (context) => [ PopupMenuItem( value: "log_out", child: const Text("Log out"), onTap: () { Future.delayed( const Duration(seconds: 0), () => showDialog( context: context, builder: (context) => AlertDialog( title: const Text("Log out"), content: const Text( "Are you sure you want to log out?"), actions: [ TextButton( onPressed: () async { Navigator.pop(context); await widget.prefs.clear(); setState(() { widget.api.apiKey = null; widget.api.apiUrl = null; widget.api.cache.clear(); }); }, child: const Text("Do it!")), TextButton( onPressed: () { Navigator.pop(context); }, child: const Text("Nevermind"), ), ], ), ), ); }, ) ]), ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: _index, onTap: (value) => setState(() => _index = value), items: const [ BottomNavigationBarItem( icon: Icon(Icons.list), label: 'Feed', ), BottomNavigationBarItem( icon: Icon(Icons.lens_blur), label: 'Unread', ), BottomNavigationBarItem( icon: Icon(Icons.bookmark_outline), activeIcon: Icon(Icons.bookmark), label: 'Saved', ), ], ), body: loggedIn ? (_fetched ? pages[_index] : const CenteredPageHint( icon: Icons.downloading, text: "Fetching your feeds...")) : Center(child: LoginPrompt(api: widget.api)), ); } }