1
0
mirror of https://github.com/git-touch/git-touch synced 2025-02-23 14:57:42 +01:00

feat(gitee): watch, star a repo (#157)

* feat(gitee): watch, star a repo

* fix: pass in props as Tuple

* feat: bitbucket branches

* fix: use class instead of map
This commit is contained in:
Shreyas Thirumalai 2021-01-11 20:01:54 +05:30 committed by GitHub
parent 2017014ffd
commit c75288ef97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 26 deletions

View File

@ -337,7 +337,15 @@ class AuthModel with ChangeNotifier {
'${activeAccount.domain}/api/v5$p', '${activeAccount.domain}/api/v5$p',
headers: headers, headers: headers,
); );
break; return;
}
case 'PUT':
{
await http.put(
'${activeAccount.domain}/api/v5$p',
headers: headers,
);
return;
} }
case 'POST': case 'POST':
{ {
@ -357,6 +365,12 @@ class AuthModel with ChangeNotifier {
); );
break; break;
} }
case 'NO CONTENT':
{
res = await http.get('${activeAccount.domain}/api/v5$p',
headers: headers);
return res;
}
default: default:
{ {
res = await http.get('${activeAccount.domain}/api/v5$p', res = await http.get('${activeAccount.domain}/api/v5$p',
@ -364,11 +378,8 @@ class AuthModel with ChangeNotifier {
break; break;
} }
} }
if (requestType != 'DELETE') { final info = json.decode(utf8.decode(res.bodyBytes));
final info = json.decode(utf8.decode(res.bodyBytes)); return info;
return info;
}
return;
} }
Future<DataWithPage> fetchGiteeWithPage(String path, Future<DataWithPage> fetchGiteeWithPage(String path,

View File

@ -144,3 +144,12 @@ class BbComment {
factory BbComment.fromJson(Map<String, dynamic> json) => factory BbComment.fromJson(Map<String, dynamic> json) =>
_$BbCommentFromJson(json); _$BbCommentFromJson(json);
} }
@JsonSerializable(fieldRename: FieldRename.snake)
class BbBranch {
String name;
String type;
BbBranch();
factory BbBranch.fromJson(Map<String, dynamic> json) =>
_$BbBranchFromJson(json);
}

View File

@ -247,3 +247,14 @@ Map<String, dynamic> _$BbCommentToJson(BbComment instance) => <String, dynamic>{
'content': instance.content, 'content': instance.content,
'user': instance.user, 'user': instance.user,
}; };
BbBranch _$BbBranchFromJson(Map<String, dynamic> json) {
return BbBranch()
..name = json['name'] as String
..type = json['type'] as String;
}
Map<String, dynamic> _$BbBranchToJson(BbBranch instance) => <String, dynamic>{
'name': instance.name,
'type': instance.type,
};

View File

