Implement subscriptions/stars panel
This commit is contained in:
parent
8e0811796c
commit
a2d00dadbd
|
@ -9,8 +9,11 @@ import 'package:interstellar/src/widgets/subscription_button.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
|
||||
class DomainsScreen extends StatefulWidget {
|
||||
final bool onlySubbed;
|
||||
|
||||
const DomainsScreen({
|
||||
super.key,
|
||||
this.onlySubbed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -28,6 +31,10 @@ class _DomainsScreenState extends State<DomainsScreen> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.onlySubbed) {
|
||||
filter = KbinAPIDomainsFilter.subscribed;
|
||||
}
|
||||
|
||||
_pagingController.addPageRequestListener(_fetchPage);
|
||||
}
|
||||
|
||||
|
@ -61,62 +68,63 @@ class _DomainsScreenState extends State<DomainsScreen> {
|
|||
),
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
...whenLoggedIn(context, [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<KbinAPIDomainsFilter>(
|
||||
value: filter,
|
||||
onChanged: (newFilter) {
|
||||
if (newFilter != null) {
|
||||
setState(() {
|
||||
filter = newFilter;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: KbinAPIDomainsFilter.all,
|
||||
child: Text('All'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: KbinAPIDomainsFilter.subscribed,
|
||||
child: Text('Subscribed'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: KbinAPIDomainsFilter.blocked,
|
||||
child: Text('Blocked'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
]) ??
|
||||
[],
|
||||
if (filter == KbinAPIDomainsFilter.all)
|
||||
SizedBox(
|
||||
width: 128,
|
||||
child: TextFormField(
|
||||
initialValue: search,
|
||||
onChanged: (newSearch) {
|
||||
setState(() {
|
||||
search = newSearch;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
label: Text('Search')),
|
||||
),
|
||||
)
|
||||
],
|
||||
if (!widget.onlySubbed)
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
...whenLoggedIn(context, [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<KbinAPIDomainsFilter>(
|
||||
value: filter,
|
||||
onChanged: (newFilter) {
|
||||
if (newFilter != null) {
|
||||
setState(() {
|
||||
filter = newFilter;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: KbinAPIDomainsFilter.all,
|
||||
child: Text('All'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: KbinAPIDomainsFilter.subscribed,
|
||||
child: Text('Subscribed'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: KbinAPIDomainsFilter.blocked,
|
||||
child: Text('Blocked'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
]) ??
|
||||
[],
|
||||
if (filter == KbinAPIDomainsFilter.all)
|
||||
SizedBox(
|
||||
width: 128,
|
||||
child: TextFormField(
|
||||
initialValue: search,
|
||||
onChanged: (newSearch) {
|
||||
setState(() {
|
||||
search = newSearch;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
label: Text('Search')),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
PagedSliverList(
|
||||
pagingController: _pagingController,
|
||||
builderDelegate: PagedChildBuilderDelegate<DomainModel>(
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:interstellar/src/screens/settings/settings_controller.dart';
|
|||
import 'package:interstellar/src/utils/utils.dart';
|
||||
import 'package:interstellar/src/widgets/avatar.dart';
|
||||
import 'package:interstellar/src/widgets/markdown.dart';
|
||||
import 'package:interstellar/src/widgets/star_button.dart';
|
||||
import 'package:interstellar/src/widgets/subscription_button.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -45,6 +46,12 @@ class _MagazineScreenState extends State<MagazineScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final globalName = _data == null
|
||||
? null
|
||||
: _data!.name.contains('@')
|
||||
? '!${_data!.name}'
|
||||
: '!${_data!.name}@${context.watch<SettingsController>().instanceHost}';
|
||||
|
||||
return FeedScreen(
|
||||
source: FeedSource.magazine,
|
||||
sourceId: widget.magazineId,
|
||||
|
@ -87,11 +94,7 @@ class _MagazineScreenState extends State<MagazineScreen> {
|
|||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
_data!.name.contains('@')
|
||||
? '!${_data!.name}'
|
||||
: '!${_data!.name}@${context.read<SettingsController>().instanceHost}',
|
||||
),
|
||||
child: Text(globalName!),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -114,6 +117,7 @@ class _MagazineScreenState extends State<MagazineScreen> {
|
|||
}
|
||||
}),
|
||||
),
|
||||
StarButton(globalName),
|
||||
if (whenLoggedIn(context, true) == true)
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
|
|
|
@ -10,8 +10,11 @@ import 'package:interstellar/src/widgets/subscription_button.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
|
||||
class MagazinesScreen extends StatefulWidget {
|
||||
final bool onlySubbed;
|
||||
|
||||
const MagazinesScreen({
|
||||
super.key,
|
||||
this.onlySubbed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -30,6 +33,10 @@ class _MagazinesScreenState extends State<MagazinesScreen> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.onlySubbed) {
|
||||
filter = APIMagazinesFilter.subscribed;
|
||||
}
|
||||
|
||||
_pagingController.addPageRequestListener(_fetchPage);
|
||||
}
|
||||
|
||||
|
@ -65,108 +72,109 @@ class _MagazinesScreenState extends State<MagazinesScreen> {
|
|||
),
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<APIMagazinesFilter>(
|
||||
value: filter,
|
||||
onChanged: (newFilter) {
|
||||
if (newFilter != null) {
|
||||
setState(() {
|
||||
filter = newFilter;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: [
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.all,
|
||||
child: Text('All'),
|
||||
),
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.local,
|
||||
child: Text('Local'),
|
||||
),
|
||||
...(whenLoggedIn(context, [
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.subscribed,
|
||||
child: Text('Subscribed'),
|
||||
),
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.moderated,
|
||||
child: Text('Moderated'),
|
||||
),
|
||||
if (context
|
||||
.read<SettingsController>()
|
||||
.serverSoftware !=
|
||||
ServerSoftware.lemmy)
|
||||
if (!widget.onlySubbed)
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<APIMagazinesFilter>(
|
||||
value: filter,
|
||||
onChanged: (newFilter) {
|
||||
if (newFilter != null) {
|
||||
setState(() {
|
||||
filter = newFilter;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: [
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.all,
|
||||
child: Text('All'),
|
||||
),
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.local,
|
||||
child: Text('Local'),
|
||||
),
|
||||
...(whenLoggedIn(context, [
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.blocked,
|
||||
child: Text('Blocked'),
|
||||
value: APIMagazinesFilter.subscribed,
|
||||
child: Text('Subscribed'),
|
||||
),
|
||||
]) ??
|
||||
[])
|
||||
],
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.moderated,
|
||||
child: Text('Moderated'),
|
||||
),
|
||||
if (context
|
||||
.read<SettingsController>()
|
||||
.serverSoftware !=
|
||||
ServerSoftware.lemmy)
|
||||
const DropdownMenuItem(
|
||||
value: APIMagazinesFilter.blocked,
|
||||
child: Text('Blocked'),
|
||||
),
|
||||
]) ??
|
||||
[])
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
...(context.watch<SettingsController>().serverSoftware ==
|
||||
ServerSoftware.lemmy ||
|
||||
filter == APIMagazinesFilter.all ||
|
||||
filter == APIMagazinesFilter.local
|
||||
? [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<APIMagazinesSort>(
|
||||
value: sort,
|
||||
onChanged: (newSort) {
|
||||
if (newSort != null) {
|
||||
...(context.watch<SettingsController>().serverSoftware ==
|
||||
ServerSoftware.lemmy ||
|
||||
filter == APIMagazinesFilter.all ||
|
||||
filter == APIMagazinesFilter.local
|
||||
? [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<APIMagazinesSort>(
|
||||
value: sort,
|
||||
onChanged: (newSort) {
|
||||
if (newSort != null) {
|
||||
setState(() {
|
||||
sort = newSort;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: APIMagazinesSort.hot,
|
||||
child: Text('Top'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: APIMagazinesSort.active,
|
||||
child: Text('Active'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: APIMagazinesSort.newest,
|
||||
child: Text('Newest'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 128,
|
||||
child: TextFormField(
|
||||
initialValue: search,
|
||||
onChanged: (newSearch) {
|
||||
setState(() {
|
||||
sort = newSort;
|
||||
search = newSearch;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: APIMagazinesSort.hot,
|
||||
child: Text('Top'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: APIMagazinesSort.active,
|
||||
child: Text('Active'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: APIMagazinesSort.newest,
|
||||
child: Text('Newest'),
|
||||
),
|
||||
],
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
label: Text('Search')),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 128,
|
||||
child: TextFormField(
|
||||
initialValue: search,
|
||||
onChanged: (newSearch) {
|
||||
setState(() {
|
||||
search = newSearch;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
label: Text('Search')),
|
||||
),
|
||||
),
|
||||
]
|
||||
: []),
|
||||
],
|
||||
]
|
||||
: []),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
PagedSliverList(
|
||||
pagingController: _pagingController,
|
||||
builderDelegate: PagedChildBuilderDelegate<DetailedMagazineModel>(
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:interstellar/src/utils/utils.dart';
|
|||
import 'package:interstellar/src/widgets/avatar.dart';
|
||||
import 'package:interstellar/src/widgets/loading_template.dart';
|
||||
import 'package:interstellar/src/widgets/markdown.dart';
|
||||
import 'package:interstellar/src/widgets/star_button.dart';
|
||||
import 'package:interstellar/src/widgets/subscription_button.dart';
|
||||
import 'package:interstellar/src/widgets/text_editor.dart';
|
||||
import 'package:interstellar/src/widgets/wrapper.dart';
|
||||
|
@ -71,6 +72,10 @@ class _UserScreenState extends State<UserScreen> {
|
|||
final user = _data!;
|
||||
final currentFeedSortOption = feedSortSelect.getOption(_sort);
|
||||
|
||||
final globalName = user.name.contains('@')
|
||||
? '@${user.name}'
|
||||
: '@${user.name}@${context.watch<SettingsController>().instanceHost}';
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
|
@ -155,22 +160,23 @@ class _UserScreenState extends State<UserScreen> {
|
|||
matchesUsername: user.name) !=
|
||||
null)
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return ProfileEditScreen(_data!, (DetailedUserModel? user) {
|
||||
setState(() {
|
||||
_data = user;
|
||||
});
|
||||
});
|
||||
})
|
||||
),
|
||||
child: const Text("Edit"))
|
||||
)
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (context) {
|
||||
return ProfileEditScreen(_data!,
|
||||
(DetailedUserModel? user) {
|
||||
setState(() {
|
||||
_data = user;
|
||||
});
|
||||
});
|
||||
})),
|
||||
child: const Text("Edit"),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -209,11 +215,7 @@ class _UserScreenState extends State<UserScreen> {
|
|||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
user.name.contains('@')
|
||||
? '@${user.name}'
|
||||
: '@${user.name}@${context.read<SettingsController>().instanceHost}',
|
||||
),
|
||||
child: Text(globalName),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -236,6 +238,7 @@ class _UserScreenState extends State<UserScreen> {
|
|||
}
|
||||
}),
|
||||
),
|
||||
StarButton(globalName),
|
||||
if (whenLoggedIn(context, true) == true)
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
|
@ -332,10 +335,9 @@ class _UserScreenState extends State<UserScreen> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Markdown(
|
||||
user.about!,
|
||||
getNameHost(context, user.name),
|
||||
)
|
||||
),
|
||||
user.about!,
|
||||
getNameHost(context, user.name),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -452,7 +454,9 @@ class _UserScreenBodyState extends State<UserScreenBody> {
|
|||
@override
|
||||
void didUpdateWidget(covariant oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_pagingController.refresh();
|
||||
if (widget.mode != oldWidget.mode || widget.sort != oldWidget.sort) {
|
||||
_pagingController.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -10,8 +10,11 @@ import 'package:interstellar/src/widgets/subscription_button.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
|
||||
class UsersScreen extends StatefulWidget {
|
||||
final bool onlySubbed;
|
||||
|
||||
const UsersScreen({
|
||||
super.key,
|
||||
this.onlySubbed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -28,6 +31,10 @@ class _UsersScreenState extends State<UsersScreen> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.onlySubbed) {
|
||||
filter = api_users.UsersFilter.followed;
|
||||
}
|
||||
|
||||
_pagingController.addPageRequestListener(_fetchPage);
|
||||
}
|
||||
|
||||
|
@ -60,50 +67,51 @@ class _UsersScreenState extends State<UsersScreen> {
|
|||
),
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
...whenLoggedIn(context, [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<api_users.UsersFilter>(
|
||||
value: filter,
|
||||
onChanged: (newFilter) {
|
||||
if (newFilter != null) {
|
||||
setState(() {
|
||||
filter = newFilter;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.all,
|
||||
child: Text('All'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.followed,
|
||||
child: Text('Followed'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.followers,
|
||||
child: Text('Followers'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.blocked,
|
||||
child: Text('Blocked'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
]) ??
|
||||
[],
|
||||
],
|
||||
if (!widget.onlySubbed)
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
...whenLoggedIn(context, [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: DropdownButton<api_users.UsersFilter>(
|
||||
value: filter,
|
||||
onChanged: (newFilter) {
|
||||
if (newFilter != null) {
|
||||
setState(() {
|
||||
filter = newFilter;
|
||||
_pagingController.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.all,
|
||||
child: Text('All'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.followed,
|
||||
child: Text('Followed'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.followers,
|
||||
child: Text('Followers'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: api_users.UsersFilter.blocked,
|
||||
child: Text('Blocked'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
]) ??
|
||||
[],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
PagedSliverList(
|
||||
pagingController: _pagingController,
|
||||
builderDelegate: PagedChildBuilderDelegate<DetailedUserModel>(
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:interstellar/src/api/feed_source.dart';
|
|||
import 'package:interstellar/src/models/magazine.dart';
|
||||
import 'package:interstellar/src/models/post.dart';
|
||||
import 'package:interstellar/src/screens/create_screen.dart';
|
||||
import 'package:interstellar/src/screens/feed/nav_drawer.dart';
|
||||
import 'package:interstellar/src/screens/feed/post_item.dart';
|
||||
import 'package:interstellar/src/screens/feed/post_page.dart';
|
||||
import 'package:interstellar/src/screens/settings/settings_controller.dart';
|
||||
|
@ -361,6 +362,7 @@ class _FeedScreenState extends State<FeedScreen> {
|
|||
)
|
||||
.toList(),
|
||||
),
|
||||
drawer: widget.sourceId != null ? null : const NavDrawer(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:interstellar/src/api/domains.dart';
|
||||
import 'package:interstellar/src/api/magazines.dart';
|
||||
import 'package:interstellar/src/api/users.dart';
|
||||
import 'package:interstellar/src/models/domain.dart';
|
||||
import 'package:interstellar/src/models/magazine.dart';
|
||||
import 'package:interstellar/src/models/user.dart';
|
||||
import 'package:interstellar/src/screens/explore/domain_screen.dart';
|
||||
import 'package:interstellar/src/screens/explore/domains_screen.dart';
|
||||
import 'package:interstellar/src/screens/explore/magazine_screen.dart';
|
||||
import 'package:interstellar/src/screens/explore/magazines_screen.dart';
|
||||
import 'package:interstellar/src/screens/explore/user_screen.dart';
|
||||
import 'package:interstellar/src/screens/explore/users_screen.dart';
|
||||
import 'package:interstellar/src/screens/settings/settings_controller.dart';
|
||||
import 'package:interstellar/src/widgets/avatar.dart';
|
||||
import 'package:interstellar/src/widgets/settings_header.dart';
|
||||
import 'package:interstellar/src/widgets/star_button.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class NavDrawer extends StatefulWidget {
|
||||
const NavDrawer({super.key});
|
||||
|
||||
@override
|
||||
State<NavDrawer> createState() => _NavDrawerState();
|
||||
}
|
||||
|
||||
class _NavDrawerState extends State<NavDrawer> {
|
||||
List<DetailedMagazineModel>? subbedMagazines;
|
||||
List<DetailedUserModel>? subbedUsers;
|
||||
List<DomainModel>? subbedDomains;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (context.read<SettingsController>().isLoggedIn) {
|
||||
context
|
||||
.read<SettingsController>()
|
||||
.api
|
||||
.magazines
|
||||
.list(filter: APIMagazinesFilter.subscribed)
|
||||
.then((value) => setState(() {
|
||||
if (value.items.isNotEmpty) {
|
||||
subbedMagazines = value.items;
|
||||
}
|
||||
}));
|
||||
if (context.read<SettingsController>().serverSoftware !=
|
||||
ServerSoftware.lemmy) {
|
||||
context
|
||||
.read<SettingsController>()
|
||||
.api
|
||||
.users
|
||||
.list(filter: UsersFilter.followed)
|
||||
.then((value) => setState(() {
|
||||
if (value.items.isNotEmpty) {
|
||||
subbedUsers = value.items;
|
||||
}
|
||||
}));
|
||||
context
|
||||
.read<SettingsController>()
|
||||
.api
|
||||
.domains
|
||||
.list(filter: KbinAPIDomainsFilter.subscribed)
|
||||
.then((value) => setState(() {
|
||||
if (value.items.isNotEmpty) {
|
||||
subbedDomains = value.items;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: SettingsHeader('Stars'),
|
||||
),
|
||||
if (context.watch<SettingsController>().stars.isEmpty)
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
'This feels empty; star a magazine or user to appear here.',
|
||||
style: TextStyle(fontWeight: FontWeight.w300),
|
||||
),
|
||||
),
|
||||
...(context.watch<SettingsController>().stars.toList()..sort()).map(
|
||||
(star) => ListTile(
|
||||
title: Text(star),
|
||||
onTap: () async {
|
||||
String name = star.substring(1);
|
||||
if (name.endsWith(
|
||||
context.read<SettingsController>().instanceHost)) {
|
||||
name = name.split('@').first;
|
||||
}
|
||||
|
||||
switch (star[0]) {
|
||||
case '@':
|
||||
final user = await context
|
||||
.read<SettingsController>()
|
||||
.api
|
||||
.users
|
||||
.getByName(name);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
UserScreen(user.id, initData: user),
|
||||
),
|
||||
);
|
||||
break;
|
||||
|
||||
case '!':
|
||||
final magazine = await context
|
||||
.read<SettingsController>()
|
||||
.api
|
||||
.magazines
|
||||
.getByName(name);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
MagazineScreen(magazine.id, initData: magazine),
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
trailing: StarButton(star),
|
||||
),
|
||||
),
|
||||
if (context.watch<SettingsController>().isLoggedIn &&
|
||||
(subbedMagazines != null ||
|
||||
subbedUsers != null ||
|
||||
subbedDomains != null)) ...[
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: SettingsHeader('Subscriptions'),
|
||||
),
|
||||
if (subbedMagazines != null) ...[
|
||||
...subbedMagazines!
|
||||
.asMap()
|
||||
.map(
|
||||
(index, magazine) => MapEntry(
|
||||
index,
|
||||
ListTile(
|
||||
title: Text(magazine.name),
|
||||
leading: magazine.icon == null
|
||||
? null
|
||||
: Avatar(magazine.icon, radius: 16),
|
||||
trailing: StarButton(magazine.name.contains('@')
|
||||
? '!${magazine.name}'
|
||||
: '!${magazine.name}@${context.watch<SettingsController>().instanceHost}'),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MagazineScreen(
|
||||
magazine.id,
|
||||
initData: magazine,
|
||||
onUpdate: (newValue) {
|
||||
setState(() {
|
||||
subbedMagazines![index] = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.values,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar:
|
||||
AppBar(title: const Text('Magazine Subscriptions')),
|
||||
body: const MagazinesScreen(onlySubbed: true),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Text('All Magazine Subs'),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (context.read<SettingsController>().serverSoftware !=
|
||||
ServerSoftware.lemmy &&
|
||||
subbedUsers != null) ...[
|
||||
...subbedUsers!
|
||||
.asMap()
|
||||
.map(
|
||||
(index, user) => MapEntry(
|
||||
index,
|
||||
ListTile(
|
||||
title: Text(user.name),
|
||||
leading: user.avatar == null
|
||||
? null
|
||||
: Avatar(user.avatar, radius: 16),
|
||||
trailing: StarButton(user.name.contains('@')
|
||||
? '@${user.name}'
|
||||
: '@${user.name}@${context.watch<SettingsController>().instanceHost}'),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => UserScreen(
|
||||
user.id,
|
||||
initData: user,
|
||||
onUpdate: (newValue) {
|
||||
setState(() {
|
||||
subbedUsers![index] = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.values,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: AppBar(title: const Text('User Follows')),
|
||||
body: const UsersScreen(onlySubbed: true),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Text('All User Follows'),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (context.read<SettingsController>().serverSoftware !=
|
||||
ServerSoftware.lemmy &&
|
||||
subbedDomains != null) ...[
|
||||
...subbedDomains!
|
||||
.asMap()
|
||||
.map(
|
||||
(index, domain) => MapEntry(
|
||||
index,
|
||||
ListTile(
|
||||
title: Text(domain.name),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DomainScreen(
|
||||
domain.id,
|
||||
initData: domain,
|
||||
onUpdate: (newValue) {
|
||||
setState(() {
|
||||
subbedDomains![index] = domain;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.values,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: AppBar(title: const Text('Domain Subscriptions')),
|
||||
body: const DomainsScreen(onlySubbed: true),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Text('All Domain Subs'),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:interstellar/src/api/comments.dart';
|
||||
import 'package:interstellar/src/screens/feed/feed_screen.dart';
|
||||
import 'package:interstellar/src/widgets/actions.dart';
|
||||
import 'package:interstellar/src/widgets/settings_header.dart';
|
||||
|
||||
import 'settings_controller.dart';
|
||||
|
||||
|
@ -32,11 +33,7 @@ class ActionSettings extends StatelessWidget {
|
|||
body: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Feed Actions',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Feed Actions'),
|
||||
ActionSettingsItem(
|
||||
metadata: feedActionExpandFab,
|
||||
location: controller.feedActionExpandFab,
|
||||
|
@ -72,11 +69,7 @@ class ActionSettings extends StatelessWidget {
|
|||
location: controller.feedActionSetType,
|
||||
setLocation: controller.updateFeedActionSetType,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Defaults',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Defaults'),
|
||||
ListTile(
|
||||
title: const Text('Feed Type'),
|
||||
leading: const Icon(Icons.tab),
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:interstellar/src/utils/language_codes.dart';
|
||||
import 'package:interstellar/src/utils/themes.dart';
|
||||
import 'package:interstellar/src/widgets/selection_menu.dart';
|
||||
import 'package:interstellar/src/widgets/settings_header.dart';
|
||||
|
||||
import 'settings_controller.dart';
|
||||
|
||||
|
@ -30,11 +31,7 @@ class GeneralScreen extends StatelessWidget {
|
|||
body: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child:
|
||||
Text('Theme', style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Theme'),
|
||||
ListTile(
|
||||
title: const Text('Theme Mode'),
|
||||
leading: const Icon(Icons.brightness_medium),
|
||||
|
@ -87,11 +84,7 @@ class GeneralScreen extends StatelessWidget {
|
|||
),
|
||||
enabled: !controller.useDynamicColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Post Appearance',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Post Appearance'),
|
||||
ListTile(
|
||||
title: const Text('Image Position'),
|
||||
leading: const Icon(Icons.image),
|
||||
|
@ -148,11 +141,7 @@ class GeneralScreen extends StatelessWidget {
|
|||
onChanged: controller.updatePostUseCardPreview,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Language',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Language'),
|
||||
SwitchListTile(
|
||||
title: const Text('Use Account Language Filter'),
|
||||
subtitle: const Text(
|
||||
|
@ -222,11 +211,7 @@ class GeneralScreen extends StatelessWidget {
|
|||
children: [Text(getLangName(controller.defaultCreateLang))],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child:
|
||||
Text('Other', style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Other'),
|
||||
ListTile(
|
||||
title: const Text('Always Show Instance'),
|
||||
leading: const Icon(Icons.public),
|
||||
|
|
|
@ -110,6 +110,9 @@ class SettingsController with ChangeNotifier {
|
|||
late String _defaultCreateLang;
|
||||
String get defaultCreateLang => _defaultCreateLang;
|
||||
|
||||
late Set<String> _stars;
|
||||
Set<String> get stars => _stars;
|
||||
|
||||
late Map<String, Server> _servers;
|
||||
late Map<String, Account> _accounts;
|
||||
late String _selectedAccount;
|
||||
|
@ -211,6 +214,8 @@ class SettingsController with ChangeNotifier {
|
|||
_langFilter = prefs.getStringList("langFilter")?.toSet() ?? {};
|
||||
_defaultCreateLang = prefs.getString("defaultCreateLang") ?? 'en';
|
||||
|
||||
_stars = prefs.getStringList("stars")?.toSet() ?? {};
|
||||
|
||||
_servers = (jsonDecode(prefs.getString('servers') ??
|
||||
'{"kbin.earth":{"software":"mbin"}}') as Map<String, dynamic>)
|
||||
.map((key, value) => MapEntry(key, Server.fromJson(value)));
|
||||
|
@ -469,6 +474,34 @@ class SettingsController with ChangeNotifier {
|
|||
await prefs.setString('defaultCreateLang', newValue);
|
||||
}
|
||||
|
||||
Future<void> addStar(
|
||||
String? newStar,
|
||||
) async {
|
||||
if (newStar == null) return;
|
||||
if (_stars.contains(newStar)) return;
|
||||
|
||||
_stars.add(newStar);
|
||||
|
||||
notifyListeners();
|
||||
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setStringList('stars', _stars.toList());
|
||||
}
|
||||
|
||||
Future<void> removeStar(
|
||||
String? oldStar,
|
||||
) async {
|
||||
if (oldStar == null) return;
|
||||
if (!_stars.contains(oldStar)) return;
|
||||
|
||||
_stars.remove(oldStar);
|
||||
|
||||
notifyListeners();
|
||||
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setStringList('stars', _stars.toList());
|
||||
}
|
||||
|
||||
Future<void> saveServer(ServerSoftware software, String server) async {
|
||||
if (_servers.containsKey(server) &&
|
||||
_servers[server]!.software == software) {
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:interstellar/src/screens/settings/action_settings.dart';
|
||||
import 'package:interstellar/src/screens/settings/general_settings.dart';
|
||||
import 'package:interstellar/src/screens/settings/login_select.dart';
|
||||
import 'package:interstellar/src/widgets/settings_header.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'settings_controller.dart';
|
||||
|
@ -44,11 +45,7 @@ class SettingsScreen extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child:
|
||||
Text('Presets', style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Presets'),
|
||||
ListTile(
|
||||
title: const Text('Classic Layout'),
|
||||
onTap: () async {
|
||||
|
@ -69,11 +66,7 @@ class SettingsScreen extends StatelessWidget {
|
|||
));
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text('Accounts',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
const SettingsHeader('Accounts'),
|
||||
...(controller.accounts.keys.toList()
|
||||
..sort((a, b) {
|
||||
final [aLocal, aHost] = a.split('@');
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsHeader extends StatelessWidget {
|
||||
final String text;
|
||||
|
||||
const SettingsHeader(this.text, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Text(text,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.merge(const TextStyle(fontWeight: FontWeight.w600))),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:interstellar/src/screens/settings/settings_controller.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class StarButton extends StatelessWidget {
|
||||
final String name;
|
||||
|
||||
const StarButton(
|
||||
this.name, {
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isStarred = context.watch<SettingsController>().stars.contains(name);
|
||||
|
||||
return IconButton(
|
||||
onPressed: isStarred
|
||||
? () => context.read<SettingsController>().removeStar(name)
|
||||
: () => context.read<SettingsController>().addStar(name),
|
||||
icon: context.read<SettingsController>().stars.contains(name)
|
||||
? const Icon(Icons.star)
|
||||
: const Icon(Icons.star_border),
|
||||
color: isStarred ? Colors.yellow : null,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue