transition all api v1 calls to api v2

This commit is contained in:
krawieck 2021-01-24 20:01:55 +01:00
parent 983734a506
commit c2f528750a
25 changed files with 462 additions and 434 deletions

View File

@ -1,4 +1,4 @@
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'util/hot_rank.dart'; import 'util/hot_rank.dart';
@ -25,13 +25,16 @@ extension on CommentSortType {
a.comment.computedHotRank.compareTo(b.comment.computedHotRank); a.comment.computedHotRank.compareTo(b.comment.computedHotRank);
case CommentSortType.new_: case CommentSortType.new_:
return (b, a) => a.comment.published.compareTo(b.comment.published); return (b, a) =>
a.comment.comment.published.compareTo(b.comment.comment.published);
case CommentSortType.old: case CommentSortType.old:
return (b, a) => b.comment.published.compareTo(a.comment.published); return (b, a) =>
b.comment.comment.published.compareTo(a.comment.comment.published);
case CommentSortType.top: case CommentSortType.top:
return (b, a) => a.comment.score.compareTo(b.comment.score); return (b, a) =>
a.comment.counts.score.compareTo(b.comment.counts.score);
} }
throw Exception('unreachable'); throw Exception('unreachable');
@ -50,15 +53,16 @@ class CommentTree {
static List<CommentTree> fromList(List<CommentView> comments) { static List<CommentTree> fromList(List<CommentView> comments) {
CommentTree gatherChildren(CommentTree parent) { CommentTree gatherChildren(CommentTree parent) {
for (final el in comments) { for (final el in comments) {
if (el.parentId == parent.comment.id) { if (el.comment.parentId == parent.comment.comment.id) {
parent.children.add(gatherChildren(CommentTree(el))); parent.children.add(gatherChildren(CommentTree(el)));
} }
} }
return parent; return parent;
} }
final topLevelParents = final topLevelParents = comments
comments.where((e) => e.parentId == null).map((e) => CommentTree(e)); .where((e) => e.comment.parentId == null)
.map((e) => CommentTree(e));
final result = topLevelParents.map(gatherChildren).toList(); final result = topLevelParents.map(gatherChildren).toList();
return result; return result;

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../pages/settings.dart'; import '../pages/settings.dart';
import '../util/goto.dart'; import '../util/goto.dart';

View File

@ -2,7 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'package:url_launcher/url_launcher.dart' as ul; import 'package:url_launcher/url_launcher.dart' as ul;
import '../hooks/delayed_loading.dart'; import '../hooks/delayed_loading.dart';
@ -32,10 +32,10 @@ class AddAccountPage extends HookWidget {
final selectedInstance = useState(instanceHost); final selectedInstance = useState(instanceHost);
final icon = useState<String>(null); final icon = useState<String>(null);
useEffect(() { useEffect(() {
LemmyApi(selectedInstance.value) // LemmyApi(selectedInstance.value).v2.run(GetPost(id: ));
.v1 LemmyApiV2(selectedInstance.value)
.getSite() .run(GetSite())
.then((site) => icon.value = site.site.icon); .then((site) => icon.value = site.siteView.site.icon);
return null; return null;
}, [selectedInstance.value]); }, [selectedInstance.value]);

View File

@ -1,7 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../hooks/debounce.dart'; import '../hooks/debounce.dart';
import '../hooks/stores.dart'; import '../hooks/stores.dart';
@ -33,7 +33,7 @@ class AddInstancePage extends HookWidget {
return; return;
} }
try { try {
icon.value = (await LemmyApi(inst).v1.getSite()).site.icon; icon.value = (await LemmyApiV2(inst).run(GetSite())).siteView.site.icon;
isSite.value = true; isSite.value = true;
// ignore: avoid_catches_without_on_clauses // ignore: avoid_catches_without_on_clauses
} catch (e) { } catch (e) {
@ -75,7 +75,7 @@ class AddInstancePage extends HookWidget {
), ),
body: ListView( body: ListView(
children: [ children: [
if (isSite.value == true) if (isSite.value == true && icon.value != null)
SizedBox( SizedBox(
height: 150, height: 150,
child: FullscreenableImage( child: FullscreenableImage(

View File

@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../util/goto.dart'; import '../util/goto.dart';
import '../widgets/markdown_text.dart'; import '../widgets/markdown_text.dart';
@ -55,23 +55,23 @@ class CommunitiesListItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) => ListTile( Widget build(BuildContext context) => ListTile(
title: Text(community.name), title: Text(community.community.name),
subtitle: community.description != null subtitle: community.community.description != null
? Opacity( ? Opacity(
opacity: 0.7, opacity: 0.7,
child: MarkdownText( child: MarkdownText(
community.description, community.community.description,
instanceHost: community.instanceHost, instanceHost: community.instanceHost,
), ),
) )
: null, : null,
onTap: () => onTap: () => goToCommunity.byId(
goToCommunity.byId(context, community.instanceHost, community.id), context, community.instanceHost, community.community.id),
leading: community.icon != null leading: community.community.icon != null
? CachedNetworkImage( ? CachedNetworkImage(
height: 50, height: 50,
width: 50, width: 50,
imageUrl: community.icon, imageUrl: community.community.icon,
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,

View File

@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fuzzy/fuzzy.dart'; import 'package:fuzzy/fuzzy.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../hooks/delayed_loading.dart'; import '../hooks/delayed_loading.dart';
import '../hooks/refreshable.dart'; import '../hooks/refreshable.dart';
@ -33,8 +33,9 @@ class CommunitiesTab extends HookWidget {
getInstances() { getInstances() {
final futures = accountsStore.loggedInInstances final futures = accountsStore.loggedInInstances
.map( .map(
(instanceHost) => (instanceHost) => LemmyApiV2(instanceHost)
LemmyApi(instanceHost).v1.getSite().then((e) => e.site), .run(GetSite())
.then((e) => e.siteView.site),
) )
.toList(); .toList();
@ -44,14 +45,13 @@ class CommunitiesTab extends HookWidget {
getCommunities() { getCommunities() {
final futures = accountsStore.loggedInInstances final futures = accountsStore.loggedInInstances
.map( .map(
(instanceHost) => LemmyApi(instanceHost) (instanceHost) => LemmyApiV2(instanceHost)
.v1 .run(GetUserDetails(
.getUserDetails(
sort: SortType.active, sort: SortType.active,
savedOnly: false, savedOnly: false,
userId: userId:
accountsStore.defaultTokenFor(instanceHost).payload.id, accountsStore.defaultTokenFor(instanceHost).payload.id,
) ))
.then((e) => e.follows), .then((e) => e.follows),
) )
.toList(); .toList();
@ -108,8 +108,8 @@ class CommunitiesTab extends HookWidget {
final instances = instancesRefreshable.snapshot.data; final instances = instancesRefreshable.snapshot.data;
final communities = communitiesRefreshable.snapshot.data final communities = communitiesRefreshable.snapshot.data
..forEach( ..forEach((e) =>
(e) => e.sort((a, b) => a.communityName.compareTo(b.communityName))); e.sort((a, b) => a.community.name.compareTo(b.community.name)));
final filterIcon = () { final filterIcon = () {
if (filterController.text.isEmpty) { if (filterController.text.isEmpty) {
@ -127,12 +127,12 @@ class CommunitiesTab extends HookWidget {
filterCommunities(List<CommunityFollowerView> comm) { filterCommunities(List<CommunityFollowerView> comm) {
final matches = Fuzzy( final matches = Fuzzy(
comm.map((e) => e.communityName).toList(), comm.map((e) => e.community.name).toList(),
options: FuzzyOptions(threshold: 0.5), options: FuzzyOptions(threshold: 0.5),
).search(filterController.text).map((e) => e.item); ).search(filterController.text).map((e) => e.item);
return matches return matches
.map((match) => comm.firstWhere((e) => e.communityName == match)); .map((match) => comm.firstWhere((e) => e.community.name == match));
} }
toggleCollapse(int i) => isCollapsed.value = toggleCollapse(int i) => isCollapsed.value =
@ -203,18 +203,18 @@ class CommunitiesTab extends HookWidget {
onTap: () => goToCommunity.byId( onTap: () => goToCommunity.byId(
context, context,
accountsStore.loggedInInstances.elementAt(i), accountsStore.loggedInInstances.elementAt(i),
comm.communityId), comm.community.id),
dense: true, dense: true,
leading: VerticalDivider( leading: VerticalDivider(
color: theme.hintColor, color: theme.hintColor,
), ),
title: Row( title: Row(
children: [ children: [
if (comm.communityIcon != null) if (comm.community.icon != null)
CachedNetworkImage( CachedNetworkImage(
height: 30, height: 30,
width: 30, width: 30,
imageUrl: comm.communityIcon, imageUrl: comm.community.icon,
imageBuilder: (context, imageProvider) => imageBuilder: (context, imageProvider) =>
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -231,14 +231,14 @@ class CommunitiesTab extends HookWidget {
const SizedBox(width: 30), const SizedBox(width: 30),
const SizedBox(width: 10), const SizedBox(width: 10),
Text( Text(
'''!${comm.communityName}${comm.isLocal ? '' : '@${comm.originInstanceHost}'}''', '''!${comm.community.name}${comm.community.local ? '' : '@${comm.originInstanceHost}'}''',
), ),
], ],
), ),
trailing: _CommunitySubscribeToggle( trailing: _CommunitySubscribeToggle(
key: ValueKey(comm.communityId), key: ValueKey(comm.community.id),
instanceHost: comm.instanceHost, instanceHost: comm.instanceHost,
communityId: comm.communityId, communityId: comm.community.id,
), ),
), ),
) )
@ -272,11 +272,11 @@ class _CommunitySubscribeToggle extends HookWidget {
delayed.start(); delayed.start();
try { try {
await LemmyApi(instanceHost).v1.followCommunity( await LemmyApiV2(instanceHost).run(FollowCommunity(
communityId: communityId, communityId: communityId,
follow: !subbed.value, follow: !subbed.value,
auth: accountsStore.defaultTokenFor(instanceHost).raw, auth: accountsStore.defaultTokenFor(instanceHost).raw,
); ));
subbed.value = !subbed.value; subbed.value = !subbed.value;
} on Exception catch (err) { } on Exception catch (err) {
Scaffold.of(context).showSnackBar(SnackBar( Scaffold.of(context).showSnackBar(SnackBar(

View File

@ -3,7 +3,7 @@ import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'package:url_launcher/url_launcher.dart' as ul; import 'package:url_launcher/url_launcher.dart' as ul;
import '../hooks/delayed_loading.dart'; import '../hooks/delayed_loading.dart';
@ -45,8 +45,8 @@ class CommunityPage extends HookWidget {
_community = null; _community = null;
CommunityPage.fromCommunityView(this._community) CommunityPage.fromCommunityView(this._community)
: instanceHost = _community.instanceHost, : instanceHost = _community.instanceHost,
communityId = _community.id, communityId = _community.community.id,
communityName = _community.name; communityName = _community.community.name;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -57,15 +57,15 @@ class CommunityPage extends HookWidget {
final token = accountsStore.defaultTokenFor(instanceHost); final token = accountsStore.defaultTokenFor(instanceHost);
if (communityId != null) { if (communityId != null) {
return LemmyApi(instanceHost).v1.getCommunity( return LemmyApiV2(instanceHost).run(GetCommunity(
id: communityId, id: communityId,
auth: token?.raw, auth: token?.raw,
); ));
} else { } else {
return LemmyApi(instanceHost).v1.getCommunity( return LemmyApiV2(instanceHost).run(GetCommunity(
name: communityName, name: communityName,
auth: token?.raw, auth: token?.raw,
); ));
} }
}); });
@ -73,7 +73,7 @@ class CommunityPage extends HookWidget {
final community = () { final community = () {
if (fullCommunitySnap.hasData) { if (fullCommunitySnap.hasData) {
return fullCommunitySnap.data.community; return fullCommunitySnap.data.communityView;
} else if (_community != null) { } else if (_community != null) {
return _community; return _community;
} else { } else {
@ -111,7 +111,7 @@ class CommunityPage extends HookWidget {
// FUNCTIONS // FUNCTIONS
void _share() => void _share() =>
Share.text('Share instance', community.actorId, 'text/plain'); Share.text('Share instance', community.community.actorId, 'text/plain');
void _openMoreMenu() { void _openMoreMenu() {
showModalBottomSheet( showModalBottomSheet(
@ -123,8 +123,9 @@ class CommunityPage extends HookWidget {
ListTile( ListTile(
leading: const Icon(Icons.open_in_browser), leading: const Icon(Icons.open_in_browser),
title: const Text('Open in browser'), title: const Text('Open in browser'),
onTap: () async => await ul.canLaunch(community.actorId) onTap: () async => await ul
? ul.launch(community.actorId) .canLaunch(community.community.actorId)
? ul.launch(community.community.actorId)
: Scaffold.of(context).showSnackBar( : Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("can't open in browser"))), const SnackBar(content: Text("can't open in browser"))),
), ),
@ -133,11 +134,10 @@ class CommunityPage extends HookWidget {
title: const Text('Nerd stuff'), title: const Text('Nerd stuff'),
onTap: () { onTap: () {
showInfoTablePopup(context, { showInfoTablePopup(context, {
'id': community.id, 'id': community.community.id,
'actorId': community.actorId, 'actorId': community.community.actorId,
'created by': '@${community.creatorName}', 'created by': '@${community.creator.name}',
'hot rank': community.hotRank, 'published': community.community.published,
'published': community.published,
}); });
}, },
), ),
@ -160,7 +160,7 @@ class CommunityPage extends HookWidget {
backgroundColor: theme.cardColor, backgroundColor: theme.cardColor,
brightness: theme.brightness, brightness: theme.brightness,
iconTheme: theme.iconTheme, iconTheme: theme.iconTheme,
title: Text('!${community.name}', title: Text('!${community.community.name}',
style: TextStyle(color: colorOnCard)), style: TextStyle(color: colorOnCard)),
actions: [ actions: [
IconButton(icon: const Icon(Icons.share), onPressed: _share), IconButton(icon: const Icon(Icons.share), onPressed: _share),
@ -190,26 +190,26 @@ class CommunityPage extends HookWidget {
children: [ children: [
InfinitePostList( InfinitePostList(
fetcher: (page, batchSize, sort) => fetcher: (page, batchSize, sort) =>
LemmyApi(community.instanceHost).v1.getPosts( LemmyApiV2(community.instanceHost).run(GetPosts(
type: PostListingType.community, type: PostListingType.community,
sort: sort, sort: sort,
communityId: community.id, communityId: community.community.id,
page: page, page: page,
limit: batchSize, limit: batchSize,
), )),
), ),
InfiniteCommentList( InfiniteCommentList(
fetcher: (page, batchSize, sortType) => fetcher: (page, batchSize, sortType) =>
LemmyApi(community.instanceHost).v1.getComments( LemmyApiV2(community.instanceHost).run(GetComments(
communityId: community.id, communityId: community.community.id,
auth: accountsStore auth: accountsStore
.defaultTokenFor(community.instanceHost) .defaultTokenFor(community.instanceHost)
?.raw, ?.raw,
type: CommentListingType.community, type: CommentListingType.community,
sort: sortType, sort: sortType,
limit: batchSize, limit: batchSize,
page: page, page: page,
)), ))),
_AboutTab( _AboutTab(
community: community, community: community,
moderators: fullCommunitySnap.data?.moderators, moderators: fullCommunitySnap.data?.moderators,
@ -237,7 +237,7 @@ class _CommunityOverview extends StatelessWidget {
final theme = Theme.of(context); final theme = Theme.of(context);
final shadow = BoxShadow(color: theme.canvasColor, blurRadius: 5); final shadow = BoxShadow(color: theme.canvasColor, blurRadius: 5);
final icon = community.icon != null final icon = community.community.icon != null
? Stack( ? Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
@ -256,9 +256,9 @@ class _CommunityOverview extends StatelessWidget {
width: 83, width: 83,
height: 83, height: 83,
child: FullscreenableImage( child: FullscreenableImage(
url: community.icon, url: community.community.icon,
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: community.icon, imageUrl: community.community.icon,
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
@ -277,11 +277,11 @@ class _CommunityOverview extends StatelessWidget {
: null; : null;
return Stack(children: [ return Stack(children: [
if (community.banner != null) if (community.community.banner != null)
FullscreenableImage( FullscreenableImage(
url: community.banner, url: community.community.banner,
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: community.banner, imageUrl: community.community.banner,
errorWidget: (_, __, ___) => const SizedBox.shrink(), errorWidget: (_, __, ___) => const SizedBox.shrink(),
), ),
), ),
@ -289,7 +289,7 @@ class _CommunityOverview extends StatelessWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 45), padding: const EdgeInsets.only(top: 45),
child: Column(children: [ child: Column(children: [
if (community.icon != null) icon, if (community.community.icon != null) icon,
// NAME // NAME
Center( Center(
child: Padding( child: Padding(
@ -304,7 +304,7 @@ class _CommunityOverview extends StatelessWidget {
text: '!', text: '!',
style: TextStyle(fontWeight: FontWeight.w200)), style: TextStyle(fontWeight: FontWeight.w200)),
TextSpan( TextSpan(
text: community.name, text: community.community.name,
style: const TextStyle(fontWeight: FontWeight.w600)), style: const TextStyle(fontWeight: FontWeight.w600)),
const TextSpan( const TextSpan(
text: '@', text: '@',
@ -326,7 +326,7 @@ class _CommunityOverview extends StatelessWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 8, left: 20, right: 20), padding: const EdgeInsets.only(top: 8, left: 20, right: 20),
child: Text( child: Text(
community.title, community.community.title,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: style:
TextStyle(fontWeight: FontWeight.w300, shadows: [shadow]), TextStyle(fontWeight: FontWeight.w300, shadows: [shadow]),
@ -346,7 +346,7 @@ class _CommunityOverview extends StatelessWidget {
padding: EdgeInsets.only(right: 3), padding: EdgeInsets.only(right: 3),
child: Icon(Icons.people, size: 20), child: Icon(Icons.people, size: 20),
), ),
Text(compactNumber(community.numberOfSubscribers)), Text(compactNumber(community.counts.subscribers)),
const Spacer( const Spacer(
flex: 4, flex: 4,
), ),
@ -416,10 +416,10 @@ class _AboutTab extends StatelessWidget {
return ListView( return ListView(
padding: const EdgeInsets.only(top: 20), padding: const EdgeInsets.only(top: 20),
children: [ children: [
if (community.description != null) ...[ if (community.community.description != null) ...[
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 15), padding: const EdgeInsets.symmetric(horizontal: 15),
child: MarkdownText(community.description, child: MarkdownText(community.community.description,
instanceHost: community.instanceHost), instanceHost: community.instanceHost),
), ),
const _Divider(), const _Divider(),
@ -435,13 +435,13 @@ class _AboutTab extends StatelessWidget {
child: _Badge('X users online'), child: _Badge('X users online'),
), ),
_Badge( _Badge(
'''${community.numberOfSubscribers} subscriber${pluralS(community.numberOfSubscribers)}'''), '''${community.counts.subscribers} subscriber${pluralS(community.counts.subscribers)}'''),
_Badge( _Badge(
'''${community.numberOfPosts} post${pluralS(community.numberOfPosts)}'''), '''${community.counts.posts} post${pluralS(community.counts.posts)}'''),
Padding( Padding(
padding: const EdgeInsets.only(right: 15), padding: const EdgeInsets.only(right: 15),
child: _Badge( child: _Badge(
'''${community.numberOfComments} comment${pluralS(community.numberOfComments)}'''), '''${community.counts.comments} comment${pluralS(community.counts.comments)}'''),
), ),
], ],
), ),
@ -454,7 +454,7 @@ class _AboutTab extends StatelessWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
onPressed: goToCategories, onPressed: goToCategories,
child: Text(community.categoryName), child: Text(community.category.name),
), ),
), ),
const _Divider(), const _Divider(),
@ -475,9 +475,12 @@ class _AboutTab extends StatelessWidget {
child: Text('Mods:', style: theme.textTheme.subtitle2), child: Text('Mods:', style: theme.textTheme.subtitle2),
), ),
for (final mod in moderators) for (final mod in moderators)
// TODO: add user picture, maybe make it into reusable component
ListTile( ListTile(
title: Text(mod.userPreferredUsername ?? '@${mod.userName}'), title: Text(
onTap: () => goToUser.byId(context, mod.instanceHost, mod.userId), mod.moderator.preferredUsername ?? '@${mod.moderator.name}'),
onTap: () =>
goToUser.byId(context, mod.instanceHost, mod.moderator.id),
), ),
] ]
], ],
@ -536,10 +539,10 @@ class _FollowButton extends HookWidget {
subscribe(Jwt token) async { subscribe(Jwt token) async {
delayed.start(); delayed.start();
try { try {
await LemmyApi(community.instanceHost).v1.followCommunity( await LemmyApiV2(community.instanceHost).run(FollowCommunity(
communityId: community.id, communityId: community.community.id,
follow: !isSubbed.value, follow: !isSubbed.value,
auth: token.raw); auth: token.raw));
isSubbed.value = !isSubbed.value; isSubbed.value = !isSubbed.value;
// ignore: avoid_catches_without_on_clauses // ignore: avoid_catches_without_on_clauses
} catch (e) { } catch (e) {

View File

@ -2,7 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/pictrs.dart';
import 'package:lemmy_api_client/v2.dart';
import '../hooks/delayed_loading.dart'; import '../hooks/delayed_loading.dart';
import '../hooks/image_picker.dart'; import '../hooks/image_picker.dart';
@ -26,20 +27,20 @@ class CreatePostFab extends HookWidget {
return FloatingActionButton( return FloatingActionButton(
onPressed: loggedInAction((_) => showCupertinoModalPopup( onPressed: loggedInAction((_) => showCupertinoModalPopup(
context: context, builder: (_) => CreatePost())), context: context, builder: (_) => CreatePostPage())),
child: const Icon(Icons.add), child: const Icon(Icons.add),
); );
} }
} }
/// Modal for creating a post to some community in some instance /// Modal for creating a post to some community in some instance
class CreatePost extends HookWidget { class CreatePostPage extends HookWidget {
final CommunityView community; final CommunityView community;
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey(); final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
CreatePost() : community = null; CreatePostPage() : community = null;
CreatePost.toCommunity(this.community); CreatePostPage.toCommunity(this.community);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -58,16 +59,15 @@ class CreatePost extends HookWidget {
final pictrsDeleteToken = useState<PictrsUploadFile>(null); final pictrsDeleteToken = useState<PictrsUploadFile>(null);
final allCommunitiesSnap = useMemoFuture( final allCommunitiesSnap = useMemoFuture(
() => LemmyApi(selectedInstance.value) () => LemmyApiV2(selectedInstance.value)
.v1 .run(ListCommunities(
.listCommunities( sort: SortType.hot,
sort: SortType.hot, limit: 9999,
limit: 9999, auth: accStore.defaultTokenFor(selectedInstance.value).raw,
auth: accStore.defaultTokenFor(selectedInstance.value).raw, ))
)
.then( .then(
(value) { (value) {
value.sort((a, b) => a.name.compareTo(b.name)); value.sort((a, b) => a.community.name.compareTo(b.community.name));
return value; return value;
}, },
), ),
@ -82,7 +82,7 @@ class CreatePost extends HookWidget {
imageUploadLoading.value = true; imageUploadLoading.value = true;
final token = accStore.defaultTokenFor(selectedInstance.value); final token = accStore.defaultTokenFor(selectedInstance.value);
final pictrs = LemmyApi(selectedInstance.value).pictrs; final pictrs = PictrsApi(selectedInstance.value);
final upload = final upload =
await pictrs.upload(filePath: pic.path, auth: token.raw); await pictrs.upload(filePath: pic.path, auth: token.raw);
pictrsDeleteToken.value = upload.files[0]; pictrsDeleteToken.value = upload.files[0];
@ -100,8 +100,7 @@ class CreatePost extends HookWidget {
} }
removePicture() { removePicture() {
LemmyApi(selectedInstance.value) PictrsApi(selectedInstance.value)
.pictrs
.delete(pictrsDeleteToken.value) .delete(pictrsDeleteToken.value)
.catchError((_) {}); .catchError((_) {});
@ -135,15 +134,16 @@ class CreatePost extends HookWidget {
border: OutlineInputBorder()), border: OutlineInputBorder()),
child: DropdownButtonHideUnderline( child: DropdownButtonHideUnderline(
child: DropdownButton<String>( child: DropdownButton<String>(
value: selectedCommunity.value?.name, value: selectedCommunity.value?.community?.name,
hint: const Text('Community'), hint: const Text('Community'),
onChanged: (val) => selectedCommunity.value = onChanged: (val) => selectedCommunity.value = allCommunitiesSnap.data
allCommunitiesSnap.data.firstWhere((e) => e.name == val), .firstWhere((e) => e.community.name == val),
items: allCommunitiesSnap.hasData items: allCommunitiesSnap.hasData
? allCommunitiesSnap.data ? allCommunitiesSnap.data
// FIXME: use id instead of name cuz it breaks with federation
.map((e) => DropdownMenuItem( .map((e) => DropdownMenuItem(
value: e.name, value: e.community.name,
child: Text(e.name), child: Text(e.community.name),
)) ))
.toList() .toList()
: const [ : const [
@ -219,19 +219,20 @@ class CreatePost extends HookWidget {
return; return;
} }
final api = LemmyApi(selectedInstance.value).v1; final api = LemmyApiV2(selectedInstance.value);
final token = accStore.defaultTokenFor(selectedInstance.value); final token = accStore.defaultTokenFor(selectedInstance.value);
delayed.start(); delayed.start();
try { try {
final res = await api.createPost( final res = await api.run(CreatePost(
url: urlController.text.isEmpty ? null : urlController.text, url: urlController.text.isEmpty ? null : urlController.text,
body: bodyController.text.isEmpty ? null : bodyController.text, body: bodyController.text.isEmpty ? null : bodyController.text,
nsfw: nsfw.value, nsfw: nsfw.value,
name: titleController.text, name: titleController.text,
communityId: selectedCommunity.value.id, communityId: selectedCommunity.value.community.id,
auth: token.raw); auth: token.raw,
));
unawaited(goToReplace(context, (_) => FullPostPage.fromPostView(res))); unawaited(goToReplace(context, (_) => FullPostPage.fromPostView(res)));
return; return;
// ignore: avoid_catches_without_on_clauses // ignore: avoid_catches_without_on_clauses

View File

@ -3,7 +3,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../hooks/logged_in_action.dart'; import '../hooks/logged_in_action.dart';
import '../hooks/refreshable.dart'; import '../hooks/refreshable.dart';
@ -25,15 +25,17 @@ class FullPostPage extends HookWidget {
assert(instanceHost != null), assert(instanceHost != null),
post = null; post = null;
FullPostPage.fromPostView(this.post) FullPostPage.fromPostView(this.post)
: id = post.id, : id = post.post.id,
instanceHost = post.instanceHost; instanceHost = post.instanceHost;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final accStore = useAccountsStore(); final accStore = useAccountsStore();
final fullPostRefreshable = useRefreshable(() => LemmyApi(instanceHost) final fullPostRefreshable =
.v1 useRefreshable(() => LemmyApiV2(instanceHost).run(GetPost(
.getPost(id: id, auth: accStore.defaultTokenFor(instanceHost)?.raw)); id: id,
auth: accStore.defaultTokenFor(instanceHost)?.raw,
)));
final loggedInAction = useLoggedInAction(instanceHost); final loggedInAction = useLoggedInAction(instanceHost);
final newComments = useState(const <CommentView>[]); final newComments = useState(const <CommentView>[]);
@ -58,7 +60,7 @@ class FullPostPage extends HookWidget {
// VARIABLES // VARIABLES
final post = fullPostRefreshable.snapshot.hasData final post = fullPostRefreshable.snapshot.hasData
? fullPostRefreshable.snapshot.data.post ? fullPostRefreshable.snapshot.data.postView
: this.post; : this.post;
final fullPost = fullPostRefreshable.snapshot.data; final fullPost = fullPostRefreshable.snapshot.data;
@ -78,7 +80,7 @@ class FullPostPage extends HookWidget {
} }
} }
sharePost() => Share.text('Share post', post.apId, 'text/plain'); sharePost() => Share.text('Share post', post.post.apId, 'text/plain');
comment() async { comment() async {
final newComment = await showCupertinoModalPopup<CommentView>( final newComment = await showCupertinoModalPopup<CommentView>(
@ -98,7 +100,7 @@ class FullPostPage extends HookWidget {
SavePostButton(post), SavePostButton(post),
IconButton( IconButton(
icon: Icon(moreIcon), icon: Icon(moreIcon),
onPressed: () => Post.showMoreMenu(context, post)), onPressed: () => PostWidget.showMoreMenu(context, post)),
], ],
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
@ -109,11 +111,11 @@ class FullPostPage extends HookWidget {
child: ListView( child: ListView(
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
children: [ children: [
Post(post, fullPost: true), PostWidget(post, fullPost: true),
if (fullPostRefreshable.snapshot.hasData) if (fullPostRefreshable.snapshot.hasData)
CommentSection( CommentSection(
newComments.value.followedBy(fullPost.comments).toList(), newComments.value.followedBy(fullPost.comments).toList(),
postCreatorId: fullPost.post.creatorId) postCreatorId: fullPost.postView.creator.id)
else if (fullPostRefreshable.snapshot.hasError) else if (fullPostRefreshable.snapshot.hasError)
Padding( Padding(
padding: padding:

View File

@ -4,7 +4,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../hooks/infinite_scroll.dart'; import '../hooks/infinite_scroll.dart';
import '../hooks/memo_future.dart'; import '../hooks/memo_future.dart';
@ -35,10 +35,11 @@ class HomeTab extends HookWidget {
final instancesIcons = useMemoFuture(() async { final instancesIcons = useMemoFuture(() async {
final instances = accStore.instances.toList(growable: false); final instances = accStore.instances.toList(growable: false);
final sites = await Future.wait(instances final sites = await Future.wait(instances
.map((e) => LemmyApi(e).v1.getSite().catchError((e) => null))); .map((e) => LemmyApiV2(e).run(GetSite()).catchError((e) => null)));
return { return {
for (var i = 0; i < sites.length; i++) instances[i]: sites[i].site.icon for (var i = 0; i < sites.length; i++)
instances[i]: sites[i].siteView.site.icon
}; };
}); });
@ -262,13 +263,13 @@ class InfiniteHomeList extends HookWidget {
}(); }();
final futures = final futures =
instances.map((instanceHost) => LemmyApi(instanceHost).v1.getPosts( instances.map((instanceHost) => LemmyApiV2(instanceHost).run(GetPosts(
type: listingType, type: listingType,
sort: sort, sort: sort,
page: page, page: page,
limit: limit, limit: limit,
auth: accStore.defaultTokenFor(instanceHost)?.raw, auth: accStore.defaultTokenFor(instanceHost)?.raw,
)); )));
final posts = await Future.wait(futures); final posts = await Future.wait(futures);
final newPosts = <PostView>[]; final newPosts = <PostView>[];
final longest = posts.map((e) => e.length).reduce(max); final longest = posts.map((e) => e.length).reduce(max);
@ -286,13 +287,13 @@ class InfiniteHomeList extends HookWidget {
Future<List<PostView>> Function(int, int) fetcherFromInstance( Future<List<PostView>> Function(int, int) fetcherFromInstance(
String instanceHost, PostListingType listingType, SortType sort) => String instanceHost, PostListingType listingType, SortType sort) =>
(page, batchSize) => LemmyApi(instanceHost).v1.getPosts( (page, batchSize) => LemmyApiV2(instanceHost).run(GetPosts(
type: listingType, type: listingType,
sort: sort, sort: sort,
page: page, page: page,
limit: batchSize, limit: batchSize,
auth: accStore.defaultTokenFor(instanceHost)?.raw, auth: accStore.defaultTokenFor(instanceHost)?.raw,
); ));
return InfiniteScroll<PostView>( return InfiniteScroll<PostView>(
prepend: Column( prepend: Column(
@ -305,7 +306,7 @@ class InfiniteHomeList extends HookWidget {
), ),
builder: (post) => Column( builder: (post) => Column(
children: [ children: [
Post(post), PostWidget(post),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],
), ),

View File

@ -3,7 +3,7 @@ import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'package:url_launcher/url_launcher.dart' as ul; import 'package:url_launcher/url_launcher.dart' as ul;
import '../hooks/stores.dart'; import '../hooks/stores.dart';
@ -30,9 +30,9 @@ class InstancePage extends HookWidget {
InstancePage({@required this.instanceHost}) InstancePage({@required this.instanceHost})
: assert(instanceHost != null), : assert(instanceHost != null),
siteFuture = LemmyApi(instanceHost).v1.getSite(), siteFuture = LemmyApiV2(instanceHost).run(GetSite()),
communitiesFuture = communitiesFuture =
LemmyApi(instanceHost).v1.listCommunities(sort: SortType.hot); LemmyApiV2(instanceHost).run(ListCommunities(sort: SortType.hot));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -80,8 +80,8 @@ class InstancePage extends HookWidget {
leading: const Icon(Icons.open_in_browser), leading: const Icon(Icons.open_in_browser),
title: const Text('Open in browser'), title: const Text('Open in browser'),
onTap: () async => await ul onTap: () async => await ul
.canLaunch('https://${site.site.instanceHost}') .canLaunch('https://${site.instanceHost}')
? ul.launch('https://${site.site.instanceHost}') ? ul.launch('https://${site.instanceHost}')
: Scaffold.of(context).showSnackBar( : Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("can't open in browser"))), const SnackBar(content: Text("can't open in browser"))),
), ),
@ -91,12 +91,12 @@ class InstancePage extends HookWidget {
onTap: () { onTap: () {
showInfoTablePopup(context, { showInfoTablePopup(context, {
'url': instanceHost, 'url': instanceHost,
'creator': '@${site.site.creatorName}', 'creator': '@${site.siteView.creator.name}',
'version': site.version, 'version': site.version,
'enableDownvotes': site.site.enableDownvotes, 'enableDownvotes': site.siteView.site.enableDownvotes,
'enableNsfw': site.site.enableNsfw, 'enableNsfw': site.siteView.site.enableNsfw,
'published': site.site.published, 'published': site.siteView.site.published,
'updated': site.site.updated, 'updated': site.siteView.site.updated,
}); });
}, },
), ),
@ -119,7 +119,7 @@ class InstancePage extends HookWidget {
backgroundColor: theme.cardColor, backgroundColor: theme.cardColor,
iconTheme: theme.iconTheme, iconTheme: theme.iconTheme,
title: Text( title: Text(
site.site.name, site.siteView.site.name,
style: TextStyle(color: colorOnCard), style: TextStyle(color: colorOnCard),
), ),
actions: [ actions: [
@ -130,11 +130,11 @@ class InstancePage extends HookWidget {
], ],
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
background: Stack(children: [ background: Stack(children: [
if (site.site.banner != null) if (site.siteView.site.banner != null)
FullscreenableImage( FullscreenableImage(
url: site.site.banner, url: site.siteView.site.banner,
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: site.site.banner, imageUrl: site.siteView.site.banner,
errorWidget: (_, __, ___) => const SizedBox.shrink(), errorWidget: (_, __, ___) => const SizedBox.shrink(),
), ),
), ),
@ -144,20 +144,20 @@ class InstancePage extends HookWidget {
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(top: 40), padding: const EdgeInsets.only(top: 40),
child: site.site.icon == null child: site.siteView.site.icon == null
? const SizedBox(height: 100, width: 100) ? const SizedBox(height: 100, width: 100)
: FullscreenableImage( : FullscreenableImage(
url: site.site.icon, url: site.siteView.site.icon,
child: CachedNetworkImage( child: CachedNetworkImage(
width: 100, width: 100,
height: 100, height: 100,
imageUrl: site.site.icon, imageUrl: site.siteView.site.icon,
errorWidget: (_, __, ___) => errorWidget: (_, __, ___) =>
const Icon(Icons.warning), const Icon(Icons.warning),
), ),
), ),
), ),
Text(site.site.name, Text(site.siteView.site.name,
style: theme.textTheme.headline6), style: theme.textTheme.headline6),
Text(instanceHost, style: theme.textTheme.caption) Text(instanceHost, style: theme.textTheme.caption)
], ],
@ -186,23 +186,23 @@ class InstancePage extends HookWidget {
children: [ children: [
InfinitePostList( InfinitePostList(
fetcher: (page, batchSize, sort) => fetcher: (page, batchSize, sort) =>
LemmyApi(instanceHost).v1.getPosts( LemmyApiV2(instanceHost).run(GetPosts(
// TODO: switch between all and subscribed // TODO: switch between all and subscribed
type: PostListingType.all, type: PostListingType.all,
sort: sort, sort: sort,
limit: batchSize, limit: batchSize,
page: page, page: page,
auth: accStore.defaultTokenFor(instanceHost)?.raw, auth: accStore.defaultTokenFor(instanceHost)?.raw,
)), ))),
InfiniteCommentList( InfiniteCommentList(
fetcher: (page, batchSize, sort) => fetcher: (page, batchSize, sort) =>
LemmyApi(instanceHost).v1.getComments( LemmyApiV2(instanceHost).run(GetComments(
type: CommentListingType.all, type: CommentListingType.all,
sort: sort, sort: sort,
limit: batchSize, limit: batchSize,
page: page, page: page,
auth: accStore.defaultTokenFor(instanceHost)?.raw, auth: accStore.defaultTokenFor(instanceHost)?.raw,
)), ))),
_AboutTab(site, _AboutTab(site,
communitiesFuture: communitiesFuture, communitiesFuture: communitiesFuture,
instanceHost: instanceHost), instanceHost: instanceHost),
@ -268,13 +268,13 @@ class _AboutTab extends HookWidget {
context, context,
(_) => CommunitiesListPage( (_) => CommunitiesListPage(
fetcher: (page, batchSize, sortType) => fetcher: (page, batchSize, sortType) =>
LemmyApi(instanceHost).v1.listCommunities( LemmyApiV2(instanceHost).run(ListCommunities(
sort: sortType, sort: sortType,
limit: batchSize, limit: batchSize,
page: page, page: page,
auth: accStore.defaultTokenFor(instanceHost)?.raw, auth: accStore.defaultTokenFor(instanceHost)?.raw,
), )),
title: 'Communities of ${site.site.name}', title: 'Communities of ${site.siteView.site.name}',
), ),
); );
} }
@ -287,7 +287,7 @@ class _AboutTab extends HookWidget {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
child: MarkdownText( child: MarkdownText(
site.site.description, site.siteView.site.description,
instanceHost: instanceHost, instanceHost: instanceHost,
), ),
), ),
@ -299,10 +299,10 @@ class _AboutTab extends HookWidget {
children: [ children: [
const SizedBox(width: 7), const SizedBox(width: 7),
const _Badge('X users online'), const _Badge('X users online'),
_Badge('${site.site.numberOfUsers} users'), _Badge('${site.siteView.counts.users} users'),
_Badge('${site.site.numberOfCommunities} communities'), _Badge('${site.siteView.counts.communities} communities'),
_Badge('${site.site.numberOfPosts} posts'), _Badge('${site.siteView.counts.posts} posts'),
_Badge('${site.site.numberOfComments} comments'), _Badge('${site.siteView.counts.comments} comments'),
const SizedBox(width: 15), const SizedBox(width: 15),
], ],
), ),
@ -317,15 +317,15 @@ class _AboutTab extends HookWidget {
), ),
), ),
if (commSnap.hasData) if (commSnap.hasData)
...commSnap.data.take(6).map((e) => ListTile( ...commSnap.data.take(6).map((c) => ListTile(
onTap: () => onTap: () => goToCommunity.byId(
goToCommunity.byId(context, e.instanceHost, e.id), context, c.instanceHost, c.community.id),
title: Text(e.name), title: Text(c.community.name),
leading: e.icon != null leading: c.community.icon != null
? CachedNetworkImage( ? CachedNetworkImage(
height: 50, height: 50,
width: 50, width: 50,
imageUrl: e.icon, imageUrl: c.community.icon,
errorWidget: (_, __, ___) => errorWidget: (_, __, ___) =>
const SizedBox(width: 50, height: 50), const SizedBox(width: 50, height: 50),
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
@ -362,20 +362,20 @@ class _AboutTab extends HookWidget {
), ),
), ),
), ),
...site.admins.map((e) => ListTile( ...site.admins.map((u) => ListTile(
title: Text((e.preferredUsername == null || title: Text((u.user.preferredUsername == null ||
e.preferredUsername.isEmpty) u.user.preferredUsername.isEmpty)
? '@${e.name}' ? '@${u.user.name}'
: e.preferredUsername), : u.user.preferredUsername),
subtitle: e.bio != null subtitle: u.user.bio != null
? MarkdownText(e.bio, instanceHost: instanceHost) ? MarkdownText(u.user.bio, instanceHost: instanceHost)
: null, : null,
onTap: () => goToUser.byId(context, instanceHost, e.id), onTap: () => goToUser.byId(context, instanceHost, u.user.id),
leading: e.avatar != null leading: u.user.avatar != null
? CachedNetworkImage( ? CachedNetworkImage(
height: 50, height: 50,
width: 50, width: 50,
imageUrl: e.avatar, imageUrl: u.user.avatar,
errorWidget: (_, __, ___) => errorWidget: (_, __, ___) =>
const SizedBox(width: 50, height: 50), const SizedBox(width: 50, height: 50),
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(

View File

@ -2,7 +2,8 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/pictrs.dart';
import 'package:lemmy_api_client/v2.dart';
import '../hooks/delayed_loading.dart'; import '../hooks/delayed_loading.dart';
import '../hooks/image_picker.dart'; import '../hooks/image_picker.dart';
@ -28,9 +29,8 @@ class ManageAccountPage extends HookWidget {
final theme = Theme.of(context); final theme = Theme.of(context);
final userFuture = useMemoized(() async { final userFuture = useMemoized(() async {
final site = await LemmyApi(instanceHost) final site = await LemmyApiV2(instanceHost).run(
.v1 GetSite(auth: accountStore.tokenFor(instanceHost, username).raw));
.getSite(auth: accountStore.tokenFor(instanceHost, username).raw);
return site.myUser; return site.myUser;
}); });
@ -45,7 +45,7 @@ class ManageAccountPage extends HookWidget {
Text('@$instanceHost@$username', style: theme.textTheme.headline6), Text('@$instanceHost@$username', style: theme.textTheme.headline6),
centerTitle: true, centerTitle: true,
), ),
body: FutureBuilder<User>( body: FutureBuilder<UserSafeSettings>(
future: userFuture, future: userFuture,
builder: (_, userSnap) { builder: (_, userSnap) {
if (userSnap.hasError) { if (userSnap.hasError) {
@ -67,7 +67,7 @@ class _ManageAccount extends HookWidget {
: assert(user != null), : assert(user != null),
super(key: key); super(key: key);
final User user; final UserSafeSettings user;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -104,35 +104,35 @@ class _ManageAccount extends HookWidget {
saveDelayedLoading.start(); saveDelayedLoading.start();
try { try {
await LemmyApi(user.instanceHost).v1.saveUserSettings( await LemmyApiV2(user.instanceHost).run(SaveUserSettings(
showNsfw: showNsfw.value, showNsfw: showNsfw.value,
theme: user.theme, theme: user.theme,
defaultSortType: defaultSortType.value, defaultSortType: defaultSortType.value,
defaultListingType: defaultListingType.value, defaultListingType: defaultListingType.value,
lang: user.lang, lang: user.lang,
showAvatars: showAvatars.value, showAvatars: showAvatars.value,
sendNotificationsToEmail: sendNotificationsToEmail.value, sendNotificationsToEmail: sendNotificationsToEmail.value,
auth: token.raw, auth: token.raw,
avatar: avatar.current, avatar: avatar.current,
banner: banner.current, banner: banner.current,
newPassword: newPasswordController.text.isEmpty newPassword: newPasswordController.text.isEmpty
? null ? null
: newPasswordController.text, : newPasswordController.text,
newPasswordVerify: newPasswordVerifyController.text.isEmpty newPasswordVerify: newPasswordVerifyController.text.isEmpty
? null ? null
: newPasswordVerifyController.text, : newPasswordVerifyController.text,
oldPassword: oldPasswordController.text.isEmpty oldPassword: oldPasswordController.text.isEmpty
? null ? null
: oldPasswordController.text, : oldPasswordController.text,
matrixUserId: matrixUserController.text.isEmpty matrixUserId: matrixUserController.text.isEmpty
? null ? null
: matrixUserController.text, : matrixUserController.text,
preferredUsername: displayNameController.text.isEmpty preferredUsername: displayNameController.text.isEmpty
? null ? null
: displayNameController.text, : displayNameController.text,
bio: bioController.text.isEmpty ? null : bioController.text, bio: bioController.text.isEmpty ? null : bioController.text,
email: emailController.text.isEmpty ? null : emailController.text, email: emailController.text.isEmpty ? null : emailController.text,
); ));
informAcceptedAvatarRef.current(); informAcceptedAvatarRef.current();
informAcceptedBannerRef.current(); informAcceptedBannerRef.current();
@ -186,10 +186,10 @@ class _ManageAccount extends HookWidget {
deleteDelayedLoading.start(); deleteDelayedLoading.start();
try { try {
await LemmyApi(user.instanceHost).v1.deleteAccount( await LemmyApiV2(user.instanceHost).run(DeleteAccount(
password: deleteAccountPasswordController.text, password: deleteAccountPasswordController.text,
auth: token.raw, auth: token.raw,
); ));
accountsStore.removeAccount(user.instanceHost, user.name); accountsStore.removeAccount(user.instanceHost, user.name);
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -439,7 +439,7 @@ class _ManageAccount extends HookWidget {
class _ImagePicker extends HookWidget { class _ImagePicker extends HookWidget {
final String name; final String name;
final String initialUrl; final String initialUrl;
final User user; final UserSafeSettings user;
final ValueChanged<String> onChange; final ValueChanged<String> onChange;
/// _ImagePicker will set the ref to a callback that can inform _ImagePicker /// _ImagePicker will set the ref to a callback that can inform _ImagePicker
@ -478,10 +478,10 @@ class _ImagePicker extends HookWidget {
if (pic != null) { if (pic != null) {
delayedLoading.start(); delayedLoading.start();
final upload = await LemmyApi(user.instanceHost).pictrs.upload( final upload = await PictrsApi(user.instanceHost).upload(
filePath: pic.path, filePath: pic.path,
auth: accountsStore.tokenFor(user.instanceHost, user.name).raw, auth: accountsStore.tokenFor(user.instanceHost, user.name).raw,
); );
pictrsDeleteToken.value = upload.files[0]; pictrsDeleteToken.value = upload.files[0];
url.value = url.value =
pathToPictrs(user.instanceHost, pictrsDeleteToken.value.file); pathToPictrs(user.instanceHost, pictrsDeleteToken.value.file);
@ -497,8 +497,7 @@ class _ImagePicker extends HookWidget {
} }
removePicture({bool updateState = true}) { removePicture({bool updateState = true}) {
LemmyApi(user.instanceHost) PictrsApi(user.instanceHost)
.pictrs
.delete(pictrsDeleteToken.value) .delete(pictrsDeleteToken.value)
.catchError((_) {}); .catchError((_) {});

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../comment_tree.dart'; import '../comment_tree.dart';
import '../hooks/stores.dart'; import '../hooks/stores.dart';
@ -82,14 +82,14 @@ class _SearchResultsList extends HookWidget {
return SortableInfiniteList( return SortableInfiniteList(
fetcher: (page, batchSize, sort) async { fetcher: (page, batchSize, sort) async {
final s = await LemmyApi(instanceHost).v1.search( final s = await LemmyApiV2(instanceHost).run(Search(
q: query, q: query,
sort: sort, sort: sort,
type: type, type: type,
auth: accStore.defaultTokenFor(instanceHost)?.raw, auth: accStore.defaultTokenFor(instanceHost)?.raw,
page: page, page: page,
limit: batchSize, limit: batchSize,
); ));
switch (s.type) { switch (s.type) {
case SearchType.comments: case SearchType.comments:
@ -107,7 +107,7 @@ class _SearchResultsList extends HookWidget {
builder: (data) { builder: (data) {
switch (type) { switch (type) {
case SearchType.comments: case SearchType.comments:
return Comment( return CommentWidget(
CommentTree(data as CommentView), CommentTree(data as CommentView),
postCreatorId: null, postCreatorId: null,
); );
@ -116,10 +116,10 @@ class _SearchResultsList extends HookWidget {
case SearchType.posts: case SearchType.posts:
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.only(bottom: 20),
child: Post(data as PostView), child: PostWidget(data as PostView),
); );
case SearchType.users: case SearchType.users:
return UsersListItem(user: data as UserView); return UsersListItem(user: data as UserViewSafe);
default: default:
throw UnimplementedError(); throw UnimplementedError();
} }

View File

@ -1,7 +1,7 @@
import 'package:esys_flutter_share/esys_flutter_share.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../widgets/user_profile.dart'; import '../widgets/user_profile.dart';
@ -9,20 +9,20 @@ import '../widgets/user_profile.dart';
class UserPage extends HookWidget { class UserPage extends HookWidget {
final int userId; final int userId;
final String instanceHost; final String instanceHost;
final Future<UserDetails> _userDetails; final Future<FullUserView> _userDetails;
UserPage({@required this.userId, @required this.instanceHost}) UserPage({@required this.userId, @required this.instanceHost})
: assert(userId != null), : assert(userId != null),
assert(instanceHost != null), assert(instanceHost != null),
_userDetails = LemmyApi(instanceHost).v1.getUserDetails( _userDetails = LemmyApiV2(instanceHost).run(GetUserDetails(
userId: userId, savedOnly: true, sort: SortType.active); userId: userId, savedOnly: true, sort: SortType.active));
UserPage.fromName({@required this.instanceHost, @required String username}) UserPage.fromName({@required this.instanceHost, @required String username})
: assert(instanceHost != null), : assert(instanceHost != null),
assert(username != null), assert(username != null),
userId = null, userId = null,
_userDetails = LemmyApi(instanceHost).v1.getUserDetails( _userDetails = LemmyApiV2(instanceHost).run(GetUserDetails(
username: username, savedOnly: true, sort: SortType.active); username: username, savedOnly: true, sort: SortType.active));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -52,7 +52,7 @@ class UserPage extends HookWidget {
IconButton( IconButton(
icon: const Icon(Icons.share), icon: const Icon(Icons.share),
onPressed: () => Share.text('Share user', onPressed: () => Share.text('Share user',
userDetailsSnap.data.user.actorId, 'text/plain'), userDetailsSnap.data.userView.user.actorId, 'text/plain'),
) )
] ]
], ],

View File

@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../util/goto.dart'; import '../util/goto.dart';
import '../widgets/markdown_text.dart'; import '../widgets/markdown_text.dart';
@ -8,7 +8,7 @@ import '../widgets/markdown_text.dart';
/// Infinite list of Users fetched by the given fetcher /// Infinite list of Users fetched by the given fetcher
class UsersListPage extends StatelessWidget { class UsersListPage extends StatelessWidget {
final String title; final String title;
final List<UserView> users; final List<UserViewSafe> users;
const UsersListPage({Key key, @required this.users, this.title}) const UsersListPage({Key key, @required this.users, this.title})
: assert(users != null), : assert(users != null),
@ -34,7 +34,7 @@ class UsersListPage extends StatelessWidget {
} }
class UsersListItem extends StatelessWidget { class UsersListItem extends StatelessWidget {
final UserView user; final UserViewSafe user;
const UsersListItem({Key key, @required this.user}) const UsersListItem({Key key, @required this.user})
: assert(user != null), : assert(user != null),
@ -42,25 +42,25 @@ class UsersListItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) => ListTile( Widget build(BuildContext context) => ListTile(
title: Text( title: Text((user.user.preferredUsername == null ||
(user.preferredUsername == null || user.preferredUsername.isEmpty) user.user.preferredUsername.isEmpty)
? '@${user.name}' ? '@${user.user.name}'
: user.preferredUsername), : user.user.preferredUsername),
subtitle: user.bio != null subtitle: user.user.bio != null
? Opacity( ? Opacity(
opacity: 0.5, opacity: 0.5,
child: MarkdownText( child: MarkdownText(
user.bio, user.user.bio,
instanceHost: user.instanceHost, instanceHost: user.instanceHost,
), ),
) )
: null, : null,
onTap: () => goToUser.byId(context, user.instanceHost, user.id), onTap: () => goToUser.byId(context, user.instanceHost, user.user.id),
leading: user.avatar != null leading: user.user.avatar != null
? CachedNetworkImage( ? CachedNetworkImage(
height: 50, height: 50,
width: 50, width: 50,
imageUrl: user.avatar, imageUrl: user.user.avatar,
errorWidget: (_, __, ___) => errorWidget: (_, __, ___) =>
const SizedBox(height: 50, width: 50), const SizedBox(height: 50, width: 50),
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(

View File

@ -2,7 +2,7 @@ import 'dart:collection';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../util/unawaited.dart'; import '../util/unawaited.dart';
@ -215,14 +215,15 @@ class AccountsStore extends ChangeNotifier {
throw Exception('No such instance was added'); throw Exception('No such instance was added');
} }
final lemmy = LemmyApi(instanceHost).v1; final lemmy = LemmyApiV2(instanceHost);
final token = await lemmy.run(Login(
final token = await lemmy.login(
usernameOrEmail: usernameOrEmail, usernameOrEmail: usernameOrEmail,
password: password, password: password,
); ));
final userData = // lemmy.login();
await lemmy.getSite(auth: token.raw).then((value) => value.myUser); final userData = await lemmy
.run(GetSite(auth: token.raw))
.then((value) => value.myUser); // TODO: change Mser to User
_tokens[instanceHost][userData.name] = token; _tokens[instanceHost][userData.name] = token;
@ -244,7 +245,7 @@ class AccountsStore extends ChangeNotifier {
if (!assumeValid) { if (!assumeValid) {
try { try {
await LemmyApi(instanceHost).v1.getSite(); await LemmyApiV2(instanceHost).run(GetSite());
// ignore: avoid_catches_without_on_clauses // ignore: avoid_catches_without_on_clauses
} catch (_) { } catch (_) {
throw Exception('This instance seems to not exist'); throw Exception('This instance seems to not exist');

View File

@ -1,4 +1,4 @@
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../cleanup_url.dart'; import '../cleanup_url.dart';
@ -11,28 +11,28 @@ import '../cleanup_url.dart';
// [.isLocal] is true iff `.originInstanceHost == .instanceHost` // [.isLocal] is true iff `.originInstanceHost == .instanceHost`
extension GetInstanceCommunityView on CommunityView { extension GetInstanceCommunityView on CommunityView {
String get originInstanceHost => _extract(actorId); String get originInstanceHost => _extract(community.actorId);
bool get isLocal => originInstanceHost == instanceHost; // bool get isLocal => originInstanceHost == instanceHost;
} }
extension GetInstanceUserView on UserView { extension GetInstanceUserView on UserViewSafe {
String get originInstanceHost => _extract(actorId); String get originInstanceHost => _extract(user.actorId);
bool get isLocal => originInstanceHost == instanceHost; // bool get isLocal => originInstanceHost == instanceHost;
} }
extension GetInstanceCommunityFollowerView on CommunityFollowerView { extension GetInstanceCommunityFollowerView on CommunityFollowerView {
String get originInstanceHost => _extract(communityActorId); String get originInstanceHost => _extract(community.actorId);
bool get isLocal => originInstanceHost == instanceHost; // bool get isLocal => originInstanceHost == instanceHost;
} }
extension GetInstancePostView on PostView { extension GetInstancePostView on PostView {
String get originInstanceHost => _extract(apId); String get originInstanceHost => _extract(post.apId);
bool get isLocal => originInstanceHost == instanceHost; // bool get isLocal => originInstanceHost == instanceHost;
} }
extension GetInstanceCommentView on CommentView { extension GetInstanceCommentView on CommentView {
String get originInstanceHost => _extract(apId); String get originInstanceHost => _extract(comment.apId);
bool get isLocal => originInstanceHost == instanceHost; // bool get isLocal => originInstanceHost == instanceHost;
} }
// TODO: change it to something more robust? regex? // TODO: change it to something more robust? regex?

View File

@ -1,6 +1,6 @@
import 'dart:math' show log, max, pow, ln10; import 'dart:math' show log, max, pow, ln10;
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
/// Calculates hot rank /// Calculates hot rank
/// because API always claims it's `0` /// because API always claims it's `0`
@ -17,5 +17,6 @@ double _calculateHotRank(int score, DateTime time) {
} }
extension CommentHotRank on CommentView { extension CommentHotRank on CommentView {
double get computedHotRank => _calculateHotRank(score, published); double get computedHotRank =>
_calculateHotRank(counts.score, comment.published);
} }

View File

@ -1,7 +1,7 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../comment_tree.dart'; import '../comment_tree.dart';
import 'bottom_modal.dart'; import 'bottom_modal.dart';
@ -29,7 +29,7 @@ class CommentSection extends HookWidget {
}) : comments = }) : comments =
CommentTree.sortList(sortType, CommentTree.fromList(rawComments)), CommentTree.sortList(sortType, CommentTree.fromList(rawComments)),
rawComments = rawComments rawComments = rawComments
..sort((b, a) => a.published.compareTo(b.published)), ..sort((b, a) => a.comment.published.compareTo(b.comment.published)),
assert(postCreatorId != null); assert(postCreatorId != null);
@override @override
@ -99,12 +99,13 @@ class CommentSection extends HookWidget {
) )
else if (sorting.value == CommentSortType.chat) else if (sorting.value == CommentSortType.chat)
for (final com in rawComments) for (final com in rawComments)
Comment( CommentWidget(
CommentTree(com), CommentTree(com),
postCreatorId: postCreatorId, postCreatorId: postCreatorId,
) )
else else
for (final com in comments) Comment(com, postCreatorId: postCreatorId), for (final com in comments)
CommentWidget(com, postCreatorId: postCreatorId),
const SizedBox(height: 50), const SizedBox(height: 50),
]); ]);
} }

View File

@ -5,7 +5,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'package:timeago/timeago.dart' as timeago; import 'package:timeago/timeago.dart' as timeago;
import 'package:url_launcher/url_launcher.dart' as ul; import 'package:url_launcher/url_launcher.dart' as ul;
@ -47,12 +47,13 @@ MediaType whatType(String url) {
} }
/// A post overview card /// A post overview card
class Post extends HookWidget { class PostWidget extends HookWidget {
final PostView post; final PostView post;
final String instanceHost; final String instanceHost;
final bool fullPost; final bool fullPost;
Post(this.post, {this.fullPost = false}) : instanceHost = post.instanceHost; PostWidget(this.post, {this.fullPost = false})
: instanceHost = post.instanceHost;
// == ACTIONS == // == ACTIONS ==
@ -66,8 +67,8 @@ class Post extends HookWidget {
ListTile( ListTile(
leading: const Icon(Icons.open_in_browser), leading: const Icon(Icons.open_in_browser),
title: const Text('Open in browser'), title: const Text('Open in browser'),
onTap: () async => await ul.canLaunch(post.apId) onTap: () async => await ul.canLaunch(post.post.apId)
? ul.launch(post.apId) ? ul.launch(post.post.apId)
: Scaffold.of(context).showSnackBar( : Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("can't open in browser"))), const SnackBar(content: Text("can't open in browser"))),
), ),
@ -76,19 +77,16 @@ class Post extends HookWidget {
title: const Text('Nerd stuff'), title: const Text('Nerd stuff'),
onTap: () { onTap: () {
showInfoTablePopup(context, { showInfoTablePopup(context, {
'id': post.id, 'id': post.post.id,
'apId': post.apId, 'apId': post.post.apId,
'upvotes': post.upvotes, 'upvotes': post.counts.upvotes,
'downvotes': post.downvotes, 'downvotes': post.counts.downvotes,
'score': post.score, 'score': post.counts.score,
'% of upvotes': '% of upvotes':
'''${(100 * (post.upvotes / (post.upvotes + post.downvotes))).toInt()}%''', '''${(100 * (post.counts.upvotes / (post.counts.upvotes + post.counts.downvotes))).toInt()}%''',
'hotRank': post.hotRank, 'local': post.post.local,
'hotRank active': post.hotRankActive, 'published': post.post.published,
'local': post.local, 'updated': post.post.updated ?? 'never',
'published': post.published,
'updated': post.updated ?? 'never',
'newestActivityTime': post.newestActivityTime,
}); });
}, },
), ),
@ -104,12 +102,12 @@ class Post extends HookWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
void _openLink() => linkLauncher( void _openLink() => linkLauncher(
context: context, url: post.url, instanceHost: instanceHost); context: context, url: post.post.url, instanceHost: instanceHost);
final urlDomain = () { final urlDomain = () {
if (whatType(post.url) == MediaType.none) return null; if (whatType(post.post.url) == MediaType.none) return null;
final url = post.url.split('/')[2]; final url = post.post.url.split('/')[2]; // TODO: change to Url(str).host
if (url.startsWith('www.')) return url.substring(4); if (url.startsWith('www.')) return url.substring(4);
return url; return url;
}(); }();
@ -122,12 +120,12 @@ class Post extends HookWidget {
Column( Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (post.communityIcon != null) if (post.community.icon != null)
Padding( Padding(
padding: const EdgeInsets.only(right: 10), padding: const EdgeInsets.only(right: 10),
child: InkWell( child: InkWell(
onTap: () => goToCommunity.byId( onTap: () => goToCommunity.byId(
context, instanceHost, post.communityId), context, instanceHost, post.community.id),
child: SizedBox( child: SizedBox(
height: 40, height: 40,
width: 40, width: 40,
@ -141,7 +139,7 @@ class Post extends HookWidget {
), ),
), ),
), ),
imageUrl: post.communityIcon, imageUrl: post.community.icon,
errorWidget: (context, url, error) => errorWidget: (context, url, error) =>
Text(error.toString()), Text(error.toString()),
), ),
@ -165,12 +163,12 @@ class Post extends HookWidget {
text: '!', text: '!',
style: TextStyle(fontWeight: FontWeight.w300)), style: TextStyle(fontWeight: FontWeight.w300)),
TextSpan( TextSpan(
text: post.communityName, text: post.community.name,
style: style:
const TextStyle(fontWeight: FontWeight.w600), const TextStyle(fontWeight: FontWeight.w600),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () => goToCommunity.byId( ..onTap = () => goToCommunity.byId(
context, instanceHost, post.communityId)), context, instanceHost, post.community.id)),
const TextSpan( const TextSpan(
text: '@', text: '@',
style: TextStyle(fontWeight: FontWeight.w300)), style: TextStyle(fontWeight: FontWeight.w300)),
@ -198,28 +196,32 @@ class Post extends HookWidget {
style: TextStyle(fontWeight: FontWeight.w300)), style: TextStyle(fontWeight: FontWeight.w300)),
TextSpan( TextSpan(
text: text:
''' ${post.creatorPreferredUsername ?? post.creatorName}''', ''' ${post.creator.preferredUsername ?? post.creator.name}''',
style: style:
const TextStyle(fontWeight: FontWeight.w600), const TextStyle(fontWeight: FontWeight.w600),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () => goToUser.byId( ..onTap = () => goToUser.byId(
context, post.instanceHost, post.creatorId), context,
post.instanceHost,
post.creator.id,
),
), ),
TextSpan( TextSpan(
text: text:
''' · ${timeago.format(post.published, locale: 'en_short')}'''), ''' · ${timeago.format(post.post.published, locale: 'en_short')}'''),
if (post.locked) const TextSpan(text: ' · 🔒'), if (post.post.locked) const TextSpan(text: ' · 🔒'),
if (post.stickied) const TextSpan(text: ' · 📌'), if (post.post.stickied)
if (post.nsfw) const TextSpan(text: ' · '), const TextSpan(text: ' · 📌'),
if (post.nsfw) if (post.post.nsfw) const TextSpan(text: ' · '),
if (post.post.nsfw)
const TextSpan( const TextSpan(
text: 'NSFW', text: 'NSFW',
style: TextStyle(color: Colors.red)), style: TextStyle(color: Colors.red)),
if (urlDomain != null) if (urlDomain != null)
TextSpan(text: ' · $urlDomain'), TextSpan(text: ' · $urlDomain'),
if (post.removed) if (post.post.removed)
const TextSpan(text: ' · REMOVED'), const TextSpan(text: ' · REMOVED'),
if (post.deleted) if (post.post.deleted)
const TextSpan(text: ' · DELETED'), const TextSpan(text: ' · DELETED'),
], ],
)) ))
@ -250,15 +252,15 @@ class Post extends HookWidget {
Expanded( Expanded(
flex: 100, flex: 100,
child: Text( child: Text(
post.name, post.post.name,
textAlign: TextAlign.left, textAlign: TextAlign.left,
softWrap: true, softWrap: true,
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w600), fontSize: 18, fontWeight: FontWeight.w600),
), ),
), ),
if (whatType(post.url) == MediaType.other && if (whatType(post.post.url) == MediaType.other &&
post.thumbnailUrl != null) ...[ post.post.thumbnailUrl != null) ...[
const Spacer(), const Spacer(),
InkWell( InkWell(
onTap: _openLink, onTap: _openLink,
@ -266,7 +268,7 @@ class Post extends HookWidget {
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: post.thumbnailUrl, imageUrl: post.post.thumbnailUrl,
width: 70, width: 70,
height: 70, height: 70,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -291,7 +293,7 @@ class Post extends HookWidget {
/// assemble link preview /// assemble link preview
Widget linkPreview() { Widget linkPreview() {
assert(post.url != null); assert(post.post.url != null);
return Padding( return Padding(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
@ -315,14 +317,14 @@ class Post extends HookWidget {
]), ]),
Row(children: [ Row(children: [
Flexible( Flexible(
child: Text(post.embedTitle ?? '', child: Text(post.post.embedTitle ?? '',
style: theme.textTheme.subtitle1 style: theme.textTheme.subtitle1
.apply(fontWeightDelta: 2))) .apply(fontWeightDelta: 2)))
]), ]),
if (post.embedDescription != null && if (post.post.embedDescription != null &&
post.embedDescription.isNotEmpty) post.post.embedDescription.isNotEmpty)
Row(children: [ Row(children: [
Flexible(child: Text(post.embedDescription)) Flexible(child: Text(post.post.embedDescription))
]), ]),
], ],
), ),
@ -334,12 +336,12 @@ class Post extends HookWidget {
/// assemble image /// assemble image
Widget postImage() { Widget postImage() {
assert(post.url != null); assert(post.post.url != null);
return FullscreenableImage( return FullscreenableImage(
url: post.url, url: post.post.url,
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: post.url, imageUrl: post.post.url,
errorWidget: (_, __, ___) => const Icon(Icons.warning), errorWidget: (_, __, ___) => const Icon(Icons.warning),
progressIndicatorBuilder: (context, url, progress) => progressIndicatorBuilder: (context, url, progress) =>
CircularProgressIndicator(value: progress.progress), CircularProgressIndicator(value: progress.progress),
@ -356,7 +358,8 @@ class Post extends HookWidget {
Expanded( Expanded(
flex: 999, flex: 999,
child: Text( child: Text(
''' ${NumberFormat.compact().format(post.numberOfComments)} comment${post.numberOfComments == 1 ? '' : 's'}''', ' ${NumberFormat.compact().format(post.counts.comments)}'
' comment${post.counts.comments == 1 ? '' : 's'}',
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
softWrap: false, softWrap: false,
), ),
@ -365,7 +368,9 @@ class Post extends HookWidget {
if (!fullPost) if (!fullPost)
IconButton( IconButton(
icon: const Icon(Icons.share), icon: const Icon(Icons.share),
onPressed: () => Share.text('Share post url', post.apId, onPressed: () => Share.text(
'Share post url',
post.post.apId,
'text/plain')), // TODO: find a way to mark it as url 'text/plain')), // TODO: find a way to mark it as url
if (!fullPost) SavePostButton(post), if (!fullPost) SavePostButton(post),
_Voting(post), _Voting(post),
@ -387,16 +392,17 @@ class Post extends HookWidget {
children: [ children: [
info(), info(),
title(), title(),
if (whatType(post.url) != MediaType.other && if (whatType(post.post.url) != MediaType.other &&
whatType(post.url) != MediaType.none) whatType(post.post.url) != MediaType.none)
postImage() postImage()
else if (post.url != null && post.url.isNotEmpty) else if (post.post.url != null && post.post.url.isNotEmpty)
linkPreview(), linkPreview(),
if (post.body != null) if (post.post.body != null)
// TODO: trim content // TODO: trim content
Padding( Padding(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: MarkdownText(post.body, instanceHost: instanceHost)), child:
MarkdownText(post.post.body, instanceHost: instanceHost)),
actions(), actions(),
], ],
), ),
@ -421,12 +427,12 @@ class _Voting extends HookWidget {
final loggedInAction = useLoggedInAction(post.instanceHost); final loggedInAction = useLoggedInAction(post.instanceHost);
vote(VoteType vote, Jwt token) async { vote(VoteType vote, Jwt token) async {
final api = LemmyApi(post.instanceHost).v1; final api = LemmyApiV2(post.instanceHost);
loading.start(); loading.start();
try { try {
final res = await api.createPostLike( final res = await api.run(
postId: post.id, score: vote, auth: token.raw); CreatePostLike(postId: post.post.id, score: vote, auth: token.raw));
myVote.value = res.myVote; myVote.value = res.myVote;
// ignore: avoid_catches_without_on_clauses // ignore: avoid_catches_without_on_clauses
} catch (e) { } catch (e) {
@ -455,7 +461,7 @@ class _Voting extends HookWidget {
width: 20, height: 20, child: CircularProgressIndicator()) width: 20, height: 20, child: CircularProgressIndicator())
else else
Text(NumberFormat.compact() Text(NumberFormat.compact()
.format(post.score + (wasVoted ? 0 : myVote.value.value))), .format(post.counts.score + (wasVoted ? 0 : myVote.value.value))),
IconButton( IconButton(
icon: Icon( icon: Icon(
Icons.arrow_downward, Icons.arrow_downward,

View File

@ -1,7 +1,7 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'bottom_modal.dart'; import 'bottom_modal.dart';

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../hooks/delayed_loading.dart'; import '../hooks/delayed_loading.dart';
import '../hooks/logged_in_action.dart'; import '../hooks/logged_in_action.dart';
@ -20,12 +20,12 @@ class SavePostButton extends HookWidget {
final loggedInAction = useLoggedInAction(post.instanceHost); final loggedInAction = useLoggedInAction(post.instanceHost);
savePost(Jwt token) async { savePost(Jwt token) async {
final api = LemmyApi(post.instanceHost).v1; final api = LemmyApiV2(post.instanceHost);
loading.start(); loading.start();
try { try {
final res = await api.savePost( final res = await api.run(SavePost(
postId: post.id, save: !isSaved.value, auth: token.raw); postId: post.post.id, save: !isSaved.value, auth: token.raw));
isSaved.value = res.saved; isSaved.value = res.saved;
// ignore: avoid_catches_without_on_clauses // ignore: avoid_catches_without_on_clauses
} catch (e) { } catch (e) {

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../comment_tree.dart'; import '../comment_tree.dart';
import '../hooks/infinite_scroll.dart'; import '../hooks/infinite_scroll.dart';
@ -57,7 +57,7 @@ class InfinitePostList extends StatelessWidget {
onStyleChange: () {}, onStyleChange: () {},
builder: (post) => Column( builder: (post) => Column(
children: [ children: [
Post(post), PostWidget(post),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],
), ),
@ -72,7 +72,7 @@ class InfiniteCommentList extends StatelessWidget {
const InfiniteCommentList({@required this.fetcher}) : assert(fetcher != null); const InfiniteCommentList({@required this.fetcher}) : assert(fetcher != null);
Widget build(BuildContext context) => SortableInfiniteList<CommentView>( Widget build(BuildContext context) => SortableInfiniteList<CommentView>(
builder: (comment) => Comment( builder: (comment) => CommentWidget(
CommentTree(comment), CommentTree(comment),
postCreatorId: null, postCreatorId: null,
detached: true, detached: true,

View File

@ -2,7 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import 'package:timeago/timeago.dart' as timeago; import 'package:timeago/timeago.dart' as timeago;
import '../hooks/stores.dart'; import '../hooks/stores.dart';
@ -17,16 +17,16 @@ import 'sortable_infinite_list.dart';
/// Shared widget of UserPage and ProfileTab /// Shared widget of UserPage and ProfileTab
class UserProfile extends HookWidget { class UserProfile extends HookWidget {
final Future<UserDetails> _userDetails; final Future<FullUserView> _userDetails;
final String instanceHost; final String instanceHost;
UserProfile({@required int userId, @required this.instanceHost}) UserProfile({@required int userId, @required this.instanceHost})
: _userDetails = LemmyApi(instanceHost).v1.getUserDetails( : _userDetails = LemmyApiV2(instanceHost).run(GetUserDetails(
userId: userId, savedOnly: false, sort: SortType.active); userId: userId, savedOnly: false, sort: SortType.active));
UserProfile.fromUserDetails(UserDetails userDetails) UserProfile.fromUserDetails(FullUserView userDetails)
: _userDetails = Future.value(userDetails), : _userDetails = Future.value(userDetails),
instanceHost = userDetails.user.instanceHost; instanceHost = userDetails.instanceHost;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -47,7 +47,7 @@ class UserProfile extends HookWidget {
); );
} }
final userView = userDetailsSnap.data.user; final userView = userDetailsSnap.data.userView;
return DefaultTabController( return DefaultTabController(
length: 3, length: 3,
@ -78,27 +78,25 @@ class UserProfile extends HookWidget {
// TODO: first batch is already fetched on render // TODO: first batch is already fetched on render
// TODO: comment and post come from the same endpoint, could be shared // TODO: comment and post come from the same endpoint, could be shared
InfinitePostList( InfinitePostList(
fetcher: (page, batchSize, sort) => LemmyApi(instanceHost) fetcher: (page, batchSize, sort) => LemmyApiV2(instanceHost)
.v1 .run(GetUserDetails(
.getUserDetails( userId: userView.user.id,
userId: userView.id,
savedOnly: false, savedOnly: false,
sort: SortType.active, sort: SortType.active,
page: page, page: page,
limit: batchSize, limit: batchSize,
) ))
.then((val) => val.posts), .then((val) => val.posts),
), ),
InfiniteCommentList( InfiniteCommentList(
fetcher: (page, batchSize, sort) => LemmyApi(instanceHost) fetcher: (page, batchSize, sort) => LemmyApiV2(instanceHost)
.v1 .run(GetUserDetails(
.getUserDetails( userId: userView.user.id,
userId: userView.id,
savedOnly: false, savedOnly: false,
sort: SortType.active, sort: SortType.active,
page: page, page: page,
limit: batchSize, limit: batchSize,
) ))
.then((val) => val.comments), .then((val) => val.comments),
), ),
_AboutTab(userDetailsSnap.data), _AboutTab(userDetailsSnap.data),
@ -113,7 +111,7 @@ class UserProfile extends HookWidget {
/// Such as his nickname, no. of posts, no. of posts, /// Such as his nickname, no. of posts, no. of posts,
/// banner, avatar etc. /// banner, avatar etc.
class _UserOverview extends HookWidget { class _UserOverview extends HookWidget {
final UserView userView; final UserViewSafe userView;
const _UserOverview(this.userView); const _UserOverview(this.userView);
@ -125,12 +123,12 @@ class _UserOverview extends HookWidget {
return Stack( return Stack(
children: [ children: [
if (userView.banner != null) if (userView.user.banner != null)
// TODO: for some reason doesnt react to presses // TODO: for some reason doesnt react to presses
FullscreenableImage( FullscreenableImage(
url: userView.banner, url: userView.user.banner,
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: userView.banner, imageUrl: userView.user.banner,
errorWidget: (_, __, ___) => const SizedBox.shrink(), errorWidget: (_, __, ___) => const SizedBox.shrink(),
), ),
) )
@ -174,7 +172,7 @@ class _UserOverview extends HookWidget {
SafeArea( SafeArea(
child: Column( child: Column(
children: [ children: [
if (userView.avatar != null) if (userView.user.avatar != null)
SizedBox( SizedBox(
width: 80, width: 80,
height: 80, height: 80,
@ -190,9 +188,9 @@ class _UserOverview extends HookWidget {
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)), borderRadius: const BorderRadius.all(Radius.circular(12)),
child: FullscreenableImage( child: FullscreenableImage(
url: userView.avatar, url: userView.user.avatar,
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: userView.avatar, imageUrl: userView.user.avatar,
errorWidget: (_, __, ___) => const SizedBox.shrink(), errorWidget: (_, __, ___) => const SizedBox.shrink(),
), ),
), ),
@ -200,14 +198,14 @@ class _UserOverview extends HookWidget {
), ),
), ),
Padding( Padding(
padding: userView.avatar != null padding: userView.user.avatar != null
? const EdgeInsets.only(top: 8) ? const EdgeInsets.only(top: 8)
: const EdgeInsets.only(top: 70), : const EdgeInsets.only(top: 70),
child: Padding( child: Padding(
padding: padding: EdgeInsets.only(
EdgeInsets.only(top: userView.avatar == null ? 10 : 0), top: userView.user.avatar == null ? 10 : 0),
child: Text( child: Text(
userView.preferredUsername ?? userView.name, userView.user.preferredUsername ?? userView.user.name,
style: theme.textTheme.headline6, style: theme.textTheme.headline6,
), ),
), ),
@ -218,7 +216,7 @@ class _UserOverview extends HookWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
'@${userView.name}@', '@${userView.user.name}@',
style: theme.textTheme.caption, style: theme.textTheme.caption,
), ),
InkWell( InkWell(
@ -248,8 +246,8 @@ class _UserOverview extends HookWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 4), padding: const EdgeInsets.only(left: 4),
child: Text( child: Text(
'${compactNumber(userView.numberOfPosts)}' '${compactNumber(userView.counts.postCount)}'
' Post${pluralS(userView.numberOfPosts)}', ' Post${pluralS(userView.counts.postCount)}',
style: TextStyle(color: colorOnTopOfAccentColor), style: TextStyle(color: colorOnTopOfAccentColor),
), ),
), ),
@ -269,8 +267,8 @@ class _UserOverview extends HookWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 4), padding: const EdgeInsets.only(left: 4),
child: Text( child: Text(
'${compactNumber(userView.numberOfComments)}' '${compactNumber(userView.counts.commentCount)}'
' Comment${pluralS(userView.numberOfComments)}', ' Comment${pluralS(userView.counts.commentCount)}',
style: style:
TextStyle(color: colorOnTopOfAccentColor), TextStyle(color: colorOnTopOfAccentColor),
), ),
@ -285,7 +283,7 @@ class _UserOverview extends HookWidget {
Padding( Padding(
padding: const EdgeInsets.only(top: 15), padding: const EdgeInsets.only(top: 15),
child: Text( child: Text(
'Joined ${timeago.format(userView.published)}', 'Joined ${timeago.format(userView.user.published)}',
style: theme.textTheme.bodyText1, style: theme.textTheme.bodyText1,
), ),
), ),
@ -301,7 +299,8 @@ class _UserOverview extends HookWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 4), padding: const EdgeInsets.only(left: 4),
child: Text( child: Text(
DateFormat('MMM dd, yyyy').format(userView.published), DateFormat('MMM dd, yyyy')
.format(userView.user.published),
style: theme.textTheme.bodyText1, style: theme.textTheme.bodyText1,
), ),
), ),
@ -318,19 +317,21 @@ class _UserOverview extends HookWidget {
} }
class _AboutTab extends HookWidget { class _AboutTab extends HookWidget {
final UserDetails userDetails; final FullUserView userDetails;
const _AboutTab(this.userDetails); const _AboutTab(this.userDetails);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final instanceHost = userDetails.user.instanceHost; final instanceHost = userDetails.userView.user.instanceHost;
final accStore = useAccountsStore(); final accStore = useAccountsStore();
final isOwnedAccount = accStore.loggedInInstances.contains(instanceHost) && final isOwnedAccount = accStore.loggedInInstances.contains(instanceHost) &&
accStore.usernamesFor(instanceHost).contains(userDetails.user.name); accStore
.usernamesFor(instanceHost)
.contains(userDetails.userView.user.name);
const wallPadding = EdgeInsets.symmetric(horizontal: 15); const wallPadding = EdgeInsets.symmetric(horizontal: 15);
@ -378,10 +379,10 @@ class _AboutTab extends HookWidget {
), ),
onTap: () {}, // TODO: go to account editing onTap: () {}, // TODO: go to account editing
), ),
if (userDetails.user.bio != null) ...[ if (userDetails.userView.user.bio != null) ...[
Padding( Padding(
padding: wallPadding, padding: wallPadding,
child: MarkdownText(userDetails.user.bio, child: MarkdownText(userDetails.userView.user.bio,
instanceHost: instanceHost)), instanceHost: instanceHost)),
divider, divider,
], ],
@ -396,9 +397,9 @@ class _AboutTab extends HookWidget {
), ),
for (final comm for (final comm
in userDetails.moderates in userDetails.moderates
..sort((a, b) => a.communityName.compareTo(b.communityName))) ..sort((a, b) => a.community.name.compareTo(b.community.name)))
communityTile( communityTile(
comm.communityName, comm.communityIcon, comm.communityId), comm.community.name, comm.community.icon, comm.community.id),
divider divider
], ],
ListTile( ListTile(
@ -412,9 +413,9 @@ class _AboutTab extends HookWidget {
if (userDetails.follows.isNotEmpty) if (userDetails.follows.isNotEmpty)
for (final comm for (final comm
in userDetails.follows in userDetails.follows
..sort((a, b) => a.communityName.compareTo(b.communityName))) ..sort((a, b) => a.community.name.compareTo(b.community.name)))
communityTile( communityTile(
comm.communityName, comm.communityIcon, comm.communityId) comm.community.name, comm.community.icon, comm.community.id)
else else
const Padding( const Padding(
padding: EdgeInsets.only(top: 8), padding: EdgeInsets.only(top: 8),

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:lemmy_api_client/v2.dart';
import '../hooks/delayed_loading.dart'; import '../hooks/delayed_loading.dart';
import '../hooks/stores.dart'; import '../hooks/stores.dart';
@ -32,7 +32,7 @@ class WriteComment extends HookWidget {
final preview = () { final preview = () {
final body = MarkdownText( final body = MarkdownText(
comment?.content ?? post.body ?? '', comment?.comment?.content ?? post.post.body ?? '',
instanceHost: instanceHost, instanceHost: instanceHost,
); );
@ -40,7 +40,7 @@ class WriteComment extends HookWidget {
return Column( return Column(
children: [ children: [
Text( Text(
post.name, post.post.name,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
@ -53,17 +53,25 @@ class WriteComment extends HookWidget {
}(); }();
handleSubmit() async { handleSubmit() async {
final api = LemmyApi(instanceHost).v1; final api = LemmyApiV2(instanceHost);
final token = accStore.defaultTokenFor(instanceHost); final token = accStore.defaultTokenFor(instanceHost);
delayed.start(); delayed.start();
try { try {
final res = await api.createComment( print('''
content: controller.text, CreateComment(
postId: post?.id ?? comment.postId, content: ${controller.text},
parentId: comment?.id, postId: ${post?.post?.id ?? comment.post.id},
auth: token.raw); parentId: ${comment?.comment?.id},
auth: ${token.raw},
)''');
final res = await api.run(CreateComment(
content: controller.text,
postId: post?.post?.id ?? comment.post.id,
parentId: comment?.comment?.id,
auth: token.raw,
));
Navigator.of(context).pop(res); Navigator.of(context).pop(res);
// ignore: avoid_catches_without_on_clauses // ignore: avoid_catches_without_on_clauses
} catch (e) { } catch (e) {