@ -426,11 +426,14 @@ class BitbucketRouter {
'/:login', '/:login',
(context, parameters) => BbUserScreen(parameters['login'].first, (context, parameters) => BbUserScreen(parameters['login'].first,
isTeam: parameters['team'].first == '1')); isTeam: parameters['team'].first == '1'));
static final repo = RouterScreen( static final repo = RouterScreen('/:owner/:name', (context, parameters) {
'/:owner/:name', if (parameters['branch'] == null) {
(context, parameters) => return BbRepoScreen(parameters['owner'].first, parameters['name'].first);
BbRepoScreen(parameters['owner'].first, parameters['name'].first), } else {
); return BbRepoScreen(parameters['owner'].first, parameters['name'].first,
branch: parameters['branch'].first);
}
});
static final object = RouterScreen( static final object = RouterScreen(
'/:owner/:name/src/:ref', '/:owner/:name/src/:ref',
(context, parameters) => BbObjectScreen( (context, parameters) => BbObjectScreen(

View File

@ -18,11 +18,12 @@ import '../generated/l10n.dart';
class BbRepoScreen extends StatelessWidget { class BbRepoScreen extends StatelessWidget {
final String owner; final String owner;
final String name; final String name;
BbRepoScreen(this.owner, this.name); final String branch;
BbRepoScreen(this.owner, this.name, {this.branch});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RefreshStatefulScaffold<Tuple2<BbRepo, String>>( return RefreshStatefulScaffold<Tuple3<BbRepo, String, List<BbBranch>>>(
title: AppBarTitle(S.of(context).repository), title: AppBarTitle(S.of(context).repository),
fetch: () async { fetch: () async {
final auth = context.read<AuthModel>(); final auth = context.read<AuthModel>();
@ -32,11 +33,17 @@ class BbRepoScreen extends StatelessWidget {
'/repositories/$owner/$name/src/${repo.mainbranch.name}/README.md'); '/repositories/$owner/$name/src/${repo.mainbranch.name}/README.md');
final readme = final readme =
res.statusCode >= 400 ? null : utf8.decode(res.bodyBytes); res.statusCode >= 400 ? null : utf8.decode(res.bodyBytes);
return Tuple2(repo, readme); final branches = await auth
.fetchBbWithPage('/repositories/$owner/$name/refs/branches')
.then((v) {
return [for (var branch in v.data) BbBranch.fromJson(branch)];
});
return Tuple3(repo, readme, branches);
}, },
bodyBuilder: (t, setState) { bodyBuilder: (t, setState) {
final theme = Provider.of<ThemeModel>(context); final theme = Provider.of<ThemeModel>(context);
final p = t.item1; final p = t.item1;
final branches = t.item3;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[ children: <Widget>[
@ -56,7 +63,8 @@ class BbRepoScreen extends StatelessWidget {
leftIconData: Octicons.code, leftIconData: Octicons.code,
text: Text('Code'), text: Text('Code'),
rightWidget: Text(filesize(p.size)), rightWidget: Text(filesize(p.size)),
url: '/bitbucket/$owner/$name/src/${p.mainbranch.name}', url:
'/bitbucket/$owner/$name/src/${branch == null ? p.mainbranch.name : branch}',
), ),
TableViewItem( TableViewItem(
leftIconData: Octicons.issue_opened, leftIconData: Octicons.issue_opened,
@ -71,8 +79,38 @@ class BbRepoScreen extends StatelessWidget {
TableViewItem( TableViewItem(
leftIconData: Octicons.history, leftIconData: Octicons.history,
text: Text('Commits'), text: Text('Commits'),
url: '/bitbucket/$owner/$name/commits/${p.mainbranch.name}', url:
'/bitbucket/$owner/$name/commits/${branch == null ? p.mainbranch.name : branch}',
), ),
if (branches != null)
TableViewItem(
leftIconData: Octicons.git_branch,
text: Text(S.of(context).branches),
rightWidget: Text(
(branch == null ? p.mainbranch.name : branch) +
'' +
branches.length.toString()),
onTap: () async {
if (branches.length < 2) return;
await theme.showPicker(
context,
PickerGroupItem(
value: branch,
items: branches
.map((b) => PickerItem(b.name, text: b.name))
.toList(),
onClose: (ref) {
if (ref != branch) {
theme.push(context,
'/bitbucket/$owner/$name?branch=$ref',
replace: true);
}
},
),
);
},
),
], ],
), ),
CommonStyle.verticalGap, CommonStyle.verticalGap,

View File

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:git_touch/models/auth.dart'; import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart'; import 'package:git_touch/models/gitee.dart';
@ -8,6 +9,7 @@ import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart'; import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/entry_item.dart'; import 'package:git_touch/widgets/entry_item.dart';
import 'package:git_touch/widgets/markdown_view.dart'; import 'package:git_touch/widgets/markdown_view.dart';
import 'package:git_touch/widgets/mutation_button.dart';
import 'package:git_touch/widgets/repo_header.dart'; import 'package:git_touch/widgets/repo_header.dart';
import 'package:git_touch/widgets/table_view.dart'; import 'package:git_touch/widgets/table_view.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -15,6 +17,12 @@ import 'package:tuple/tuple.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../generated/l10n.dart'; import '../generated/l10n.dart';
class StatusPayload {
bool isWatching;
bool isStarred;
StatusPayload(this.isWatching, this.isStarred);
}
class GeRepoScreen extends StatelessWidget { class GeRepoScreen extends StatelessWidget {
final String owner; final String owner;
final String name; final String name;
@ -24,7 +32,7 @@ class GeRepoScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RefreshStatefulScaffold< return RefreshStatefulScaffold<
Tuple3<GiteeRepo, MarkdownViewData, List<GiteeBranch>>>( Tuple4<GiteeRepo, MarkdownViewData, List<GiteeBranch>, StatusPayload>>(
title: AppBarTitle(S.of(context).repository), title: AppBarTitle(S.of(context).repository),
fetch: () async { fetch: () async {
final auth = context.read<AuthModel>(); final auth = context.read<AuthModel>();
@ -49,7 +57,15 @@ class GeRepoScreen extends StatelessWidget {
await auth.fetchGitee('/repos/$owner/$name/branches').then((v) { await auth.fetchGitee('/repos/$owner/$name/branches').then((v) {
return [for (var branch in v) GiteeBranch.fromJson(branch)]; return [for (var branch in v) GiteeBranch.fromJson(branch)];
}); });
return Tuple3(repo, readmeData, branches); bool isStarred = await auth
.fetchGitee('/user/starred/$owner/$name', requestType: 'NO CONTENT')
.then((v) => v.statusCode == HttpStatus.noContent);
bool isWatching = await auth
.fetchGitee('/user/subscriptions/$owner/$name',
requestType: 'NO CONTENT')
.then((v) => v.statusCode == HttpStatus.noContent);
StatusPayload statusPayload = StatusPayload(isWatching, isStarred);
return Tuple4(repo, readmeData, branches, statusPayload);
}, },
bodyBuilder: (t, setState) { bodyBuilder: (t, setState) {
final p = t.item1; final p = t.item1;
@ -59,17 +75,48 @@ class GeRepoScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[ children: <Widget>[
RepoHeader( RepoHeader(
avatarUrl: p.owner.avatarUrl, avatarUrl: p.owner.avatarUrl,
avatarLink: '/gitee/${p.namespace.path}', avatarLink: '/gitee/${p.namespace.path}',
owner: p.namespace.path, owner: p.namespace.path,
name: p.path, name: p.path,
description: p.description, description: p.description,
homepageUrl: p.homepage, homepageUrl: p.homepage,
), actions: [
CommonStyle.border, Row(children: <Widget>[
MutationButton(
active: t.item4.isWatching,
text: t.item4.isWatching ? 'Ignore' : 'Watch',
onTap: () async {
final String watchType =
t.item4.isWatching ? 'ignoring' : 'watching';
await context.read<AuthModel>().fetchGitee(
'/user/subscriptions/$owner/$name?watch_type=$watchType',
requestType: t.item4.isWatching ? 'DELETE' : 'PUT');
setState(() {
t.item4.isWatching = !t.item4.isWatching;
});
},
),
SizedBox(width: 8),
MutationButton(
active: t.item4.isStarred,
text: t.item4.isStarred ? 'Unstar' : 'Star',
onTap: () async {
await context.read<AuthModel>().fetchGitee(
'/user/starred/$owner/$name',
requestType: t.item4.isStarred ? 'DELETE' : 'PUT');
setState(() {
t.item4.isStarred = !t.item4.isStarred;
});
},
)
])
]),
Row( Row(
children: <Widget>[ children: <Widget>[
EntryItem( EntryItem(
count: p.watchersCount,
text: 'Watchers', text: 'Watchers',
url: '/gitee/$owner/$name/watchers', url: '/gitee/$owner/$name/watchers',
), ),