feat(bb): user screen

This commit is contained in:
Rongjian Zhang 2020-02-02 16:40:12 +08:00
parent 9eec13df9a
commit 830bceaa36
5 changed files with 220 additions and 1 deletions

View File

@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/screens/bb_user.dart';
import 'package:git_touch/screens/gitea_orgs.dart';
import 'package:git_touch/screens/gitea_user.dart';
import 'package:git_touch/screens/gitlab_explore.dart';
@ -60,6 +61,14 @@ class _HomeState extends State<Home> {
return GitlabUserScreen(null);
}
break;
case PlatformType.bitbucket:
switch (index) {
case 0:
return BbUserScreen(null);
case 1:
return BbUserScreen(null);
}
break;
case PlatformType.gitea:
switch (index) {
case 0:
@ -134,6 +143,17 @@ class _HomeState extends State<Home> {
title: Text('Me'),
),
];
case PlatformType.bitbucket:
return [
BottomNavigationBarItem(
icon: Icon(Icons.explore),
title: Text('Explore'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Me'),
),
];
case PlatformType.gitea:
return [
BottomNavigationBarItem(

View File

@ -32,7 +32,12 @@ class DataWithPage<T> {
int cursor;
bool hasMore;
int total;
DataWithPage({this.data, this.cursor, this.hasMore, this.total});
DataWithPage({
@required this.data,
@required this.cursor,
@required this.hasMore,
this.total,
});
}
class AuthModel with ChangeNotifier {
@ -242,6 +247,30 @@ class AuthModel with ChangeNotifier {
}
}
Future fetchBb(String p) async {
if (!p.startsWith('/api')) p = '/api/2.0$p';
final input = Uri.parse(p);
final uri = Uri.parse(activeAccount.domain).replace(
userInfo: '${activeAccount.login}:${activeAccount.appPassword}',
path: input.path,
query: input.query,
);
final res = await http.get(uri);
final info = json.decode(utf8.decode(res.bodyBytes));
return info;
}
Future<DataWithPage<List>> fetchBbWithPage(String p) async {
final res = await fetchBb(p);
final v = BbPagination.fromJson(res);
return DataWithPage(
cursor: v.page,
total: v.size,
data: v.values,
hasMore: v.next != null,
);
}
Future<void> init() async {
// Listen scheme
_sub = getUriLinksStream().listen(_onSchemeDetected, onError: (err) {

View File

@ -1,10 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
part 'bitbucket.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class BbPagination {
int pagelen;
int size;
int page;
String next;
List values;
BbPagination();
factory BbPagination.fromJson(Map<String, dynamic> json) =>
_$BbPaginationFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbUser {
String username;
String nickname;
String displayName;
String type; // user, team
bool isStaff;
DateTime createdOn;
Map<String, dynamic> links;
@ -12,3 +26,22 @@ class BbUser {
BbUser();
factory BbUser.fromJson(Map<String, dynamic> json) => _$BbUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbRepo {
String name;
BbUser owner;
String website;
String language;
int size;
String type; // repository
bool isPrivate;
DateTime createdOn;
DateTime updatedOn;
String description;
String fullName;
Map<String, dynamic> links;
String get avatarUrl => links['avatar']['href'];
BbRepo();
factory BbRepo.fromJson(Map<String, dynamic> json) => _$BbRepoFromJson(json);
}

View File

@ -6,10 +6,30 @@ part of 'bitbucket.dart';
// JsonSerializableGenerator
// **************************************************************************
BbPagination _$BbPaginationFromJson(Map<String, dynamic> json) {
return BbPagination()
..pagelen = json['pagelen'] as int
..size = json['size'] as int
..page = json['page'] as int
..next = json['next'] as String
..values = json['values'] as List;
}
Map<String, dynamic> _$BbPaginationToJson(BbPagination instance) =>
<String, dynamic>{
'pagelen': instance.pagelen,
'size': instance.size,
'page': instance.page,
'next': instance.next,
'values': instance.values,
};
BbUser _$BbUserFromJson(Map<String, dynamic> json) {
return BbUser()
..username = json['username'] as String
..nickname = json['nickname'] as String
..displayName = json['display_name'] as String
..type = json['type'] as String
..isStaff = json['is_staff'] as bool
..createdOn = json['created_on'] == null
? null
@ -19,8 +39,47 @@ BbUser _$BbUserFromJson(Map<String, dynamic> json) {
Map<String, dynamic> _$BbUserToJson(BbUser instance) => <String, dynamic>{
'username': instance.username,
'nickname': instance.nickname,
'display_name': instance.displayName,
'type': instance.type,
'is_staff': instance.isStaff,
'created_on': instance.createdOn?.toIso8601String(),
'links': instance.links,
};
BbRepo _$BbRepoFromJson(Map<String, dynamic> json) {
return BbRepo()
..name = json['name'] as String
..owner = json['owner'] == null
? null
: BbUser.fromJson(json['owner'] as Map<String, dynamic>)
..website = json['website'] as String
..language = json['language'] as String
..size = json['size'] as int
..type = json['type'] as String
..isPrivate = json['is_private'] as bool
..createdOn = json['created_on'] == null
? null
: DateTime.parse(json['created_on'] as String)
..updatedOn = json['updated_on'] == null
? null
: DateTime.parse(json['updated_on'] as String)
..description = json['description'] as String
..fullName = json['full_name'] as String
..links = json['links'] as Map<String, dynamic>;
}
Map<String, dynamic> _$BbRepoToJson(BbRepo instance) => <String, dynamic>{
'name': instance.name,
'owner': instance.owner,
'website': instance.website,
'language': instance.language,
'size': instance.size,
'type': instance.type,
'is_private': instance.isPrivate,
'created_on': instance.createdOn?.toIso8601String(),
'updated_on': instance.updatedOn?.toIso8601String(),
'description': instance.description,
'full_name': instance.fullName,
'links': instance.links,
};

78
lib/screens/bb_user.dart Normal file
View File

@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/repository_item.dart';
import 'package:git_touch/widgets/user_header.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:timeago/timeago.dart' as timeago;
class BbUserScreen extends StatelessWidget {
final String login;
BbUserScreen(this.login);
bool get isViewer => login == null;
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<Tuple2<BbUser, Iterable<BbRepo>>>(
title: Text(isViewer ? 'Me' : 'User'),
fetchData: () async {
final auth = Provider.of<AuthModel>(context);
final _login = login ?? auth.activeAccount.login;
final res = await Future.wait([
auth.fetchBb('/users/$_login'),
auth.fetchBbWithPage('/repositories/$_login'),
]);
return Tuple2(
BbUser.fromJson(res[0]),
[
for (var v in (res[1] as DataWithPage<List>).data)
BbRepo.fromJson(v)
],
);
},
action: isViewer
? ActionEntry(
iconData: Icons.settings,
url: '/settings',
)
: null,
bodyBuilder: (data, _) {
final user = data.item1;
final repos = data.item2;
return Column(
children: <Widget>[
UserHeader(
login: user.displayName,
avatarUrl: user.avatarUrl,
name: user.nickname,
createdAt: user.createdOn,
bio: null,
),
CommonStyle.border,
Column(
children: <Widget>[
for (var v in repos)
RepositoryItem(
owner: v.owner.displayName,
name: v.name,
url: '/bitbucket/${v.fullName}',
avatarUrl: v.avatarUrl,
avatarLink: '/bitbucket/${v.owner.displayName}',
note: 'Updated ${timeago.format(v.updatedOn)}',
description: v.description,
forkCount: 0,
starCount: 0,
)
],
)
],
);
},
);
}
}