Compare commits

...

89 Commits

Author SHA1 Message Date
Rongjian Zhang 65e43dc633 chore: add generated dart files 2022-10-10 22:49:21 +08:00
Rongjian Zhang 06a96a14be chore: style tweaks 2022-10-10 21:52:20 +08:00
Rongjian Zhang e2de7feec2 fix: text height behavior 2022-10-10 20:19:28 +08:00
Rongjian Zhang 56402d789c feat: gh repo tweaks 2022-10-09 21:31:23 +08:00
Rongjian Zhang 29242aa45d chore: style tweaks 2022-10-08 21:19:07 +08:00
Rongjian Zhang ad44495236 fix: tag color with dark theme 2022-10-08 17:40:38 +08:00
Rongjian Zhang 3d21b6c226 refactor: app bar title 2022-10-08 01:06:03 +08:00
Rongjian Zhang fbb524a7de chore: remove unused files 2022-10-08 01:05:51 +08:00
Rongjian Zhang 7bd412eb2b refactor: remove reexports 2022-10-08 00:56:35 +08:00
Rongjian Zhang 7ca3fe03c5 refactor: use list extension 2022-10-08 00:46:49 +08:00
Rongjian Zhang b65ce98077 fix: swipe to remove account 2022-10-07 16:50:13 +08:00
Rongjian Zhang fddf67d518 chore: fix typo 2022-10-07 15:41:08 +08:00
Rongjian Zhang 5a5b20eb7e refactor: login screen styles 2022-10-07 15:24:56 +08:00
Rongjian Zhang efd74ccd86 refactor: replace material widgets 2022-10-06 16:21:56 +08:00
Rongjian Zhang 7b51747576 fix: pull page redirect 2022-10-06 15:18:01 +08:00
Rongjian Zhang 61d5a3e846 refactor: drop material color references 2022-10-06 15:13:59 +08:00
Rongjian Zhang 9892d87f9b Revert "fix: tag text color"
This reverts commit e872b4753d.
2022-10-06 15:08:04 +08:00
Rongjian Zhang 826664e3f1 refactor: language bar 2022-10-05 02:11:35 +08:00
Rongjian Zhang e6298b2363 refactor: button widgets 2022-10-05 01:51:53 +08:00
Rongjian Zhang 3953544d55 fix: new issue route priority 2022-10-05 01:40:03 +08:00
Rongjian Zhang f7eb0e6f62 feat(github): organization sponsors 2022-10-05 01:29:21 +08:00
Rongjian Zhang 830441ba05 feat(github): sponsors and sponsoring 2022-10-04 21:53:04 +08:00
Rongjian Zhang d86229e367 fix: contributions bar padding 2022-10-04 21:02:09 +08:00
Rongjian Zhang a1182ed6a5 feat(github): refine user screen organizations 2022-10-04 20:56:58 +08:00
Rongjian Zhang 633b58d36f fix: entry item style 2022-10-04 20:18:56 +08:00
Rongjian Zhang 4c2a7177eb feat(github): twitter username for orgs 2022-10-04 04:14:52 +08:00
Rongjian Zhang 057bad71fe refactor: check null 2022-10-04 04:09:36 +08:00
Rongjian Zhang 9b339c6cdf feat(github): twitter username at user screen 2022-10-04 04:07:15 +08:00
Rongjian Zhang 7a71d13f7d fix: github gql query 2022-10-04 03:58:45 +08:00
Rongjian Zhang 36e505d511 style: fix lint warnings 2022-10-04 01:05:29 +08:00
Rongjian Zhang 775b2cef49 chore: temporarily disable lint rules 2022-10-04 01:00:47 +08:00
Rongjian Zhang e872b4753d fix: tag text color 2022-10-03 20:27:59 +08:00
Rongjian Zhang df946c514a style: import sort format 2022-10-03 18:21:22 +08:00
Rongjian Zhang e45b8fa134 chore: add deprecated messages 2022-10-03 18:09:41 +08:00
Rongjian Zhang 2d28eb7b41 chore: style tweaks 2022-10-03 13:27:11 +08:00
Rongjian Zhang 55f3f6a713 fix: add monospace filter for code fonts 2022-10-02 18:50:22 +08:00
Rongjian Zhang 0a28311797 refactor: use ant sliver list 2022-10-02 16:57:34 +08:00
Rongjian Zhang b7dcf933a3 style: unused local variable 2022-10-02 13:13:28 +08:00
Rongjian Zhang 26b78a31a6 chore: style tweaks 2022-10-02 13:07:46 +08:00
Rongjian Zhang 0e952a6ef7 feat: dynamic fetch google fonts 2022-10-02 12:50:32 +08:00
Rongjian Zhang 9e2850558d feat: launch built-in maps 2022-10-02 12:50:32 +08:00
Rongjian Zhang 206cd1244c feat: launch built-in maps 2022-10-02 03:34:55 +08:00
Rongjian Zhang 4fe46b274a chore: update project query 2022-10-02 02:22:31 +08:00
Rongjian Zhang 048d35112b feat(gitlab): add gql client 2022-10-02 01:26:34 +08:00
Rongjian Zhang 3474d931ac ci: use vscode tasks and makefile 2022-10-02 00:59:01 +08:00
Rongjian Zhang 706d456c46 fix: split to packages to support gql gen 2022-10-02 00:32:05 +08:00
Rongjian Zhang efc2359f27 feat(gitlab): graphql schema and build config 2022-10-01 15:10:25 +08:00
Rongjian Zhang ba34337b07 chore: split schema update script 2022-10-01 13:30:34 +08:00
Rongjian Zhang 89966a95e6 fix: unused fragments server error 2022-10-01 04:50:53 +08:00
Rongjian Zhang 9df896a0db build: update deps 2022-10-01 04:20:05 +08:00
Rongjian Zhang 7b08f4cc07 refactor: split gql queries into files 2022-10-01 04:03:08 +08:00
Rongjian Zhang d5f3bd9f24 fix: hex color parse 2022-10-01 02:54:09 +08:00
Rongjian Zhang a30385a414 fix(gh): pull request router 2022-10-01 02:28:20 +08:00
Rongjian Zhang 8d1e3fa596 style: use super parameters 2022-09-26 10:59:30 +08:00
Rongjian Zhang d7972c4084 chore: settings style tweaks 2022-09-26 01:32:51 +08:00
Rongjian Zhang 6df89e771e feat(gh): organization follow 2022-09-26 01:26:58 +08:00
Rongjian Zhang 536b031655 feat: add sponsors 2022-09-25 23:12:41 +08:00
Rongjian Zhang 15a57899fd refactor: replace legacy palette 2022-09-25 02:46:37 +08:00
Rongjian Zhang fee559ba9a feat: apply dark theme 2022-09-25 02:23:55 +08:00
Rongjian Zhang 073af6f492 fix: theme usage 2022-09-24 18:35:19 +08:00
Rongjian Zhang d8f38d34aa style: fix apply 2022-09-24 13:51:11 +08:00
Rongjian Zhang 9b914ff1f1 refactor: github user screen query 2022-09-24 13:29:27 +08:00
Rongjian Zhang 20b7addf51 refactor: use list item 2022-09-24 02:22:17 +08:00
Rongjian Zhang 5214a404b1 refactor: replace table view 2022-09-24 01:49:27 +08:00
Rongjian Zhang cf6871ce20 refactor: push method as extension 2022-09-23 01:50:45 +08:00
Rongjian Zhang 988cdbfe45 refactor: list prop update 2022-09-22 23:37:06 +08:00
Rongjian Zhang e8eb860222 refactor: branch name widget 2022-09-22 16:56:02 +08:00
Rongjian Zhang 7e79da77ee style: lint fix apply 2022-09-22 16:28:21 +08:00
Rongjian Zhang 606370a85e fix: router path typo 2022-09-22 15:59:23 +08:00
Rongjian Zhang e5929aa1b8 refactor: use list 2022-09-21 02:04:07 +08:00
Rongjian Zhang f0b52e82ab feat(github): add meta screen 2022-09-20 21:26:38 +08:00
Rongjian Zhang 5b7b465b09 style: single quotes and package imports 2022-09-19 01:02:57 +08:00
Rongjian Zhang 8eda3e36c8 refactor: use popup show 2022-09-18 20:37:33 +08:00
Rongjian Zhang b2e7a97dab refactor: drop unused orgrepo query 2022-09-18 17:34:51 +08:00
Rongjian Zhang 137af2df75 refactor: use gql for orgs 2022-09-18 17:34:51 +08:00
Rongjian Zhang ec99de2b6d refactor: gql query names 2022-09-18 17:34:51 +08:00
Rongjian Zhang 66e14509de refactor: use gql data for org repos 2022-09-18 17:34:51 +08:00
Rongjian Zhang fac17a69ef refactor: mutation button 2022-09-18 13:02:06 +08:00
Rongjian Zhang d9c50cd0a9 fix: hex color prefix 2022-09-17 23:30:47 +08:00
Rongjian Zhang 4e715d9697 chore: contribution dark color 2022-09-17 23:15:01 +08:00
Rongjian Zhang d250f1c95c build: update icons 2022-09-17 22:35:45 +08:00
Rongjian Zhang a512e41121 refactor: use action sheet 2022-09-17 21:59:00 +08:00
Rongjian Zhang 9545b26310 refactor: drop scaffold theme 2022-09-17 20:46:45 +08:00
Rongjian Zhang 4af23c160d refactor: drop material widgets 2022-09-17 20:35:45 +08:00
Rongjian Zhang 084bc3c86a refactor: use ant list 2022-09-14 21:19:52 +08:00
Rongjian Zhang aac5fb866b refactor: table view 2022-09-14 11:52:35 +08:00
Rongjian Zhang 08f70164f4 refactor: css color parse 2022-09-12 22:59:17 +08:00
Rongjian Zhang cac6385e6e fix: path screen map 2022-09-12 11:54:41 +08:00
Rongjian Zhang 9207d52cf3 refactor: tag widgets 2022-09-12 11:46:42 +08:00
321 changed files with 692652 additions and 6134 deletions

4
.gitignore vendored
View File

@ -45,7 +45,3 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
*.gql.dart
*.g.dart
schema.graphql

29
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "all",
"dependsOn": ["json", "github", "gitlab"]
},
{
"label": "json",
"type": "dart",
"command": "dart",
"args": ["run", "build_runner", "watch"]
},
{
"label": "github",
"type": "dart",
"command": "dart",
"cwd": "packages/gql_github",
"args": ["run", "build_runner", "watch"]
},
{
"label": "gitlab",
"type": "dart",
"command": "dart",
"cwd": "packages/gql_gitlab",
"args": ["run", "build_runner", "watch"]
}
]
}

View File

@ -22,10 +22,22 @@ linter:
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
prefer_single_quotes: true
always_use_package_imports: true
sort_constructors_first: true
sort_unnamed_constructors_first: true
avoid_types_on_closure_parameters: true
omit_local_variable_types: true
prefer_final_in_for_each: true
prefer_final_locals: true
use_super_parameters: true
use_key_in_widget_constructors: false # waiting for auto fix: https://github.com/dart-lang/sdk/issues/50056
avoid_print: false # TODO:
analyzer:
errors:
todo: ignore
fixme: ignore
exclude:
- "**/*.gql.dart"
- "**/*.g.dart"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,17 +2,19 @@ PODS:
- Flutter (1.0.0)
- launch_review (0.0.1):
- Flutter
- maps_launcher (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_ios (0.0.1):
- Flutter
- Sentry (7.23.0):
- Sentry/Core (= 7.23.0)
- Sentry/Core (7.23.0)
- Sentry (7.26.0):
- Sentry/Core (= 7.26.0)
- Sentry/Core (7.26.0)
- sentry_flutter (0.0.1):
- Flutter
- FlutterMacOS
- Sentry (~> 7.23.0)
- Sentry (~> 7.26.0)
- share_plus (0.0.1):
- Flutter
- shared_preferences_ios (0.0.1):
@ -27,6 +29,7 @@ PODS:
DEPENDENCIES:
- Flutter (from `Flutter`)
- launch_review (from `.symlinks/plugins/launch_review/ios`)
- maps_launcher (from `.symlinks/plugins/maps_launcher/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
@ -45,6 +48,8 @@ EXTERNAL SOURCES:
:path: Flutter
launch_review:
:path: ".symlinks/plugins/launch_review/ios"
maps_launcher:
:path: ".symlinks/plugins/maps_launcher/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_ios:
@ -65,10 +70,11 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
launch_review: 75d5a956ba8eaa493e9c9d4bf4c05e505e8d5ed0
maps_launcher: 2e5b6a2d664ec6c27f82ffa81b74228d770ab203
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
Sentry: a0d4563fa4ddacba31fdcc35daaa8573d87224d6
sentry_flutter: 8bde7d0e57a721727fe573f13bb292c497b5a249
Sentry: 6b8f4a4f93c2471b0d73819c52da0c0ce4f0323c
sentry_flutter: 6604b70e74bfb15522c23788295e1debdbc86676
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a

View File

@ -1,5 +1,5 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
@ -8,77 +8,70 @@ import 'package:intl/locale.dart' as l;
import 'package:provider/provider.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final auth = Provider.of<AuthModel>(context);
final theme = Provider.of<ThemeModel>(context);
// ignore: prefer_function_declarations_over_variables
final LocaleListResolutionCallback localeListResolutionCallback =
(locales, supportedLocales) {
// 1. user set locale
// 2. system locale
try {
if (theme.locale != null) {
final intlLocale = l.Locale.parse(theme.locale!);
locales = [
Locale.fromSubtags(
languageCode: intlLocale.languageCode,
countryCode: intlLocale.countryCode,
scriptCode: intlLocale.scriptCode,
),
...locales!
];
}
} catch (err) {
print(err);
}
for (final locale in locales!) {
// this is necessary because Flutter only handles zh_Hans -> zh
// and would not handle non-exist language code
if (AppLocalizations.delegate.isSupported(locale)) {
return locale;
}
}
// 3. if none match, use the default
return supportedLocales.firstWhere((l) => l.languageCode == 'en');
};
return Container(
return AntTheme(
key: auth.rootKey,
child: theme.theme == AppThemeType.cupertino
? CupertinoApp.router(
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
theme: CupertinoThemeData(brightness: theme.brightness),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
localeListResolutionCallback: localeListResolutionCallback,
)
: MaterialApp.router(
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
theme: ThemeData(
brightness: theme.brightness,
primaryColor:
theme.brightness == Brightness.dark ? null : Colors.white,
scaffoldBackgroundColor: theme.palette.background,
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
},
data: AntThemeData(brightness: theme.brightness),
child: Builder(
builder: (context) {
final antTheme = AntTheme.of(context);
return CupertinoApp.router(
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
theme: CupertinoThemeData(
brightness: theme.brightness,
primaryColor: antTheme.colorPrimary,
scaffoldBackgroundColor: antTheme.colorBox,
textTheme: CupertinoTextThemeData(
textStyle: TextStyle(
fontSize: antTheme.fontSizeMain,
color: antTheme.colorText,
),
colorScheme: ColorScheme.fromSwatch()
.copyWith(secondary: theme.palette.primary),
),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
localeListResolutionCallback: localeListResolutionCallback,
),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
localeListResolutionCallback: (locales, supportedLocales) {
// 1. user set locale
// 2. system locale
try {
if (theme.locale != null) {
final intlLocale = l.Locale.parse(theme.locale!);
locales = [
Locale.fromSubtags(
languageCode: intlLocale.languageCode,
countryCode: intlLocale.countryCode,
scriptCode: intlLocale.scriptCode,
),
...locales!
];
}
} catch (err) {
print(err);
}
for (final locale in locales!) {
// this is necessary because Flutter only handles zh_Hans -> zh
// and would not handle non-exist language code
if (AppLocalizations.delegate.isSupported(locale)) {
return locale;
}
}
// 3. if none match, use the default
return supportedLocales.firstWhere((l) => l.languageCode == 'en');
},
);
},
),
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/models/theme.dart';
@ -33,7 +34,7 @@ import 'package:universal_io/io.dart';
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
@ -63,7 +64,7 @@ class _HomeState extends State<Home> {
// go to app store
LaunchReview.launch(writeReview: false);
} else {
context.read<ThemeModel>().push(context, latest.htmlUrl!);
context.pushUrl(latest.htmlUrl!);
}
}
}
@ -91,7 +92,7 @@ class _HomeState extends State<Home> {
case 3:
return GhSearchScreen();
case 4:
return GhViewer();
return const GhViewerScreen();
}
break;
case PlatformType.gitlab:
@ -143,8 +144,7 @@ class _HomeState extends State<Home> {
}
Widget _buildNotificationIcon(BuildContext context, IconData iconData) {
final theme = Provider.of<ThemeModel>(context);
int count = Provider.of<NotificationModel>(context).count;
final count = Provider.of<NotificationModel>(context).count;
if (count == 0) {
return Icon(iconData);
}
@ -154,10 +154,11 @@ class _HomeState extends State<Home> {
children: <Widget>[
Icon(iconData),
Positioned(
right: -2,
top: -2,
child: Icon(Octicons.primitive_dot,
color: theme.palette.primary, size: 14))
right: -2,
top: -2,
child: Icon(Octicons.dot_fill,
color: AntTheme.of(context).colorPrimary, size: 14),
)
],
);
}
@ -238,7 +239,6 @@ class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context);
if (auth.activeAccount == null) {
@ -247,56 +247,35 @@ class _HomeState extends State<Home> {
final navigationItems = _buildNavigationItems(auth.activeAccount!.platform);
switch (theme.theme) {
case AppThemeType.cupertino:
return WillPopScope(
onWillPop: () async {
return !(await getNavigatorKey(auth.activeTab)
.currentState
?.maybePop())!;
},
child: CupertinoTabScaffold(
tabBuilder: (context, index) {
return CupertinoTabView(
navigatorKey: getNavigatorKey(index),
builder: (context) {
return _buildScreen(index);
},
);
return WillPopScope(
onWillPop: () async {
return !(await getNavigatorKey(auth.activeTab)
.currentState
?.maybePop())!;
},
child: CupertinoTabScaffold(
tabBuilder: (context, index) {
return CupertinoTabView(
navigatorKey: getNavigatorKey(index),
builder: (context) {
return _buildScreen(index);
},
tabBar: CupertinoTabBar(
items: navigationItems,
currentIndex: auth.activeTab,
onTap: (index) {
if (auth.activeTab == index) {
getNavigatorKey(index)
.currentState
?.popUntil((route) => route.isFirst);
} else {
auth.setActiveTab(index);
}
},
),
),
);
default:
return Scaffold(
body: IndexedStack(
index: auth.activeTab,
children: [
for (var i = 0; i < navigationItems.length; i++) _buildScreen(i)
],
),
bottomNavigationBar: BottomNavigationBar(
selectedItemColor: theme.palette.primary,
items: navigationItems,
currentIndex: auth.activeTab,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
);
},
tabBar: CupertinoTabBar(
items: navigationItems,
currentIndex: auth.activeTab,
onTap: (index) {
if (auth.activeTab == index) {
getNavigatorKey(index)
.currentState
?.popUntil((route) => route.isFirst);
} else {
auth.setActiveTab(index);
},
),
);
}
}
},
),
),
);
}
}

View File

@ -115,18 +115,6 @@
"@dark": {
"description": "dark mode"
},
"scaffoldTheme": "Scaffold Theme",
"@scaffoldTheme": {
"description": "Kind of theme - cupertino or material"
},
"cupertino": "Cupertino",
"@cupertino": {
"description": "Cupertino scaffold theme"
},
"material": "Material",
"@material": {
"description": "Material scaffold theme"
},
"codeTheme": "Code Theme",
"@codeTheme": {
"description": "code theme"
@ -490,7 +478,7 @@
},
"reviewed": "reviewed",
"@reviewed": {
"description": "reviewed"
"description": "reviewed"
},
"mergedEventMessage": "merged commit {commit} into {mergeRefName}",
"@mergedEventMessage": {
@ -688,10 +676,10 @@
"description": "example: repo1,repo2 were added to the installation id ID",
"placeholders": {
"repos": {
"type": "String"
"type": "String"
},
"id": {
"type": "String"
"type": "String"
}
}
},
@ -703,7 +691,7 @@
"type": "String"
},
"id": {
"type": "String"
"type": "String"
}
}
},
@ -745,7 +733,7 @@
},
"was": "was",
"@was": {
"description": "was"
"description": "was"
},
"convertProjectCard": "converted the project card into an issue",
"@convertProjectCard": {
@ -781,7 +769,7 @@
},
"made": "made",
"@made": {
"description": "made"
"description": "made"
},
"public": "public",
"@public": {
@ -821,7 +809,7 @@
"type": "String"
},
"affectedRange": {
"type": "String"
"type": "String"
},
"action": {
"type": "String"
@ -836,7 +824,7 @@
"type": "String"
},
"action": {
"type": "String"
"type": "String"
}
}
},

View File

@ -1,11 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/app.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/code.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/models/theme.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
@ -17,8 +15,6 @@ void main() async {
},
// Init your App.
appRunner: () async {
GoogleFonts.config.allowRuntimeFetching = false;
final notificationModel = NotificationModel();
final themeModel = ThemeModel();
final authModel = AuthModel();
@ -29,11 +25,6 @@ void main() async {
codeModel.init(),
]);
// To match status bar color to app bar color
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
));
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => notificationModel),
@ -41,7 +32,7 @@ void main() async {
ChangeNotifierProvider(create: (context) => authModel),
ChangeNotifierProvider(create: (context) => codeModel),
],
child: MyApp(),
child: const MyApp(),
));
},
);

View File

@ -4,14 +4,7 @@ part 'account.g.dart';
@JsonSerializable(includeIfNull: false)
class Account {
String platform;
String domain;
String token;
String login;
String avatarUrl;
int? gitlabId; // For GitLab
String? appPassword; // For Bitbucket
String? accountId; // For Bitbucket
// For Bitbucket
// equals(Account a) {
// final uri = Uri.parse(domain);
@ -38,6 +31,14 @@ class Account {
factory Account.fromJson(Map<String, dynamic> json) =>
_$AccountFromJson(json);
String platform;
String domain;
String token;
String login;
String avatarUrl;
int? gitlabId; // For GitLab
String? appPassword; // For Bitbucket
String? accountId;
Map<String, dynamic> toJson() => _$AccountToJson(this);
}

39
lib/models/account.g.dart Normal file
View File

@ -0,0 +1,39 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'account.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Account _$AccountFromJson(Map<String, dynamic> json) => Account(
platform: json['platform'] as String,
domain: json['domain'] as String,
token: json['token'] as String,
login: json['login'] as String,
avatarUrl: json['avatarUrl'] as String,
gitlabId: json['gitlabId'] as int?,
appPassword: json['appPassword'] as String?,
accountId: json['accountId'] as String?,
);
Map<String, dynamic> _$AccountToJson(Account instance) {
final val = <String, dynamic>{
'platform': instance.platform,
'domain': instance.domain,
'token': instance.token,
'login': instance.login,
'avatarUrl': instance.avatarUrl,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('gitlabId', instance.gitlabId);
writeNotNull('appPassword', instance.appPassword);
writeNotNull('accountId', instance.accountId);
return val;
}

View File

@ -1,25 +1,26 @@
import 'dart:convert';
import 'dart:async';
import 'dart:convert';
import 'package:ferry/ferry.dart';
// import 'package:in_app_review/in_app_review.dart';
import 'package:universal_io/io.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/account.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/models/gitea.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/models/gitlab.dart';
import 'package:git_touch/models/gogs.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:github/github.dart';
import 'package:gql_http_link/gql_http_link.dart';
import 'package:fimber/fimber.dart';
import 'package:http/http.dart' as http;
import 'package:uni_links/uni_links.dart';
import 'package:nanoid/nanoid.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../utils/utils.dart';
import 'account.dart';
import 'gitlab.dart';
import 'gogs.dart';
import 'package:uni_links/uni_links.dart';
// import 'package:in_app_review/in_app_review.dart';
import 'package:universal_io/io.dart';
import 'package:url_launcher/url_launcher.dart';
const clientId = 'df930d7d2e219f26142a';
@ -33,16 +34,16 @@ class PlatformType {
}
class DataWithPage<T> {
T data;
int cursor;
bool hasMore;
int total;
DataWithPage({
required this.data,
required this.cursor,
required this.hasMore,
required this.total,
});
T data;
int cursor;
bool hasMore;
int total;
}
class AuthModel with ChangeNotifier {
@ -106,7 +107,7 @@ class AuthModel with ChangeNotifier {
await loginWithToken(token);
}
Future<void> loginWithToken(String token) async {
Future<void> loginWithToken(String t) async {
try {
final queryData = await query('''
{
@ -115,12 +116,12 @@ class AuthModel with ChangeNotifier {
avatarUrl
}
}
''', token);
''', t);
await _addAccount(Account(
platform: PlatformType.github,
domain: 'https://github.com',
token: token,
token: t,
login: queryData['viewer']['login'] as String,
avatarUrl: queryData['viewer']['avatarUrl'] as String,
));
@ -199,7 +200,7 @@ class AuthModel with ChangeNotifier {
hasMore: next != null,
total: int.tryParse(
res.headers['X-Total'] ?? res.headers['x-total'] ?? '') ??
TOTAL_COUNT_FALLBACK,
kTotalCountFallback,
);
}
@ -236,7 +237,7 @@ class AuthModel with ChangeNotifier {
Map<String, dynamic> body = const {},
}) async {
late http.Response res;
Map<String, String> headers = {
final headers = <String, String>{
'Authorization': 'token $token',
HttpHeaders.contentTypeHeader: 'application/json'
};
@ -284,7 +285,7 @@ class AuthModel with ChangeNotifier {
Future<DataWithPage> fetchGiteaWithPage(String path,
{int? page, int? limit}) async {
page = page ?? 1;
limit = limit ?? PAGE_SIZE;
limit = limit ?? kPageSize;
var uri = Uri.parse('${activeAccount!.domain}/api/v1$path');
uri = uri.replace(
@ -302,7 +303,7 @@ class AuthModel with ChangeNotifier {
cursor: page + 1,
hasMore: info is List && info.isNotEmpty,
total: int.tryParse(res.headers['x-total-count'] ?? '') ??
TOTAL_COUNT_FALLBACK,
kTotalCountFallback,
);
}
@ -340,7 +341,7 @@ class AuthModel with ChangeNotifier {
Map<String, dynamic> body = const {},
}) async {
late http.Response res;
Map<String, String> headers = {
final headers = <String, String>{
'Authorization': 'token $token',
HttpHeaders.contentTypeHeader: 'application/json'
};
@ -388,7 +389,7 @@ class AuthModel with ChangeNotifier {
Future<DataWithPage> fetchGogsWithPage(String path,
{int? page, int? limit}) async {
page = page ?? 1;
limit = limit ?? PAGE_SIZE;
limit = limit ?? kPageSize;
var uri = Uri.parse('${activeAccount!.domain}/api/v1$path');
uri = uri.replace(
@ -406,7 +407,7 @@ class AuthModel with ChangeNotifier {
cursor: page + 1,
hasMore: info is List && info.isNotEmpty,
total: int.tryParse(res.headers['x-total-count'] ?? '') ??
TOTAL_COUNT_FALLBACK,
kTotalCountFallback,
);
}
@ -416,7 +417,7 @@ class AuthModel with ChangeNotifier {
Map<String, dynamic> body = const {},
}) async {
http.Response res;
Map<String, String> headers = {
final headers = <String, String>{
'Authorization': 'token $token',
HttpHeaders.contentTypeHeader: 'application/json'
};
@ -475,7 +476,7 @@ class AuthModel with ChangeNotifier {
Future<DataWithPage> fetchGiteeWithPage(String path,
{int? page, int? limit}) async {
page = page ?? 1;
limit = limit ?? PAGE_SIZE;
limit = limit ?? kPageSize;
var uri = Uri.parse('${activeAccount!.domain}/api/v5$path');
uri = uri.replace(
@ -490,7 +491,7 @@ class AuthModel with ChangeNotifier {
final totalPage = int.tryParse(res.headers['total_page'] ?? '');
final totalCount =
int.tryParse(res.headers['total_count'] ?? '') ?? TOTAL_COUNT_FALLBACK;
int.tryParse(res.headers['total_count'] ?? '') ?? kTotalCountFallback;
return DataWithPage(
data: info,
@ -541,7 +542,7 @@ class AuthModel with ChangeNotifier {
userInfo: '${activeAccount!.login}:${activeAccount!.appPassword}',
path: input.path,
queryParameters: {
'pagelen': PAGE_SIZE.toString(),
'pagelen': kPageSize.toString(),
...input.queryParameters
},
);
@ -610,11 +611,11 @@ class AuthModel with ChangeNotifier {
Fimber.e('getUriLinksStream failed', ex: err);
});
var prefs = await SharedPreferences.getInstance();
final prefs = await SharedPreferences.getInstance();
// Read accounts
try {
String? str = prefs.getString(StorageKeys.accounts);
final str = prefs.getString(StorageKeys.accounts);
// Fimber.d('read accounts: $str');
_accounts = (json.decode(str ?? '[]') as List)
.map((item) => Account.fromJson(item))
@ -663,7 +664,8 @@ class AuthModel with ChangeNotifier {
StorageKeys.getDefaultStartTabKey(activeAccount!.platform)) ??
0;
_ghClient = null;
_gqlClient = null;
_ghGqlClient = null;
_glGqlClient = null;
notifyListeners();
// TODO: strategy
@ -688,29 +690,39 @@ class AuthModel with ChangeNotifier {
return _ghClient!;
}
Client? _gqlClient;
Client get gqlClient {
_gqlClient ??= Client(
Client? _ghGqlClient;
Client get ghGqlClient {
return _ghGqlClient ??= Client(
link: HttpLink(
'$_apiPrefix/graphql',
defaultHeaders: {HttpHeaders.authorizationHeader: 'token $token'},
),
// https://ferrygraphql.com/docs/fetch-policies#default-fetchpolicies
defaultFetchPolicies: {
OperationType.query: FetchPolicy.NetworkOnly,
},
defaultFetchPolicies: {OperationType.query: FetchPolicy.NetworkOnly},
);
return _gqlClient!;
}
Future<dynamic> query(String query, [String? token]) async {
token ??= token;
Client? _glGqlClient;
Client get glGqlClient {
return _glGqlClient ??= Client(
link: HttpLink(
Uri.parse(activeAccount!.domain)
.replace(path: '/api/graphql')
.toString(),
defaultHeaders: {'Private-Token': token},
),
// https://ferrygraphql.com/docs/fetch-policies#default-fetchpolicies
defaultFetchPolicies: {OperationType.query: FetchPolicy.NetworkOnly},
);
}
Future<dynamic> query(String query, [String? t]) async {
t ??= token;
final res = await http
.post(Uri.parse('$_apiPrefix/graphql'),
headers: {
HttpHeaders.authorizationHeader: 'token $token',
HttpHeaders.authorizationHeader: 'token $t',
HttpHeaders.contentTypeHeader: 'application/json'
},
body: json.encode({'query': query}))

View File

@ -1,39 +1,42 @@
import 'package:json_annotation/json_annotation.dart';
part 'bitbucket.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class BbPagination {
String? next;
List values;
BbPagination({required this.values});
factory BbPagination.fromJson(Map<String, dynamic> json) =>
_$BbPaginationFromJson(json);
String? next;
List values;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbRepoOwner {
BbRepoOwner();
factory BbRepoOwner.fromJson(Map<String, dynamic> json) =>
_$BbRepoOwnerFromJson(json);
String? nickname;
String? displayName;
String? type; // user, team
Map<String, dynamic>? links;
String? get avatarUrl => links!['avatar']['href'];
BbRepoOwner();
factory BbRepoOwner.fromJson(Map<String, dynamic> json) =>
_$BbRepoOwnerFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbUser extends BbRepoOwner {
BbUser();
factory BbUser.fromJson(Map<String, dynamic> json) => _$BbUserFromJson(json);
String? username;
bool? isStaff;
DateTime? createdOn;
String? accountId;
BbUser();
factory BbUser.fromJson(Map<String, dynamic> json) => _$BbUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbRepo {
BbRepo();
factory BbRepo.fromJson(Map<String, dynamic> json) => _$BbRepoFromJson(json);
String? name;
BbRepoOwner? owner;
String? website;
@ -50,51 +53,52 @@ class BbRepo {
Map<String, dynamic>? links;
String get ownerLogin => fullName!.split('/')[0]; // owner has no username
String? get avatarUrl => links!['avatar']['href'];
BbRepo();
factory BbRepo.fromJson(Map<String, dynamic> json) => _$BbRepoFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbRepoMainbranch {
String? type;
String? name;
BbRepoMainbranch();
factory BbRepoMainbranch.fromJson(Map<String, dynamic> json) =>
_$BbRepoMainbranchFromJson(json);
String? type;
String? name;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbTree {
BbTree({required this.type, required this.path});
factory BbTree.fromJson(Map<String, dynamic> json) => _$BbTreeFromJson(json);
String type;
String path;
int? size;
Map<String, dynamic>? links;
BbTree({required this.type, required this.path});
factory BbTree.fromJson(Map<String, dynamic> json) => _$BbTreeFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbCommit {
BbCommit();
factory BbCommit.fromJson(Map<String, dynamic> json) =>
_$BbCommitFromJson(json);
String? message;
DateTime? date;
String? hash;
BbCommitAuthor? author;
BbCommit();
factory BbCommit.fromJson(Map<String, dynamic> json) =>
_$BbCommitFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbCommitAuthor {
String? raw;
BbRepoOwner? user;
BbCommitAuthor();
factory BbCommitAuthor.fromJson(Map<String, dynamic> json) =>
_$BbCommitAuthorFromJson(json);
String? raw;
BbRepoOwner? user;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbIssues {
BbIssues();
factory BbIssues.fromJson(Map<String, dynamic> json) =>
_$BbIssuesFromJson(json);
String? priority;
String? state;
BbRepo? repository;
@ -103,50 +107,47 @@ class BbIssues {
DateTime? createdOn;
Map<String, dynamic>? links;
String? get issueLink => links!['self']['href'];
BbIssues();
factory BbIssues.fromJson(Map<String, dynamic> json) =>
_$BbIssuesFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbPulls {
BbPulls();
factory BbPulls.fromJson(Map<String, dynamic> json) =>
_$BbPullsFromJson(json);
String? description;
BbRepoOwner? author;
String? title;
Map<String, dynamic>? links;
String? get pullRequestLink => links!['self']['href'];
DateTime? createdOn;
BbPulls();
factory BbPulls.fromJson(Map<String, dynamic> json) =>
_$BbPullsFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbCommentContent {
String? raw;
String? markup;
String? html;
BbCommentContent();
factory BbCommentContent.fromJson(Map<String, dynamic> json) =>
_$BbCommentContentFromJson(json);
String? raw;
String? markup;
String? html;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbComment {
BbComment();
factory BbComment.fromJson(Map<String, dynamic> json) =>
_$BbCommentFromJson(json);
String? createdOn;
String? updatedOn;
BbCommentContent? content;
BbRepoOwner? user;
BbComment();
factory BbComment.fromJson(Map<String, dynamic> json) =>
_$BbCommentFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class BbBranch {
String? name;
String? type;
BbBranch();
factory BbBranch.fromJson(Map<String, dynamic> json) =>
_$BbBranchFromJson(json);
String? name;
String? type;
}

231
lib/models/bitbucket.g.dart Normal file
View File

@ -0,0 +1,231 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bitbucket.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
BbPagination _$BbPaginationFromJson(Map<String, dynamic> json) => BbPagination(
values: json['values'] as List<dynamic>,
)..next = json['next'] as String?;
Map<String, dynamic> _$BbPaginationToJson(BbPagination instance) =>
<String, dynamic>{
'next': instance.next,
'values': instance.values,
};
BbRepoOwner _$BbRepoOwnerFromJson(Map<String, dynamic> json) => BbRepoOwner()
..nickname = json['nickname'] as String?
..displayName = json['display_name'] as String?
..type = json['type'] as String?
..links = json['links'] as Map<String, dynamic>?;
Map<String, dynamic> _$BbRepoOwnerToJson(BbRepoOwner instance) =>
<String, dynamic>{
'nickname': instance.nickname,
'display_name': instance.displayName,
'type': instance.type,
'links': instance.links,
};
BbUser _$BbUserFromJson(Map<String, dynamic> json) => BbUser()
..nickname = json['nickname'] as String?
..displayName = json['display_name'] as String?
..type = json['type'] as String?
..links = json['links'] as Map<String, dynamic>?
..username = json['username'] as String?
..isStaff = json['is_staff'] as bool?
..createdOn = json['created_on'] == null
? null
: DateTime.parse(json['created_on'] as String)
..accountId = json['account_id'] as String?;
Map<String, dynamic> _$BbUserToJson(BbUser instance) => <String, dynamic>{
'nickname': instance.nickname,
'display_name': instance.displayName,
'type': instance.type,
'links': instance.links,
'username': instance.username,
'is_staff': instance.isStaff,
'created_on': instance.createdOn?.toIso8601String(),
'account_id': instance.accountId,
};
BbRepo _$BbRepoFromJson(Map<String, dynamic> json) => BbRepo()
..name = json['name'] as String?
..owner = json['owner'] == null
? null
: BbRepoOwner.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?
..slug = json['slug'] as String?
..mainbranch = json['mainbranch'] == null
? null
: BbRepoMainbranch.fromJson(json['mainbranch'] as Map<String, dynamic>)
..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,
'slug': instance.slug,
'mainbranch': instance.mainbranch,
'links': instance.links,
};
BbRepoMainbranch _$BbRepoMainbranchFromJson(Map<String, dynamic> json) =>
BbRepoMainbranch()
..type = json['type'] as String?
..name = json['name'] as String?;
Map<String, dynamic> _$BbRepoMainbranchToJson(BbRepoMainbranch instance) =>
<String, dynamic>{
'type': instance.type,
'name': instance.name,
};
BbTree _$BbTreeFromJson(Map<String, dynamic> json) => BbTree(
type: json['type'] as String,
path: json['path'] as String,
)
..size = json['size'] as int?
..links = json['links'] as Map<String, dynamic>?;
Map<String, dynamic> _$BbTreeToJson(BbTree instance) => <String, dynamic>{
'type': instance.type,
'path': instance.path,
'size': instance.size,
'links': instance.links,
};
BbCommit _$BbCommitFromJson(Map<String, dynamic> json) => BbCommit()
..message = json['message'] as String?
..date = json['date'] == null ? null : DateTime.parse(json['date'] as String)
..hash = json['hash'] as String?
..author = json['author'] == null
? null
: BbCommitAuthor.fromJson(json['author'] as Map<String, dynamic>);
Map<String, dynamic> _$BbCommitToJson(BbCommit instance) => <String, dynamic>{
'message': instance.message,
'date': instance.date?.toIso8601String(),
'hash': instance.hash,
'author': instance.author,
};
BbCommitAuthor _$BbCommitAuthorFromJson(Map<String, dynamic> json) =>
BbCommitAuthor()
..raw = json['raw'] as String?
..user = json['user'] == null
? null
: BbRepoOwner.fromJson(json['user'] as Map<String, dynamic>);
Map<String, dynamic> _$BbCommitAuthorToJson(BbCommitAuthor instance) =>
<String, dynamic>{
'raw': instance.raw,
'user': instance.user,
};
BbIssues _$BbIssuesFromJson(Map<String, dynamic> json) => BbIssues()
..priority = json['priority'] as String?
..state = json['state'] as String?
..repository = json['repository'] == null
? null
: BbRepo.fromJson(json['repository'] as Map<String, dynamic>)
..title = json['title'] as String?
..reporter = json['reporter'] == null
? null
: BbRepoOwner.fromJson(json['reporter'] as Map<String, dynamic>)
..createdOn = json['created_on'] == null
? null
: DateTime.parse(json['created_on'] as String)
..links = json['links'] as Map<String, dynamic>?;
Map<String, dynamic> _$BbIssuesToJson(BbIssues instance) => <String, dynamic>{
'priority': instance.priority,
'state': instance.state,
'repository': instance.repository,
'title': instance.title,
'reporter': instance.reporter,
'created_on': instance.createdOn?.toIso8601String(),
'links': instance.links,
};
BbPulls _$BbPullsFromJson(Map<String, dynamic> json) => BbPulls()
..description = json['description'] as String?
..author = json['author'] == null
? null
: BbRepoOwner.fromJson(json['author'] as Map<String, dynamic>)
..title = json['title'] as String?
..links = json['links'] as Map<String, dynamic>?
..createdOn = json['created_on'] == null
? null
: DateTime.parse(json['created_on'] as String);
Map<String, dynamic> _$BbPullsToJson(BbPulls instance) => <String, dynamic>{
'description': instance.description,
'author': instance.author,
'title': instance.title,
'links': instance.links,
'created_on': instance.createdOn?.toIso8601String(),
};
BbCommentContent _$BbCommentContentFromJson(Map<String, dynamic> json) =>
BbCommentContent()
..raw = json['raw'] as String?
..markup = json['markup'] as String?
..html = json['html'] as String?;
Map<String, dynamic> _$BbCommentContentToJson(BbCommentContent instance) =>
<String, dynamic>{
'raw': instance.raw,
'markup': instance.markup,
'html': instance.html,
};
BbComment _$BbCommentFromJson(Map<String, dynamic> json) => BbComment()
..createdOn = json['created_on'] as String?
..updatedOn = json['updated_on'] as String?
..content = json['content'] == null
? null
: BbCommentContent.fromJson(json['content'] as Map<String, dynamic>)
..user = json['user'] == null
? null
: BbRepoOwner.fromJson(json['user'] as Map<String, dynamic>);
Map<String, dynamic> _$BbCommentToJson(BbComment instance) => <String, dynamic>{
'created_on': instance.createdOn,
'updated_on': instance.updatedOn,
'content': instance.content,
'user': instance.user,
};
BbBranch _$BbBranchFromJson(Map<String, dynamic> json) => 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

@ -1,5 +1,5 @@
import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_highlight/theme_map.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:google_fonts/google_fonts.dart';
@ -8,21 +8,28 @@ import 'package:shared_preferences/shared_preferences.dart';
class CodeModel with ChangeNotifier {
static var themes = themeMap.keys.toList();
static const fontSizes = [12, 13, 14, 15, 16, 17, 18, 19, 20];
static const fontFamilies = [
static final fontFamilies = [
'System',
'JetBrains Mono',
'Fira Code',
'Inconsolata',
'PT Mono',
'Source Code Pro',
'Ubuntu Mono',
// 'Cascadia Code', // TODO: https://github.com/google/fonts/issues/2179
// https://fonts.google.com/?category=Monospace
...GoogleFonts.asMap().keys.where((element) =>
element.endsWith('Mono') ||
[
'Inconsolata',
'Source Code Pro',
'Nanum Gothic Coding',
'Cousine',
'Anonymous Pro',
'Courier Prime',
'VT323',
'Fira Code'
].contains(element)),
];
String _theme = 'vs';
String _themeDark = 'vs2015';
int _fontSize = 14;
String _fontFamily = 'JetBrains Mono';
String _fontFamily = 'System';
String get theme => _theme;
String get themeDark => _themeDark;
@ -39,11 +46,11 @@ class CodeModel with ChangeNotifier {
}
Future<void> init() async {
var prefs = await SharedPreferences.getInstance();
var vh = prefs.getString(StorageKeys.codeTheme);
var vdh = prefs.getString(StorageKeys.codeThemeDark);
var vs = prefs.getInt(StorageKeys.iCodeFontSize);
var vf = prefs.getString(StorageKeys.codeFontFamily);
final prefs = await SharedPreferences.getInstance();
final vh = prefs.getString(StorageKeys.codeTheme);
final vdh = prefs.getString(StorageKeys.codeThemeDark);
final vs = prefs.getInt(StorageKeys.iCodeFontSize);
final vf = prefs.getString(StorageKeys.codeFontFamily);
Fimber.d('read code: $vh, $vs, $vf');
if (themeMap.keys.contains(vh)) {
@ -63,7 +70,7 @@ class CodeModel with ChangeNotifier {
}
setTheme(String v) async {
var prefs = await SharedPreferences.getInstance();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(StorageKeys.codeTheme, v);
Fimber.d('write code theme: $v');
@ -73,7 +80,7 @@ class CodeModel with ChangeNotifier {
}
setThemeDark(String v) async {
var prefs = await SharedPreferences.getInstance();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(StorageKeys.codeThemeDark, v);
Fimber.d('write code theme dark: $v');
@ -83,7 +90,7 @@ class CodeModel with ChangeNotifier {
}
setFontSize(int v) async {
var prefs = await SharedPreferences.getInstance();
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(StorageKeys.iCodeFontSize, v);
Fimber.d('write code font size: $v');
@ -93,7 +100,7 @@ class CodeModel with ChangeNotifier {
}
setFontFamily(String v) async {
var prefs = await SharedPreferences.getInstance();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(StorageKeys.codeFontFamily, v);
Fimber.d('write code font family: $v');

View File

@ -4,18 +4,21 @@ part 'gitea.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaUser {
GiteaUser();
factory GiteaUser.fromJson(Map<String, dynamic> json) =>
_$GiteaUserFromJson(json);
int? id;
String? login;
String? fullName;
String? avatarUrl;
DateTime? created;
GiteaUser();
factory GiteaUser.fromJson(Map<String, dynamic> json) =>
_$GiteaUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaOrg {
GiteaOrg();
factory GiteaOrg.fromJson(Map<String, dynamic> json) =>
_$GiteaOrgFromJson(json);
int? id;
String? username;
String? fullName;
@ -23,13 +26,13 @@ class GiteaOrg {
String? description;
String? website;
String? location;
GiteaOrg();
factory GiteaOrg.fromJson(Map<String, dynamic> json) =>
_$GiteaOrgFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaRepository {
GiteaRepository();
factory GiteaRepository.fromJson(Map<String, dynamic> json) =>
_$GiteaRepositoryFromJson(json);
int? id;
GiteaUser? owner;
String? name;
@ -41,34 +44,33 @@ class GiteaRepository {
int? size;
int? openIssuesCount;
int? openPrCounter;
GiteaRepository();
factory GiteaRepository.fromJson(Map<String, dynamic> json) =>
_$GiteaRepositoryFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaTree {
GiteaTree({required this.type, required this.name});
factory GiteaTree.fromJson(Map<String, dynamic> json) =>
_$GiteaTreeFromJson(json);
String type;
String name;
String? path;
int? size;
String? downloadUrl;
GiteaTree({required this.type, required this.name});
factory GiteaTree.fromJson(Map<String, dynamic> json) =>
_$GiteaTreeFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaBlob extends GiteaTree {
String? content;
GiteaBlob({required String type, required String name})
: super(name: name, type: type);
GiteaBlob({required super.type, required super.name});
factory GiteaBlob.fromJson(Map<String, dynamic> json) =>
_$GiteaBlobFromJson(json);
String? content;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaCommit {
GiteaCommit();
factory GiteaCommit.fromJson(Map<String, dynamic> json) =>
_$GiteaCommitFromJson(json);
int? number;
GiteaUser? author;
String? title;
@ -76,33 +78,33 @@ class GiteaCommit {
GiteaCommitDetail? commit;
String? sha;
String? htmlUrl;
GiteaCommit();
factory GiteaCommit.fromJson(Map<String, dynamic> json) =>
_$GiteaCommitFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaCommitDetail {
String? message;
GiteaCommitAuthor? author;
GiteaCommitAuthor? committer;
GiteaCommitDetail();
factory GiteaCommitDetail.fromJson(Map<String, dynamic> json) =>
_$GiteaCommitDetailFromJson(json);
String? message;
GiteaCommitAuthor? author;
GiteaCommitAuthor? committer;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaCommitAuthor {
String? name;
String? email;
DateTime? date;
GiteaCommitAuthor();
factory GiteaCommitAuthor.fromJson(Map<String, dynamic> json) =>
_$GiteaCommitAuthorFromJson(json);
String? name;
String? email;
DateTime? date;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaIssue {
GiteaIssue();
factory GiteaIssue.fromJson(Map<String, dynamic> json) =>
_$GiteaIssueFromJson(json);
String? title;
String? body;
int? number;
@ -112,31 +114,31 @@ class GiteaIssue {
String? state;
String? htmlUrl;
List<GiteaLabel>? labels;
GiteaIssue();
factory GiteaIssue.fromJson(Map<String, dynamic> json) =>
_$GiteaIssueFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaLabel {
String? color;
String? name;
GiteaLabel();
factory GiteaLabel.fromJson(Map<String, dynamic> json) =>
_$GiteaLabelFromJson(json);
String? color;
String? name;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaHeatmapItem {
int? timestamp;
int? contributions;
GiteaHeatmapItem();
factory GiteaHeatmapItem.fromJson(Map<String, dynamic> json) =>
_$GiteaHeatmapItemFromJson(json);
int? timestamp;
int? contributions;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteaComment {
GiteaComment();
factory GiteaComment.fromJson(Map<String, dynamic> json) =>
_$GiteaCommentFromJson(json);
String? body;
DateTime? createdAt;
String? htmlUrl;
@ -144,7 +146,4 @@ class GiteaComment {
DateTime? updatedAt;
int? id;
GiteaUser? user;
GiteaComment();
factory GiteaComment.fromJson(Map<String, dynamic> json) =>
_$GiteaCommentFromJson(json);
}

243
lib/models/gitea.g.dart Normal file
View File

@ -0,0 +1,243 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'gitea.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
GiteaUser _$GiteaUserFromJson(Map<String, dynamic> json) => GiteaUser()
..id = json['id'] as int?
..login = json['login'] as String?
..fullName = json['full_name'] as String?
..avatarUrl = json['avatar_url'] as String?
..created = json['created'] == null
? null
: DateTime.parse(json['created'] as String);
Map<String, dynamic> _$GiteaUserToJson(GiteaUser instance) => <String, dynamic>{
'id': instance.id,
'login': instance.login,
'full_name': instance.fullName,
'avatar_url': instance.avatarUrl,
'created': instance.created?.toIso8601String(),
};
GiteaOrg _$GiteaOrgFromJson(Map<String, dynamic> json) => GiteaOrg()
..id = json['id'] as int?
..username = json['username'] as String?
..fullName = json['full_name'] as String?
..avatarUrl = json['avatar_url'] as String?
..description = json['description'] as String?
..website = json['website'] as String?
..location = json['location'] as String?;
Map<String, dynamic> _$GiteaOrgToJson(GiteaOrg instance) => <String, dynamic>{
'id': instance.id,
'username': instance.username,
'full_name': instance.fullName,
'avatar_url': instance.avatarUrl,
'description': instance.description,
'website': instance.website,
'location': instance.location,
};
GiteaRepository _$GiteaRepositoryFromJson(Map<String, dynamic> json) =>
GiteaRepository()
..id = json['id'] as int?
..owner = json['owner'] == null
? null
: GiteaUser.fromJson(json['owner'] as Map<String, dynamic>)
..name = json['name'] as String?
..description = json['description'] as String?
..starsCount = json['stars_count'] as int?
..forksCount = json['forks_count'] as int?
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..website = json['website'] as String?
..size = json['size'] as int?
..openIssuesCount = json['open_issues_count'] as int?
..openPrCounter = json['open_pr_counter'] as int?;
Map<String, dynamic> _$GiteaRepositoryToJson(GiteaRepository instance) =>
<String, dynamic>{
'id': instance.id,
'owner': instance.owner,
'name': instance.name,
'description': instance.description,
'stars_count': instance.starsCount,
'forks_count': instance.forksCount,
'updated_at': instance.updatedAt?.toIso8601String(),
'website': instance.website,
'size': instance.size,
'open_issues_count': instance.openIssuesCount,
'open_pr_counter': instance.openPrCounter,
};
GiteaTree _$GiteaTreeFromJson(Map<String, dynamic> json) => GiteaTree(
type: json['type'] as String,
name: json['name'] as String,
)
..path = json['path'] as String?
..size = json['size'] as int?
..downloadUrl = json['download_url'] as String?;
Map<String, dynamic> _$GiteaTreeToJson(GiteaTree instance) => <String, dynamic>{
'type': instance.type,
'name': instance.name,
'path': instance.path,
'size': instance.size,
'download_url': instance.downloadUrl,
};
GiteaBlob _$GiteaBlobFromJson(Map<String, dynamic> json) => GiteaBlob(
type: json['type'] as String,
name: json['name'] as String,
)
..path = json['path'] as String?
..size = json['size'] as int?
..downloadUrl = json['download_url'] as String?
..content = json['content'] as String?;
Map<String, dynamic> _$GiteaBlobToJson(GiteaBlob instance) => <String, dynamic>{
'type': instance.type,
'name': instance.name,
'path': instance.path,
'size': instance.size,
'download_url': instance.downloadUrl,
'content': instance.content,
};
GiteaCommit _$GiteaCommitFromJson(Map<String, dynamic> json) => GiteaCommit()
..number = json['number'] as int?
..author = json['author'] == null
? null
: GiteaUser.fromJson(json['author'] as Map<String, dynamic>)
..title = json['title'] as String?
..body = json['body'] as String?
..commit = json['commit'] == null
? null
: GiteaCommitDetail.fromJson(json['commit'] as Map<String, dynamic>)
..sha = json['sha'] as String?
..htmlUrl = json['html_url'] as String?;
Map<String, dynamic> _$GiteaCommitToJson(GiteaCommit instance) =>
<String, dynamic>{
'number': instance.number,
'author': instance.author,
'title': instance.title,
'body': instance.body,
'commit': instance.commit,
'sha': instance.sha,
'html_url': instance.htmlUrl,
};
GiteaCommitDetail _$GiteaCommitDetailFromJson(Map<String, dynamic> json) =>
GiteaCommitDetail()
..message = json['message'] as String?
..author = json['author'] == null
? null
: GiteaCommitAuthor.fromJson(json['author'] as Map<String, dynamic>)
..committer = json['committer'] == null
? null
: GiteaCommitAuthor.fromJson(
json['committer'] as Map<String, dynamic>);
Map<String, dynamic> _$GiteaCommitDetailToJson(GiteaCommitDetail instance) =>
<String, dynamic>{
'message': instance.message,
'author': instance.author,
'committer': instance.committer,
};
GiteaCommitAuthor _$GiteaCommitAuthorFromJson(Map<String, dynamic> json) =>
GiteaCommitAuthor()
..name = json['name'] as String?
..email = json['email'] as String?
..date =
json['date'] == null ? null : DateTime.parse(json['date'] as String);
Map<String, dynamic> _$GiteaCommitAuthorToJson(GiteaCommitAuthor instance) =>
<String, dynamic>{
'name': instance.name,
'email': instance.email,
'date': instance.date?.toIso8601String(),
};
GiteaIssue _$GiteaIssueFromJson(Map<String, dynamic> json) => GiteaIssue()
..title = json['title'] as String?
..body = json['body'] as String?
..number = json['number'] as int?
..user = json['user'] == null
? null
: GiteaUser.fromJson(json['user'] as Map<String, dynamic>)
..comments = json['comments'] as int?
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..state = json['state'] as String?
..htmlUrl = json['html_url'] as String?
..labels = (json['labels'] as List<dynamic>?)
?.map((e) => GiteaLabel.fromJson(e as Map<String, dynamic>))
.toList();
Map<String, dynamic> _$GiteaIssueToJson(GiteaIssue instance) =>
<String, dynamic>{
'title': instance.title,
'body': instance.body,
'number': instance.number,
'user': instance.user,
'comments': instance.comments,
'updated_at': instance.updatedAt?.toIso8601String(),
'state': instance.state,
'html_url': instance.htmlUrl,
'labels': instance.labels,
};
GiteaLabel _$GiteaLabelFromJson(Map<String, dynamic> json) => GiteaLabel()
..color = json['color'] as String?
..name = json['name'] as String?;
Map<String, dynamic> _$GiteaLabelToJson(GiteaLabel instance) =>
<String, dynamic>{
'color': instance.color,
'name': instance.name,
};
GiteaHeatmapItem _$GiteaHeatmapItemFromJson(Map<String, dynamic> json) =>
GiteaHeatmapItem()
..timestamp = json['timestamp'] as int?
..contributions = json['contributions'] as int?;
Map<String, dynamic> _$GiteaHeatmapItemToJson(GiteaHeatmapItem instance) =>
<String, dynamic>{
'timestamp': instance.timestamp,
'contributions': instance.contributions,
};
GiteaComment _$GiteaCommentFromJson(Map<String, dynamic> json) => GiteaComment()
..body = json['body'] as String?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..htmlUrl = json['html_url'] as String?
..originalAuthor = json['original_author'] as String?
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..id = json['id'] as int?
..user = json['user'] == null
? null
: GiteaUser.fromJson(json['user'] as Map<String, dynamic>);
Map<String, dynamic> _$GiteaCommentToJson(GiteaComment instance) =>
<String, dynamic>{
'body': instance.body,
'created_at': instance.createdAt?.toIso8601String(),
'html_url': instance.htmlUrl,
'original_author': instance.originalAuthor,
'updated_at': instance.updatedAt?.toIso8601String(),
'id': instance.id,
'user': instance.user,
};

View File

@ -4,6 +4,9 @@ part 'gitee.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeUser {
GiteeUser();
factory GiteeUser.fromJson(Map<String, dynamic> json) =>
_$GiteeUserFromJson(json);
String? login;
String? avatarUrl;
String? name;
@ -16,24 +19,24 @@ class GiteeUser {
int? stared;
int? watched;
DateTime? createdAt;
GiteeUser();
factory GiteeUser.fromJson(Map<String, dynamic> json) =>
_$GiteeUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeListUser {
GiteeListUser();
factory GiteeListUser.fromJson(Map<String, dynamic> json) =>
_$GiteeListUserFromJson(json);
String? login;
String? avatarUrl;
String? name;
String? htmlUrl;
GiteeListUser();
factory GiteeListUser.fromJson(Map<String, dynamic> json) =>
_$GiteeListUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeRepo {
GiteeRepo();
factory GiteeRepo.fromJson(Map<String, dynamic> json) =>
_$GiteeRepoFromJson(json);
GiteeRepoNamespace? namespace;
GiteeRepoOwner? owner;
String? path;
@ -51,90 +54,90 @@ class GiteeRepo {
int? openIssuesCount;
bool? pullRequestsEnabled;
String? defaultBranch;
GiteeRepo();
factory GiteeRepo.fromJson(Map<String, dynamic> json) =>
_$GiteeRepoFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeRepoOwner {
String? login;
String? avatarUrl;
GiteeRepoOwner();
factory GiteeRepoOwner.fromJson(Map<String, dynamic> json) =>
_$GiteeRepoOwnerFromJson(json);
String? login;
String? avatarUrl;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeRepoNamespace {
String? path;
GiteeRepoNamespace();
factory GiteeRepoNamespace.fromJson(Map<String, dynamic> json) =>
_$GiteeRepoNamespaceFromJson(json);
String? path;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeCommit {
GiteeCommit();
factory GiteeCommit.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitFromJson(json);
GiteeUser? author;
GiteeCommitDetail? commit;
String? sha;
String? htmlUrl;
List<GiteeCommitFile>? files;
GiteeCommit();
factory GiteeCommit.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeCommitDetail {
String? message;
GiteeCommitAuthor? author;
GiteeCommitAuthor? committer;
GiteeCommitDetail();
factory GiteeCommitDetail.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitDetailFromJson(json);
String? message;
GiteeCommitAuthor? author;
GiteeCommitAuthor? committer;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeCommitAuthor {
String? name;
String? email;
DateTime? date;
GiteeCommitAuthor();
factory GiteeCommitAuthor.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitAuthorFromJson(json);
String? name;
String? email;
DateTime? date;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeTreeItem {
GiteeTreeItem({required this.path, required this.type});
factory GiteeTreeItem.fromJson(Map<String, dynamic> json) =>
_$GiteeTreeItemFromJson(json);
String path;
String type;
String? sha;
int? size;
GiteeTreeItem({required this.path, required this.type});
factory GiteeTreeItem.fromJson(Map<String, dynamic> json) =>
_$GiteeTreeItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeBlob {
String? content;
GiteeBlob();
factory GiteeBlob.fromJson(Map<String, dynamic> json) =>
_$GiteeBlobFromJson(json);
String? content;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeLabel {
String? color;
String? name;
GiteeLabel();
factory GiteeLabel.fromJson(Map<String, dynamic> json) =>
_$GiteeLabelFromJson(json);
String? color;
String? name;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeIssue {
GiteeIssue();
factory GiteeIssue.fromJson(Map<String, dynamic> json) =>
_$GiteeIssueFromJson(json);
int? comments;
String? commentsUrl;
String? createdAt;
@ -149,13 +152,13 @@ class GiteeIssue {
String? number;
List<GiteeLabel>? labels;
int? id;
GiteeIssue();
factory GiteeIssue.fromJson(Map<String, dynamic> json) =>
_$GiteeIssueFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteePull {
GiteePull();
factory GiteePull.fromJson(Map<String, dynamic> json) =>
_$GiteePullFromJson(json);
String? commentsUrl;
String? createdAt;
String? htmlUrl;
@ -168,34 +171,34 @@ class GiteePull {
List<GiteeLabel>? labels;
int? number;
int? id;
GiteePull();
factory GiteePull.fromJson(Map<String, dynamic> json) =>
_$GiteePullFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeComment {
GiteeComment();
factory GiteeComment.fromJson(Map<String, dynamic> json) =>
_$GiteeCommentFromJson(json);
int? id;
String? body;
String? createdAt;
GiteeRepoOwner? user;
GiteeComment();
factory GiteeComment.fromJson(Map<String, dynamic> json) =>
_$GiteeCommentFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteePatch {
String? diff;
GiteePatch();
factory GiteePatch.fromJson(Map<String, dynamic> json) =>
_$GiteePatchFromJson(json);
String? diff;
}
// Two different classes because of variable type mismatch
// for additions, deletions, patch
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteePullFile {
GiteePullFile();
factory GiteePullFile.fromJson(Map<String, dynamic> json) =>
_$GiteePullFileFromJson(json);
String? additions;
String? deletions;
String? blobUrl;
@ -203,13 +206,13 @@ class GiteePullFile {
String? sha;
String? status;
GiteePatch? patch;
GiteePullFile();
factory GiteePullFile.fromJson(Map<String, dynamic> json) =>
_$GiteePullFileFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeCommitFile {
GiteeCommitFile();
factory GiteeCommitFile.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitFileFromJson(json);
int? additions;
int? deletions;
int? changes;
@ -218,24 +221,21 @@ class GiteeCommitFile {
String? sha;
String? status;
String? patch;
GiteeCommitFile();
factory GiteeCommitFile.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitFileFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeContributor {
String? name;
int? contributions;
GiteeContributor();
factory GiteeContributor.fromJson(Map<String, dynamic> json) =>
_$GiteeContributorFromJson(json);
String? name;
int? contributions;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeBranch {
String? name;
GiteeBranch();
factory GiteeBranch.fromJson(Map<String, dynamic> json) =>
_$GiteeBranchFromJson(json);
String? name;
}

366
lib/models/gitee.g.dart Normal file
View File

@ -0,0 +1,366 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'gitee.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
GiteeUser _$GiteeUserFromJson(Map<String, dynamic> json) => GiteeUser()
..login = json['login'] as String?
..avatarUrl = json['avatar_url'] as String?
..name = json['name'] as String?
..htmlUrl = json['html_url'] as String?
..bio = json['bio'] as String?
..blog = json['blog'] as String?
..publicRepos = json['public_repos'] as int?
..followers = json['followers'] as int?
..following = json['following'] as int?
..stared = json['stared'] as int?
..watched = json['watched'] as int?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String);
Map<String, dynamic> _$GiteeUserToJson(GiteeUser instance) => <String, dynamic>{
'login': instance.login,
'avatar_url': instance.avatarUrl,
'name': instance.name,
'html_url': instance.htmlUrl,
'bio': instance.bio,
'blog': instance.blog,
'public_repos': instance.publicRepos,
'followers': instance.followers,
'following': instance.following,
'stared': instance.stared,
'watched': instance.watched,
'created_at': instance.createdAt?.toIso8601String(),
};
GiteeListUser _$GiteeListUserFromJson(Map<String, dynamic> json) =>
GiteeListUser()
..login = json['login'] as String?
..avatarUrl = json['avatar_url'] as String?
..name = json['name'] as String?
..htmlUrl = json['html_url'] as String?;
Map<String, dynamic> _$GiteeListUserToJson(GiteeListUser instance) =>
<String, dynamic>{
'login': instance.login,
'avatar_url': instance.avatarUrl,
'name': instance.name,
'html_url': instance.htmlUrl,
};
GiteeRepo _$GiteeRepoFromJson(Map<String, dynamic> json) => GiteeRepo()
..namespace = json['namespace'] == null
? null
: GiteeRepoNamespace.fromJson(json['namespace'] as Map<String, dynamic>)
..owner = json['owner'] == null
? null
: GiteeRepoOwner.fromJson(json['owner'] as Map<String, dynamic>)
..path = json['path'] as String?
..description = json['description'] as String?
..private = json['private'] as bool?
..public = json['public'] as bool?
..internal = json['internal'] as bool?
..fork = json['fork'] as bool?
..forksCount = json['forks_count'] as int?
..stargazersCount = json['stargazers_count'] as int?
..watchersCount = json['watchers_count'] as int?
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..license = json['license'] as String?
..homepage = json['homepage'] as String?
..openIssuesCount = json['open_issues_count'] as int?
..pullRequestsEnabled = json['pull_requests_enabled'] as bool?
..defaultBranch = json['default_branch'] as String?;
Map<String, dynamic> _$GiteeRepoToJson(GiteeRepo instance) => <String, dynamic>{
'namespace': instance.namespace,
'owner': instance.owner,
'path': instance.path,
'description': instance.description,
'private': instance.private,
'public': instance.public,
'internal': instance.internal,
'fork': instance.fork,
'forks_count': instance.forksCount,
'stargazers_count': instance.stargazersCount,
'watchers_count': instance.watchersCount,
'updated_at': instance.updatedAt?.toIso8601String(),
'license': instance.license,
'homepage': instance.homepage,
'open_issues_count': instance.openIssuesCount,
'pull_requests_enabled': instance.pullRequestsEnabled,
'default_branch': instance.defaultBranch,
};
GiteeRepoOwner _$GiteeRepoOwnerFromJson(Map<String, dynamic> json) =>
GiteeRepoOwner()
..login = json['login'] as String?
..avatarUrl = json['avatar_url'] as String?;
Map<String, dynamic> _$GiteeRepoOwnerToJson(GiteeRepoOwner instance) =>
<String, dynamic>{
'login': instance.login,
'avatar_url': instance.avatarUrl,
};
GiteeRepoNamespace _$GiteeRepoNamespaceFromJson(Map<String, dynamic> json) =>
GiteeRepoNamespace()..path = json['path'] as String?;
Map<String, dynamic> _$GiteeRepoNamespaceToJson(GiteeRepoNamespace instance) =>
<String, dynamic>{
'path': instance.path,
};
GiteeCommit _$GiteeCommitFromJson(Map<String, dynamic> json) => GiteeCommit()
..author = json['author'] == null
? null
: GiteeUser.fromJson(json['author'] as Map<String, dynamic>)
..commit = json['commit'] == null
? null
: GiteeCommitDetail.fromJson(json['commit'] as Map<String, dynamic>)
..sha = json['sha'] as String?
..htmlUrl = json['html_url'] as String?
..files = (json['files'] as List<dynamic>?)
?.map((e) => GiteeCommitFile.fromJson(e as Map<String, dynamic>))
.toList();
Map<String, dynamic> _$GiteeCommitToJson(GiteeCommit instance) =>
<String, dynamic>{
'author': instance.author,
'commit': instance.commit,
'sha': instance.sha,
'html_url': instance.htmlUrl,
'files': instance.files,
};
GiteeCommitDetail _$GiteeCommitDetailFromJson(Map<String, dynamic> json) =>
GiteeCommitDetail()
..message = json['message'] as String?
..author = json['author'] == null
? null
: GiteeCommitAuthor.fromJson(json['author'] as Map<String, dynamic>)
..committer = json['committer'] == null
? null
: GiteeCommitAuthor.fromJson(
json['committer'] as Map<String, dynamic>);
Map<String, dynamic> _$GiteeCommitDetailToJson(GiteeCommitDetail instance) =>
<String, dynamic>{
'message': instance.message,
'author': instance.author,
'committer': instance.committer,
};
GiteeCommitAuthor _$GiteeCommitAuthorFromJson(Map<String, dynamic> json) =>
GiteeCommitAuthor()
..name = json['name'] as String?
..email = json['email'] as String?
..date =
json['date'] == null ? null : DateTime.parse(json['date'] as String);
Map<String, dynamic> _$GiteeCommitAuthorToJson(GiteeCommitAuthor instance) =>
<String, dynamic>{
'name': instance.name,
'email': instance.email,
'date': instance.date?.toIso8601String(),
};
GiteeTreeItem _$GiteeTreeItemFromJson(Map<String, dynamic> json) =>
GiteeTreeItem(
path: json['path'] as String,
type: json['type'] as String,
)
..sha = json['sha'] as String?
..size = json['size'] as int?;
Map<String, dynamic> _$GiteeTreeItemToJson(GiteeTreeItem instance) =>
<String, dynamic>{
'path': instance.path,
'type': instance.type,
'sha': instance.sha,
'size': instance.size,
};
GiteeBlob _$GiteeBlobFromJson(Map<String, dynamic> json) =>
GiteeBlob()..content = json['content'] as String?;
Map<String, dynamic> _$GiteeBlobToJson(GiteeBlob instance) => <String, dynamic>{
'content': instance.content,
};
GiteeLabel _$GiteeLabelFromJson(Map<String, dynamic> json) => GiteeLabel()
..color = json['color'] as String?
..name = json['name'] as String?;
Map<String, dynamic> _$GiteeLabelToJson(GiteeLabel instance) =>
<String, dynamic>{
'color': instance.color,
'name': instance.name,
};
GiteeIssue _$GiteeIssueFromJson(Map<String, dynamic> json) => GiteeIssue()
..comments = json['comments'] as int?
..commentsUrl = json['comments_url'] as String?
..createdAt = json['created_at'] as String?
..htmlUrl = json['html_url'] as String?
..updatedAt = json['updated_at'] as String?
..body = json['body'] as String?
..bodyHtml = json['body_html'] as String?
..title = json['title'] as String?
..state = json['state'] as String?
..repository = json['repository'] == null
? null
: GiteeRepo.fromJson(json['repository'] as Map<String, dynamic>)
..user = json['user'] == null
? null
: GiteeRepoOwner.fromJson(json['user'] as Map<String, dynamic>)
..number = json['number'] as String?
..labels = (json['labels'] as List<dynamic>?)
?.map((e) => GiteeLabel.fromJson(e as Map<String, dynamic>))
.toList()
..id = json['id'] as int?;
Map<String, dynamic> _$GiteeIssueToJson(GiteeIssue instance) =>
<String, dynamic>{
'comments': instance.comments,
'comments_url': instance.commentsUrl,
'created_at': instance.createdAt,
'html_url': instance.htmlUrl,
'updated_at': instance.updatedAt,
'body': instance.body,
'body_html': instance.bodyHtml,
'title': instance.title,
'state': instance.state,
'repository': instance.repository,
'user': instance.user,
'number': instance.number,
'labels': instance.labels,
'id': instance.id,
};
GiteePull _$GiteePullFromJson(Map<String, dynamic> json) => GiteePull()
..commentsUrl = json['comments_url'] as String?
..createdAt = json['created_at'] as String?
..htmlUrl = json['html_url'] as String?
..updatedAt = json['updated_at'] as String?
..body = json['body'] as String?
..bodyHtml = json['body_html'] as String?
..title = json['title'] as String?
..state = json['state'] as String?
..user = json['user'] == null
? null
: GiteeRepoOwner.fromJson(json['user'] as Map<String, dynamic>)
..labels = (json['labels'] as List<dynamic>?)
?.map((e) => GiteeLabel.fromJson(e as Map<String, dynamic>))
.toList()
..number = json['number'] as int?
..id = json['id'] as int?;
Map<String, dynamic> _$GiteePullToJson(GiteePull instance) => <String, dynamic>{
'comments_url': instance.commentsUrl,
'created_at': instance.createdAt,
'html_url': instance.htmlUrl,
'updated_at': instance.updatedAt,
'body': instance.body,
'body_html': instance.bodyHtml,
'title': instance.title,
'state': instance.state,
'user': instance.user,
'labels': instance.labels,
'number': instance.number,
'id': instance.id,
};
GiteeComment _$GiteeCommentFromJson(Map<String, dynamic> json) => GiteeComment()
..id = json['id'] as int?
..body = json['body'] as String?
..createdAt = json['created_at'] as String?
..user = json['user'] == null
? null
: GiteeRepoOwner.fromJson(json['user'] as Map<String, dynamic>);
Map<String, dynamic> _$GiteeCommentToJson(GiteeComment instance) =>
<String, dynamic>{
'id': instance.id,
'body': instance.body,
'created_at': instance.createdAt,
'user': instance.user,
};
GiteePatch _$GiteePatchFromJson(Map<String, dynamic> json) =>
GiteePatch()..diff = json['diff'] as String?;
Map<String, dynamic> _$GiteePatchToJson(GiteePatch instance) =>
<String, dynamic>{
'diff': instance.diff,
};
GiteePullFile _$GiteePullFileFromJson(Map<String, dynamic> json) =>
GiteePullFile()
..additions = json['additions'] as String?
..deletions = json['deletions'] as String?
..blobUrl = json['blob_url'] as String?
..filename = json['filename'] as String?
..sha = json['sha'] as String?
..status = json['status'] as String?
..patch = json['patch'] == null
? null
: GiteePatch.fromJson(json['patch'] as Map<String, dynamic>);
Map<String, dynamic> _$GiteePullFileToJson(GiteePullFile instance) =>
<String, dynamic>{
'additions': instance.additions,
'deletions': instance.deletions,
'blob_url': instance.blobUrl,
'filename': instance.filename,
'sha': instance.sha,
'status': instance.status,
'patch': instance.patch,
};
GiteeCommitFile _$GiteeCommitFileFromJson(Map<String, dynamic> json) =>
GiteeCommitFile()
..additions = json['additions'] as int?
..deletions = json['deletions'] as int?
..changes = json['changes'] as int?
..blobUrl = json['blob_url'] as String?
..filename = json['filename'] as String?
..sha = json['sha'] as String?
..status = json['status'] as String?
..patch = json['patch'] as String?;
Map<String, dynamic> _$GiteeCommitFileToJson(GiteeCommitFile instance) =>
<String, dynamic>{
'additions': instance.additions,
'deletions': instance.deletions,
'changes': instance.changes,
'blob_url': instance.blobUrl,
'filename': instance.filename,
'sha': instance.sha,
'status': instance.status,
'patch': instance.patch,
};
GiteeContributor _$GiteeContributorFromJson(Map<String, dynamic> json) =>
GiteeContributor()
..name = json['name'] as String?
..contributions = json['contributions'] as int?;
Map<String, dynamic> _$GiteeContributorToJson(GiteeContributor instance) =>
<String, dynamic>{
'name': instance.name,
'contributions': instance.contributions,
};
GiteeBranch _$GiteeBranchFromJson(Map<String, dynamic> json) =>
GiteeBranch()..name = json['name'] as String?;
Map<String, dynamic> _$GiteeBranchToJson(GiteeBranch instance) =>
<String, dynamic>{
'name': instance.name,
};

View File

@ -6,6 +6,10 @@ part 'github.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEvent {
GithubEvent();
factory GithubEvent.fromJson(Map<String, dynamic> json) =>
_$GithubEventFromJson(json);
GithubEventUser? actor;
String? type;
GithubEventRepo? repo;
@ -22,36 +26,33 @@ class GithubEvent {
_repo ??= parseRepositoryFullName(repo!.name!);
return _repo!.item2;
}
GithubEvent();
factory GithubEvent.fromJson(Map<String, dynamic> json) =>
_$GithubEventFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventUser {
String? login;
String? avatarUrl;
GithubEventUser();
factory GithubEventUser.fromJson(Map<String, dynamic> json) =>
_$GithubEventUserFromJson(json);
String? login;
String? avatarUrl;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventRepo {
String? name;
GithubEventRepo();
factory GithubEventRepo.fromJson(Map<String, dynamic> json) =>
_$GithubEventRepoFromJson(json);
String? name;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventPayload {
GithubEventPayload();
factory GithubEventPayload.fromJson(Map<String, dynamic> json) =>
_$GithubEventPayloadFromJson(json);
GithubEventIssue? issue;
GithubEventIssue? pullRequest;
GithubEventComment? comment;
@ -72,14 +73,14 @@ class GithubEventPayload {
GithubCheckrunItem? checkRun;
GithubCheckSuiteItem? checkSuite;
GithubContentReferenceItem? contentReference;
GithubEventPayload();
factory GithubEventPayload.fromJson(Map<String, dynamic> json) =>
_$GithubEventPayloadFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventIssue {
GithubEventIssue();
factory GithubEventIssue.fromJson(Map<String, dynamic> json) =>
_$GithubEventIssueFromJson(json);
String? title;
GithubEventUser? user;
int? number;
@ -91,51 +92,47 @@ class GithubEventIssue {
DateTime? createdAt;
bool get isPullRequestComment => pullRequest != null;
GithubEventIssue();
factory GithubEventIssue.fromJson(Map<String, dynamic> json) =>
_$GithubEventIssueFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventComment {
String? body;
GithubEventUser? user;
String? commitId;
String? htmlUrl;
GithubEventComment();
factory GithubEventComment.fromJson(Map<String, dynamic> json) =>
_$GithubEventCommentFromJson(json);
String? body;
GithubEventUser? user;
String? commitId;
String? htmlUrl;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventCommit {
String? sha;
String? message;
GithubEventCommit();
factory GithubEventCommit.fromJson(Map<String, dynamic> json) =>
_$GithubEventCommitFromJson(json);
String? sha;
String? message;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventRelease {
String? htmlUrl;
String? tagName;
GithubEventRelease();
factory GithubEventRelease.fromJson(Map<String, dynamic> json) =>
_$GithubEventReleaseFromJson(json);
String? htmlUrl;
String? tagName;
}
// Notification
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubNotificationItem {
GithubNotificationItem();
factory GithubNotificationItem.fromJson(Map<String, dynamic> json) =>
_$GithubNotificationItemFromJson(json);
String? id;
GithubNotificationItemSubject? subject;
DateTime? updatedAt;
@ -146,15 +143,14 @@ class GithubNotificationItem {
String? state;
String get key => '_$hashCode';
GithubNotificationItem();
factory GithubNotificationItem.fromJson(Map<String, dynamic> json) =>
_$GithubNotificationItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubNotificationItemSubject {
GithubNotificationItemSubject();
factory GithubNotificationItemSubject.fromJson(Map<String, dynamic> json) =>
_$GithubNotificationItemSubjectFromJson(json);
String? title;
String? type;
String? url;
@ -164,15 +160,14 @@ class GithubNotificationItemSubject {
_number ??= int.parse(url?.split('/').last ?? '0');
return _number;
}
GithubNotificationItemSubject();
factory GithubNotificationItemSubject.fromJson(Map<String, dynamic> json) =>
_$GithubNotificationItemSubjectFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubNotificationItemRepo {
GithubNotificationItemRepo();
factory GithubNotificationItemRepo.fromJson(Map<String, dynamic> json) =>
_$GithubNotificationItemRepoFromJson(json);
String? fullName;
Tuple2<String, String>? _repo;
@ -185,137 +180,132 @@ class GithubNotificationItemRepo {
_repo ??= parseRepositoryFullName(fullName!);
return _repo!.item2;
}
GithubNotificationItemRepo();
factory GithubNotificationItemRepo.fromJson(Map<String, dynamic> json) =>
_$GithubNotificationItemRepoFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubTreeItem {
GithubTreeItem();
factory GithubTreeItem.fromJson(Map<String, dynamic> json) =>
_$GithubTreeItemFromJson(json);
String? name;
String? path;
int? size;
String? type;
String? downloadUrl;
String? content;
GithubTreeItem();
factory GithubTreeItem.fromJson(Map<String, dynamic> json) =>
_$GithubTreeItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubPagesItem {
String? pageName;
String? title;
String? action;
GithubPagesItem();
factory GithubPagesItem.fromJson(Map<String, dynamic> json) =>
_$GithubPagesItemFromJson(json);
String? pageName;
String? title;
String? action;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubSecurityItem {
String? summary;
String? description;
String? severity;
GithubSecurityItem();
factory GithubSecurityItem.fromJson(Map<String, dynamic> json) =>
_$GithubSecurityItemFromJson(json);
String? summary;
String? description;
String? severity;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubAlertItem {
String? affectedPackageName;
String? affectedRange;
GithubAlertItem();
factory GithubAlertItem.fromJson(Map<String, dynamic> json) =>
_$GithubAlertItemFromJson(json);
String? affectedPackageName;
String? affectedRange;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubProjectItem {
GithubProjectItem();
factory GithubProjectItem.fromJson(Map<String, dynamic> json) =>
_$GithubProjectItemFromJson(json);
String? name;
String? state;
String? body;
String? htmlUrl;
GithubProjectItem();
factory GithubProjectItem.fromJson(Map<String, dynamic> json) =>
_$GithubProjectItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubProjectColumnItem {
String? htmlUrl;
String? columnsUrl;
String? name;
GithubProjectColumnItem();
factory GithubProjectColumnItem.fromJson(Map<String, dynamic> json) =>
_$GithubProjectColumnItemFromJson(json);
String? htmlUrl;
String? columnsUrl;
String? name;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubInstallationRepositoriesItem {
List<GithubNotificationItemRepo>? repositoriesAdded, repositoriesRemoved;
String? repositoriesSelection;
int? id;
GithubInstallationRepositoriesItem();
factory GithubInstallationRepositoriesItem.fromJson(
Map<String, dynamic> json) =>
_$GithubInstallationRepositoriesItemFromJson(json);
List<GithubNotificationItemRepo>? repositoriesAdded, repositoriesRemoved;
String? repositoriesSelection;
int? id;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubCheckrunItem {
String? status;
String? name;
int? id;
GithubCheckrunItem();
factory GithubCheckrunItem.fromJson(Map<String, dynamic> json) =>
_$GithubCheckrunItemFromJson(json);
String? status;
String? name;
int? id;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubCheckSuiteItem {
String? status;
String? conclusion;
GithubCheckSuiteItem();
factory GithubCheckSuiteItem.fromJson(Map<String, dynamic> json) =>
_$GithubCheckSuiteItemFromJson(json);
String? status;
String? conclusion;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubContentReferenceItem {
int? id;
String? reference;
GithubContentReferenceItem();
factory GithubContentReferenceItem.fromJson(Map<String, dynamic> json) =>
_$GithubContentReferenceItemFromJson(json);
int? id;
String? reference;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubContributorItem {
GithubContributorItem();
factory GithubContributorItem.fromJson(Map<String, dynamic> json) =>
_$GithubContributorItemFromJson(json);
int? id;
String? login;
String? avatarUrl;
String? htmlUrl;
int? contributions;
GithubContributorItem();
factory GithubContributorItem.fromJson(Map<String, dynamic> json) =>
_$GithubContributorItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubUserOrganizationItem {
GithubUserOrganizationItem();
factory GithubUserOrganizationItem.fromJson(Map<String, dynamic> json) =>
_$GithubUserOrganizationItemFromJson(json);
int? id;
String? login;
String? avatarUrl;
String? description;
String? url;
GithubUserOrganizationItem();
factory GithubUserOrganizationItem.fromJson(Map<String, dynamic> json) =>
_$GithubUserOrganizationItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
@ -329,6 +319,9 @@ class GistFiles {
this.truncated,
this.content,
});
factory GistFiles.fromJson(Map<String, dynamic> json) =>
_$GistFilesFromJson(json);
String? filename;
int? size;
String? rawUrl;
@ -336,21 +329,21 @@ class GistFiles {
String? language;
bool? truncated;
String? content;
factory GistFiles.fromJson(Map<String, dynamic> json) =>
_$GistFilesFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubGistsItem {
GithubGistsItem();
factory GithubGistsItem.fromJson(Map<String, dynamic> json) =>
_$GithubGistsItemFromJson(json);
String? id;
String? description;
bool? public;
Map<String, GistFiles>? files;
GithubEventUser? owner;
List<GistFiles> get fileNames {
List<GistFiles> filenames = [];
files!.forEach((String key, GistFiles value) {
final filenames = <GistFiles>[];
files!.forEach((key, value) {
filenames.add(value);
});
return filenames;
@ -358,32 +351,28 @@ class GithubGistsItem {
DateTime? createdAt;
DateTime? updatedAt;
GithubGistsItem();
factory GithubGistsItem.fromJson(Map<String, dynamic> json) =>
_$GithubGistsItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubFilesItem {
GithubFilesItem();
factory GithubFilesItem.fromJson(Map<String, dynamic> json) =>
_$GithubFilesItemFromJson(json);
String? filename;
String? status;
int? additions;
int? deletions;
int? changes;
String? patch;
GithubFilesItem();
factory GithubFilesItem.fromJson(Map<String, dynamic> json) =>
_$GithubFilesItemFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubComparisonItem {
GithubComparisonItem();
factory GithubComparisonItem.fromJson(Map<String, dynamic> json) =>
_$GithubComparisonItemFromJson(json);
List<GithubFilesItem>? files;
String? status;
int? aheadBy;
int? behindBy;
GithubComparisonItem();
factory GithubComparisonItem.fromJson(Map<String, dynamic> json) =>
_$GithubComparisonItemFromJson(json);
}

525
lib/models/github.g.dart Normal file
View File

@ -0,0 +1,525 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'github.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
GithubEvent _$GithubEventFromJson(Map<String, dynamic> json) => GithubEvent()
..actor = json['actor'] == null
? null
: GithubEventUser.fromJson(json['actor'] as Map<String, dynamic>)
..type = json['type'] as String?
..repo = json['repo'] == null
? null
: GithubEventRepo.fromJson(json['repo'] as Map<String, dynamic>)
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..payload = json['payload'] == null
? null
: GithubEventPayload.fromJson(json['payload'] as Map<String, dynamic>);
Map<String, dynamic> _$GithubEventToJson(GithubEvent instance) =>
<String, dynamic>{
'actor': instance.actor,
'type': instance.type,
'repo': instance.repo,
'created_at': instance.createdAt?.toIso8601String(),
'payload': instance.payload,
};
GithubEventUser _$GithubEventUserFromJson(Map<String, dynamic> json) =>
GithubEventUser()
..login = json['login'] as String?
..avatarUrl = json['avatar_url'] as String?;
Map<String, dynamic> _$GithubEventUserToJson(GithubEventUser instance) =>
<String, dynamic>{
'login': instance.login,
'avatar_url': instance.avatarUrl,
};
GithubEventRepo _$GithubEventRepoFromJson(Map<String, dynamic> json) =>
GithubEventRepo()..name = json['name'] as String?;
Map<String, dynamic> _$GithubEventRepoToJson(GithubEventRepo instance) =>
<String, dynamic>{
'name': instance.name,
};
GithubEventPayload _$GithubEventPayloadFromJson(Map<String, dynamic> json) =>
GithubEventPayload()
..issue = json['issue'] == null
? null
: GithubEventIssue.fromJson(json['issue'] as Map<String, dynamic>)
..pullRequest = json['pull_request'] == null
? null
: GithubEventIssue.fromJson(
json['pull_request'] as Map<String, dynamic>)
..comment = json['comment'] == null
? null
: GithubEventComment.fromJson(json['comment'] as Map<String, dynamic>)
..release = json['release'] == null
? null
: GithubEventRelease.fromJson(json['release'] as Map<String, dynamic>)
..action = json['action'] as String?
..ref = json['ref'] as String?
..refType = json['ref_type'] as String?
..before = json['before'] as String?
..head = json['head'] as String?
..commits = (json['commits'] as List<dynamic>?)
?.map((e) => GithubEventCommit.fromJson(e as Map<String, dynamic>))
.toList()
..forkee = json['forkee'] as Map<String, dynamic>?
..pages = (json['pages'] as List<dynamic>?)
?.map((e) => GithubPagesItem.fromJson(e as Map<String, dynamic>))
.toList()
..securityAdvisory = json['security_advisory'] == null
? null
: GithubSecurityItem.fromJson(
json['security_advisory'] as Map<String, dynamic>)
..alert = json['alert'] == null
? null
: GithubAlertItem.fromJson(json['alert'] as Map<String, dynamic>)
..project = json['project'] == null
? null
: GithubProjectItem.fromJson(json['project'] as Map<String, dynamic>)
..projectColumn = json['project_column'] == null
? null
: GithubProjectColumnItem.fromJson(
json['project_column'] as Map<String, dynamic>)
..installation = json['installation'] == null
? null
: GithubInstallationRepositoriesItem.fromJson(
json['installation'] as Map<String, dynamic>)
..checkRun = json['check_run'] == null
? null
: GithubCheckrunItem.fromJson(
json['check_run'] as Map<String, dynamic>)
..checkSuite = json['check_suite'] == null
? null
: GithubCheckSuiteItem.fromJson(
json['check_suite'] as Map<String, dynamic>)
..contentReference = json['content_reference'] == null
? null
: GithubContentReferenceItem.fromJson(
json['content_reference'] as Map<String, dynamic>);
Map<String, dynamic> _$GithubEventPayloadToJson(GithubEventPayload instance) =>
<String, dynamic>{
'issue': instance.issue,
'pull_request': instance.pullRequest,
'comment': instance.comment,
'release': instance.release,
'action': instance.action,
'ref': instance.ref,
'ref_type': instance.refType,
'before': instance.before,
'head': instance.head,
'commits': instance.commits,
'forkee': instance.forkee,
'pages': instance.pages,
'security_advisory': instance.securityAdvisory,
'alert': instance.alert,
'project': instance.project,
'project_column': instance.projectColumn,
'installation': instance.installation,
'check_run': instance.checkRun,
'check_suite': instance.checkSuite,
'content_reference': instance.contentReference,
};
GithubEventIssue _$GithubEventIssueFromJson(Map<String, dynamic> json) =>
GithubEventIssue()
..title = json['title'] as String?
..user = json['user'] == null
? null
: GithubEventUser.fromJson(json['user'] as Map<String, dynamic>)
..number = json['number'] as int?
..body = json['body'] as String?
..pullRequest = json['pull_request']
..state = json['state'] as String?
..comments = json['comments'] as int?
..merged = json['merged'] as bool?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String);
Map<String, dynamic> _$GithubEventIssueToJson(GithubEventIssue instance) =>
<String, dynamic>{
'title': instance.title,
'user': instance.user,
'number': instance.number,
'body': instance.body,
'pull_request': instance.pullRequest,
'state': instance.state,
'comments': instance.comments,
'merged': instance.merged,
'created_at': instance.createdAt?.toIso8601String(),
};
GithubEventComment _$GithubEventCommentFromJson(Map<String, dynamic> json) =>
GithubEventComment()
..body = json['body'] as String?
..user = json['user'] == null
? null
: GithubEventUser.fromJson(json['user'] as Map<String, dynamic>)
..commitId = json['commit_id'] as String?
..htmlUrl = json['html_url'] as String?;
Map<String, dynamic> _$GithubEventCommentToJson(GithubEventComment instance) =>
<String, dynamic>{
'body': instance.body,
'user': instance.user,
'commit_id': instance.commitId,
'html_url': instance.htmlUrl,
};
GithubEventCommit _$GithubEventCommitFromJson(Map<String, dynamic> json) =>
GithubEventCommit()
..sha = json['sha'] as String?
..message = json['message'] as String?;
Map<String, dynamic> _$GithubEventCommitToJson(GithubEventCommit instance) =>
<String, dynamic>{
'sha': instance.sha,
'message': instance.message,
};
GithubEventRelease _$GithubEventReleaseFromJson(Map<String, dynamic> json) =>
GithubEventRelease()
..htmlUrl = json['html_url'] as String?
..tagName = json['tag_name'] as String?;
Map<String, dynamic> _$GithubEventReleaseToJson(GithubEventRelease instance) =>
<String, dynamic>{
'html_url': instance.htmlUrl,
'tag_name': instance.tagName,
};
GithubNotificationItem _$GithubNotificationItemFromJson(
Map<String, dynamic> json) =>
GithubNotificationItem()
..id = json['id'] as String?
..subject = json['subject'] == null
? null
: GithubNotificationItemSubject.fromJson(
json['subject'] as Map<String, dynamic>)
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..repository = json['repository'] == null
? null
: GithubNotificationItemRepo.fromJson(
json['repository'] as Map<String, dynamic>)
..unread = json['unread'] as bool?;
Map<String, dynamic> _$GithubNotificationItemToJson(
GithubNotificationItem instance) =>
<String, dynamic>{
'id': instance.id,
'subject': instance.subject,
'updated_at': instance.updatedAt?.toIso8601String(),
'repository': instance.repository,
'unread': instance.unread,
};
GithubNotificationItemSubject _$GithubNotificationItemSubjectFromJson(
Map<String, dynamic> json) =>
GithubNotificationItemSubject()
..title = json['title'] as String?
..type = json['type'] as String?
..url = json['url'] as String?;
Map<String, dynamic> _$GithubNotificationItemSubjectToJson(
GithubNotificationItemSubject instance) =>
<String, dynamic>{
'title': instance.title,
'type': instance.type,
'url': instance.url,
};
GithubNotificationItemRepo _$GithubNotificationItemRepoFromJson(
Map<String, dynamic> json) =>
GithubNotificationItemRepo()..fullName = json['full_name'] as String?;
Map<String, dynamic> _$GithubNotificationItemRepoToJson(
GithubNotificationItemRepo instance) =>
<String, dynamic>{
'full_name': instance.fullName,
};
GithubTreeItem _$GithubTreeItemFromJson(Map<String, dynamic> json) =>
GithubTreeItem()
..name = json['name'] as String?
..path = json['path'] as String?
..size = json['size'] as int?
..type = json['type'] as String?
..downloadUrl = json['download_url'] as String?
..content = json['content'] as String?;
Map<String, dynamic> _$GithubTreeItemToJson(GithubTreeItem instance) =>
<String, dynamic>{
'name': instance.name,
'path': instance.path,
'size': instance.size,
'type': instance.type,
'download_url': instance.downloadUrl,
'content': instance.content,
};
GithubPagesItem _$GithubPagesItemFromJson(Map<String, dynamic> json) =>
GithubPagesItem()
..pageName = json['page_name'] as String?
..title = json['title'] as String?
..action = json['action'] as String?;
Map<String, dynamic> _$GithubPagesItemToJson(GithubPagesItem instance) =>
<String, dynamic>{
'page_name': instance.pageName,
'title': instance.title,
'action': instance.action,
};
GithubSecurityItem _$GithubSecurityItemFromJson(Map<String, dynamic> json) =>
GithubSecurityItem()
..summary = json['summary'] as String?
..description = json['description'] as String?
..severity = json['severity'] as String?;
Map<String, dynamic> _$GithubSecurityItemToJson(GithubSecurityItem instance) =>
<String, dynamic>{
'summary': instance.summary,
'description': instance.description,
'severity': instance.severity,
};
GithubAlertItem _$GithubAlertItemFromJson(Map<String, dynamic> json) =>
GithubAlertItem()
..affectedPackageName = json['affected_package_name'] as String?
..affectedRange = json['affected_range'] as String?;
Map<String, dynamic> _$GithubAlertItemToJson(GithubAlertItem instance) =>
<String, dynamic>{
'affected_package_name': instance.affectedPackageName,
'affected_range': instance.affectedRange,
};
GithubProjectItem _$GithubProjectItemFromJson(Map<String, dynamic> json) =>
GithubProjectItem()
..name = json['name'] as String?
..state = json['state'] as String?
..body = json['body'] as String?
..htmlUrl = json['html_url'] as String?;
Map<String, dynamic> _$GithubProjectItemToJson(GithubProjectItem instance) =>
<String, dynamic>{
'name': instance.name,
'state': instance.state,
'body': instance.body,
'html_url': instance.htmlUrl,
};
GithubProjectColumnItem _$GithubProjectColumnItemFromJson(
Map<String, dynamic> json) =>
GithubProjectColumnItem()
..htmlUrl = json['html_url'] as String?
..columnsUrl = json['columns_url'] as String?
..name = json['name'] as String?;
Map<String, dynamic> _$GithubProjectColumnItemToJson(
GithubProjectColumnItem instance) =>
<String, dynamic>{
'html_url': instance.htmlUrl,
'columns_url': instance.columnsUrl,
'name': instance.name,
};
GithubInstallationRepositoriesItem _$GithubInstallationRepositoriesItemFromJson(
Map<String, dynamic> json) =>
GithubInstallationRepositoriesItem()
..repositoriesAdded = (json['repositories_added'] as List<dynamic>?)
?.map((e) =>
GithubNotificationItemRepo.fromJson(e as Map<String, dynamic>))
.toList()
..repositoriesRemoved = (json['repositories_removed'] as List<dynamic>?)
?.map((e) =>
GithubNotificationItemRepo.fromJson(e as Map<String, dynamic>))
.toList()
..repositoriesSelection = json['repositories_selection'] as String?
..id = json['id'] as int?;
Map<String, dynamic> _$GithubInstallationRepositoriesItemToJson(
GithubInstallationRepositoriesItem instance) =>
<String, dynamic>{
'repositories_added': instance.repositoriesAdded,
'repositories_removed': instance.repositoriesRemoved,
'repositories_selection': instance.repositoriesSelection,
'id': instance.id,
};
GithubCheckrunItem _$GithubCheckrunItemFromJson(Map<String, dynamic> json) =>
GithubCheckrunItem()
..status = json['status'] as String?
..name = json['name'] as String?
..id = json['id'] as int?;
Map<String, dynamic> _$GithubCheckrunItemToJson(GithubCheckrunItem instance) =>
<String, dynamic>{
'status': instance.status,
'name': instance.name,
'id': instance.id,
};
GithubCheckSuiteItem _$GithubCheckSuiteItemFromJson(
Map<String, dynamic> json) =>
GithubCheckSuiteItem()
..status = json['status'] as String?
..conclusion = json['conclusion'] as String?;
Map<String, dynamic> _$GithubCheckSuiteItemToJson(
GithubCheckSuiteItem instance) =>
<String, dynamic>{
'status': instance.status,
'conclusion': instance.conclusion,
};
GithubContentReferenceItem _$GithubContentReferenceItemFromJson(
Map<String, dynamic> json) =>
GithubContentReferenceItem()
..id = json['id'] as int?
..reference = json['reference'] as String?;
Map<String, dynamic> _$GithubContentReferenceItemToJson(
GithubContentReferenceItem instance) =>
<String, dynamic>{
'id': instance.id,
'reference': instance.reference,
};
GithubContributorItem _$GithubContributorItemFromJson(
Map<String, dynamic> json) =>
GithubContributorItem()
..id = json['id'] as int?
..login = json['login'] as String?
..avatarUrl = json['avatar_url'] as String?
..htmlUrl = json['html_url'] as String?
..contributions = json['contributions'] as int?;
Map<String, dynamic> _$GithubContributorItemToJson(
GithubContributorItem instance) =>
<String, dynamic>{
'id': instance.id,
'login': instance.login,
'avatar_url': instance.avatarUrl,
'html_url': instance.htmlUrl,
'contributions': instance.contributions,
};
GithubUserOrganizationItem _$GithubUserOrganizationItemFromJson(
Map<String, dynamic> json) =>
GithubUserOrganizationItem()
..id = json['id'] as int?
..login = json['login'] as String?
..avatarUrl = json['avatar_url'] as String?
..description = json['description'] as String?
..url = json['url'] as String?;
Map<String, dynamic> _$GithubUserOrganizationItemToJson(
GithubUserOrganizationItem instance) =>
<String, dynamic>{
'id': instance.id,
'login': instance.login,
'avatar_url': instance.avatarUrl,
'description': instance.description,
'url': instance.url,
};
GistFiles _$GistFilesFromJson(Map<String, dynamic> json) => GistFiles(
filename: json['filename'] as String?,
size: json['size'] as int?,
rawUrl: json['raw_url'] as String?,
type: json['type'] as String?,
language: json['language'] as String?,
truncated: json['truncated'] as bool?,
content: json['content'] as String?,
);
Map<String, dynamic> _$GistFilesToJson(GistFiles instance) => <String, dynamic>{
'filename': instance.filename,
'size': instance.size,
'raw_url': instance.rawUrl,
'type': instance.type,
'language': instance.language,
'truncated': instance.truncated,
'content': instance.content,
};
GithubGistsItem _$GithubGistsItemFromJson(Map<String, dynamic> json) =>
GithubGistsItem()
..id = json['id'] as String?
..description = json['description'] as String?
..public = json['public'] as bool?
..files = (json['files'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, GistFiles.fromJson(e as Map<String, dynamic>)),
)
..owner = json['owner'] == null
? null
: GithubEventUser.fromJson(json['owner'] as Map<String, dynamic>)
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String);
Map<String, dynamic> _$GithubGistsItemToJson(GithubGistsItem instance) =>
<String, dynamic>{
'id': instance.id,
'description': instance.description,
'public': instance.public,
'files': instance.files,
'owner': instance.owner,
'created_at': instance.createdAt?.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
};
GithubFilesItem _$GithubFilesItemFromJson(Map<String, dynamic> json) =>
GithubFilesItem()
..filename = json['filename'] as String?
..status = json['status'] as String?
..additions = json['additions'] as int?
..deletions = json['deletions'] as int?
..changes = json['changes'] as int?
..patch = json['patch'] as String?;
Map<String, dynamic> _$GithubFilesItemToJson(GithubFilesItem instance) =>
<String, dynamic>{
'filename': instance.filename,
'status': instance.status,
'additions': instance.additions,
'deletions': instance.deletions,
'changes': instance.changes,
'patch': instance.patch,
};
GithubComparisonItem _$GithubComparisonItemFromJson(
Map<String, dynamic> json) =>
GithubComparisonItem()
..files = (json['files'] as List<dynamic>?)
?.map((e) => GithubFilesItem.fromJson(e as Map<String, dynamic>))
.toList()
..status = json['status'] as String?
..aheadBy = json['ahead_by'] as int?
..behindBy = json['behind_by'] as int?;
Map<String, dynamic> _$GithubComparisonItemToJson(
GithubComparisonItem instance) =>
<String, dynamic>{
'files': instance.files,
'status': instance.status,
'ahead_by': instance.aheadBy,
'behind_by': instance.behindBy,
};

View File

@ -4,6 +4,9 @@ part 'gitlab.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabUser {
GitlabUser();
factory GitlabUser.fromJson(Map<String, dynamic> json) =>
_$GitlabUserFromJson(json);
int? id;
String? username;
String? name;
@ -11,70 +14,70 @@ class GitlabUser {
String? bio;
DateTime? createdAt;
int? accessLevel;
GitlabUser();
factory GitlabUser.fromJson(Map<String, dynamic> json) =>
_$GitlabUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabGroup {
GitlabGroup();
factory GitlabGroup.fromJson(Map<String, dynamic> json) =>
_$GitlabGroupFromJson(json);
int? id;
String? path;
String? name;
String? avatarUrl;
String? description;
List<GitlabProject>? projects;
GitlabGroup();
factory GitlabGroup.fromJson(Map<String, dynamic> json) =>
_$GitlabGroupFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabTodoProject {
String? pathWithNamespace;
GitlabTodoProject();
factory GitlabTodoProject.fromJson(Map<String, dynamic> json) =>
_$GitlabTodoProjectFromJson(json);
String? pathWithNamespace;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabTodo {
GitlabTodo();
factory GitlabTodo.fromJson(Map<String, dynamic> json) =>
_$GitlabTodoFromJson(json);
GitlabUser? author;
GitlabTodoProject? project;
String? actionName;
String? targetType;
GitlabTodoTarget? target;
GitlabTodo();
factory GitlabTodo.fromJson(Map<String, dynamic> json) =>
_$GitlabTodoFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabTodoTarget {
GitlabTodoTarget();
factory GitlabTodoTarget.fromJson(Map<String, dynamic> json) =>
_$GitlabTodoTargetFromJson(json);
int? iid;
int? projectId;
String? title;
GitlabUser? author;
String? description;
DateTime? createdAt;
GitlabTodoTarget();
factory GitlabTodoTarget.fromJson(Map<String, dynamic> json) =>
_$GitlabTodoTargetFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabIssueNote {
GitlabIssueNote();
factory GitlabIssueNote.fromJson(Map<String, dynamic> json) =>
_$GitlabIssueNoteFromJson(json);
GitlabUser? author;
String? body;
bool? system;
DateTime? createdAt;
GitlabIssueNote();
factory GitlabIssueNote.fromJson(Map<String, dynamic> json) =>
_$GitlabIssueNoteFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabProject {
GitlabProject();
factory GitlabProject.fromJson(Map<String, dynamic> json) =>
_$GitlabProjectFromJson(json);
int? id;
String? name;
String? avatarUrl;
@ -93,103 +96,103 @@ class GitlabProject {
DateTime? lastActivityAt;
DateTime? createdAt;
String? defaultBranch;
GitlabProject();
factory GitlabProject.fromJson(Map<String, dynamic> json) =>
_$GitlabProjectFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabProjectBadge {
String? renderedImageUrl;
GitlabProjectBadge();
factory GitlabProjectBadge.fromJson(Map<String, dynamic> json) =>
_$GitlabProjectBadgeFromJson(json);
String? renderedImageUrl;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabProjectStatistics {
int? commitCount;
int? repositorySize;
GitlabProjectStatistics();
factory GitlabProjectStatistics.fromJson(Map<String, dynamic> json) =>
_$GitlabProjectStatisticsFromJson(json);
int? commitCount;
int? repositorySize;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabProjectNamespace {
GitlabProjectNamespace();
factory GitlabProjectNamespace.fromJson(Map<String, dynamic> json) =>
_$GitlabProjectNamespaceFromJson(json);
int? id;
String? name;
String? path;
String? kind;
GitlabProjectNamespace();
factory GitlabProjectNamespace.fromJson(Map<String, dynamic> json) =>
_$GitlabProjectNamespaceFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabTreeItem {
String type;
String path;
String name;
GitlabTreeItem({required this.type, required this.path, required this.name});
factory GitlabTreeItem.fromJson(Map<String, dynamic> json) =>
_$GitlabTreeItemFromJson(json);
String type;
String path;
String name;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabBlob {
String? content;
GitlabBlob();
factory GitlabBlob.fromJson(Map<String, dynamic> json) =>
_$GitlabBlobFromJson(json);
String? content;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabEvent {
GitlabEvent();
factory GitlabEvent.fromJson(Map<String, dynamic> json) =>
_$GitlabEventFromJson(json);
GitlabUser? author;
String? actionName;
String? targetType;
GitlabEventNote? note;
GitlabEvent();
factory GitlabEvent.fromJson(Map<String, dynamic> json) =>
_$GitlabEventFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabEventNote {
String? body;
String? noteableType;
int? noteableIid;
GitlabEventNote();
factory GitlabEventNote.fromJson(Map<String, dynamic> json) =>
_$GitlabEventNoteFromJson(json);
String? body;
String? noteableType;
int? noteableIid;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabCommit {
GitlabCommit();
factory GitlabCommit.fromJson(Map<String, dynamic> json) =>
_$GitlabCommitFromJson(json);
String? id;
String? shortId;
String? title;
DateTime? createdAt;
String? authorName;
String? message;
GitlabCommit();
factory GitlabCommit.fromJson(Map<String, dynamic> json) =>
_$GitlabCommitFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabDiff {
String? diff;
String? newPath;
String? oldPath;
GitlabDiff();
factory GitlabDiff.fromJson(Map<String, dynamic> json) =>
_$GitlabDiffFromJson(json);
String? diff;
String? newPath;
String? oldPath;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabIssue {
GitlabIssue();
factory GitlabIssue.fromJson(Map<String, dynamic> json) =>
_$GitlabIssueFromJson(json);
String? title;
int? iid;
int? projectId;
@ -197,25 +200,22 @@ class GitlabIssue {
int? userNotesCount;
DateTime? updatedAt;
List<String>? labels;
GitlabIssue();
factory GitlabIssue.fromJson(Map<String, dynamic> json) =>
_$GitlabIssueFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabStarrer {
DateTime? starredSince;
GitlabUser? user;
GitlabStarrer();
factory GitlabStarrer.fromJson(Map<String, dynamic> json) =>
_$GitlabStarrerFromJson(json);
DateTime? starredSince;
GitlabUser? user;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GitlabBranch {
String? name;
bool? merged;
GitlabBranch();
factory GitlabBranch.fromJson(Map<String, dynamic> json) =>
_$GitlabBranchFromJson(json);
String? name;
bool? merged;
}

351
lib/models/gitlab.g.dart Normal file
View File

@ -0,0 +1,351 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'gitlab.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
GitlabUser _$GitlabUserFromJson(Map<String, dynamic> json) => GitlabUser()
..id = json['id'] as int?
..username = json['username'] as String?
..name = json['name'] as String?
..avatarUrl = json['avatar_url'] as String?
..bio = json['bio'] as String?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..accessLevel = json['access_level'] as int?;
Map<String, dynamic> _$GitlabUserToJson(GitlabUser instance) =>
<String, dynamic>{
'id': instance.id,
'username': instance.username,
'name': instance.name,
'avatar_url': instance.avatarUrl,
'bio': instance.bio,
'created_at': instance.createdAt?.toIso8601String(),
'access_level': instance.accessLevel,
};
GitlabGroup _$GitlabGroupFromJson(Map<String, dynamic> json) => GitlabGroup()
..id = json['id'] as int?
..path = json['path'] as String?
..name = json['name'] as String?
..avatarUrl = json['avatar_url'] as String?
..description = json['description'] as String?
..projects = (json['projects'] as List<dynamic>?)
?.map((e) => GitlabProject.fromJson(e as Map<String, dynamic>))
.toList();
Map<String, dynamic> _$GitlabGroupToJson(GitlabGroup instance) =>
<String, dynamic>{
'id': instance.id,
'path': instance.path,
'name': instance.name,
'avatar_url': instance.avatarUrl,
'description': instance.description,
'projects': instance.projects,
};
GitlabTodoProject _$GitlabTodoProjectFromJson(Map<String, dynamic> json) =>
GitlabTodoProject()
..pathWithNamespace = json['path_with_namespace'] as String?;
Map<String, dynamic> _$GitlabTodoProjectToJson(GitlabTodoProject instance) =>
<String, dynamic>{
'path_with_namespace': instance.pathWithNamespace,
};
GitlabTodo _$GitlabTodoFromJson(Map<String, dynamic> json) => GitlabTodo()
..author = json['author'] == null
? null
: GitlabUser.fromJson(json['author'] as Map<String, dynamic>)
..project = json['project'] == null
? null
: GitlabTodoProject.fromJson(json['project'] as Map<String, dynamic>)
..actionName = json['action_name'] as String?
..targetType = json['target_type'] as String?
..target = json['target'] == null
? null
: GitlabTodoTarget.fromJson(json['target'] as Map<String, dynamic>);
Map<String, dynamic> _$GitlabTodoToJson(GitlabTodo instance) =>
<String, dynamic>{
'author': instance.author,
'project': instance.project,
'action_name': instance.actionName,
'target_type': instance.targetType,
'target': instance.target,
};
GitlabTodoTarget _$GitlabTodoTargetFromJson(Map<String, dynamic> json) =>
GitlabTodoTarget()
..iid = json['iid'] as int?
..projectId = json['project_id'] as int?
..title = json['title'] as String?
..author = json['author'] == null
? null
: GitlabUser.fromJson(json['author'] as Map<String, dynamic>)
..description = json['description'] as String?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String);
Map<String, dynamic> _$GitlabTodoTargetToJson(GitlabTodoTarget instance) =>
<String, dynamic>{
'iid': instance.iid,
'project_id': instance.projectId,
'title': instance.title,
'author': instance.author,
'description': instance.description,
'created_at': instance.createdAt?.toIso8601String(),
};
GitlabIssueNote _$GitlabIssueNoteFromJson(Map<String, dynamic> json) =>
GitlabIssueNote()
..author = json['author'] == null
? null
: GitlabUser.fromJson(json['author'] as Map<String, dynamic>)
..body = json['body'] as String?
..system = json['system'] as bool?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String);
Map<String, dynamic> _$GitlabIssueNoteToJson(GitlabIssueNote instance) =>
<String, dynamic>{
'author': instance.author,
'body': instance.body,
'system': instance.system,
'created_at': instance.createdAt?.toIso8601String(),
};
GitlabProject _$GitlabProjectFromJson(Map<String, dynamic> json) =>
GitlabProject()
..id = json['id'] as int?
..name = json['name'] as String?
..avatarUrl = json['avatar_url'] as String?
..description = json['description'] as String?
..starCount = json['star_count'] as int?
..forksCount = json['forks_count'] as int?
..visibility = json['visibility'] as String?
..readmeUrl = json['readme_url'] as String?
..webUrl = json['web_url'] as String?
..namespace = json['namespace'] == null
? null
: GitlabProjectNamespace.fromJson(
json['namespace'] as Map<String, dynamic>)
..owner = json['owner'] == null
? null
: GitlabUser.fromJson(json['owner'] as Map<String, dynamic>)
..issuesEnabled = json['issues_enabled'] as bool?
..openIssuesCount = json['open_issues_count'] as int?
..mergeRequestsEnabled = json['merge_requests_enabled'] as bool?
..statistics = json['statistics'] == null
? null
: GitlabProjectStatistics.fromJson(
json['statistics'] as Map<String, dynamic>)
..lastActivityAt = json['last_activity_at'] == null
? null
: DateTime.parse(json['last_activity_at'] as String)
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..defaultBranch = json['default_branch'] as String?;
Map<String, dynamic> _$GitlabProjectToJson(GitlabProject instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'avatar_url': instance.avatarUrl,
'description': instance.description,
'star_count': instance.starCount,
'forks_count': instance.forksCount,
'visibility': instance.visibility,
'readme_url': instance.readmeUrl,
'web_url': instance.webUrl,
'namespace': instance.namespace,
'owner': instance.owner,
'issues_enabled': instance.issuesEnabled,
'open_issues_count': instance.openIssuesCount,
'merge_requests_enabled': instance.mergeRequestsEnabled,
'statistics': instance.statistics,
'last_activity_at': instance.lastActivityAt?.toIso8601String(),
'created_at': instance.createdAt?.toIso8601String(),
'default_branch': instance.defaultBranch,
};
GitlabProjectBadge _$GitlabProjectBadgeFromJson(Map<String, dynamic> json) =>
GitlabProjectBadge()
..renderedImageUrl = json['rendered_image_url'] as String?;
Map<String, dynamic> _$GitlabProjectBadgeToJson(GitlabProjectBadge instance) =>
<String, dynamic>{
'rendered_image_url': instance.renderedImageUrl,
};
GitlabProjectStatistics _$GitlabProjectStatisticsFromJson(
Map<String, dynamic> json) =>
GitlabProjectStatistics()
..commitCount = json['commit_count'] as int?
..repositorySize = json['repository_size'] as int?;
Map<String, dynamic> _$GitlabProjectStatisticsToJson(
GitlabProjectStatistics instance) =>
<String, dynamic>{
'commit_count': instance.commitCount,
'repository_size': instance.repositorySize,
};
GitlabProjectNamespace _$GitlabProjectNamespaceFromJson(
Map<String, dynamic> json) =>
GitlabProjectNamespace()
..id = json['id'] as int?
..name = json['name'] as String?
..path = json['path'] as String?
..kind = json['kind'] as String?;
Map<String, dynamic> _$GitlabProjectNamespaceToJson(
GitlabProjectNamespace instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'path': instance.path,
'kind': instance.kind,
};
GitlabTreeItem _$GitlabTreeItemFromJson(Map<String, dynamic> json) =>
GitlabTreeItem(
type: json['type'] as String,
path: json['path'] as String,
name: json['name'] as String,
);
Map<String, dynamic> _$GitlabTreeItemToJson(GitlabTreeItem instance) =>
<String, dynamic>{
'type': instance.type,
'path': instance.path,
'name': instance.name,
};
GitlabBlob _$GitlabBlobFromJson(Map<String, dynamic> json) =>
GitlabBlob()..content = json['content'] as String?;
Map<String, dynamic> _$GitlabBlobToJson(GitlabBlob instance) =>
<String, dynamic>{
'content': instance.content,
};
GitlabEvent _$GitlabEventFromJson(Map<String, dynamic> json) => GitlabEvent()
..author = json['author'] == null
? null
: GitlabUser.fromJson(json['author'] as Map<String, dynamic>)
..actionName = json['action_name'] as String?
..targetType = json['target_type'] as String?
..note = json['note'] == null
? null
: GitlabEventNote.fromJson(json['note'] as Map<String, dynamic>);
Map<String, dynamic> _$GitlabEventToJson(GitlabEvent instance) =>
<String, dynamic>{
'author': instance.author,
'action_name': instance.actionName,
'target_type': instance.targetType,
'note': instance.note,
};
GitlabEventNote _$GitlabEventNoteFromJson(Map<String, dynamic> json) =>
GitlabEventNote()
..body = json['body'] as String?
..noteableType = json['noteable_type'] as String?
..noteableIid = json['noteable_iid'] as int?;
Map<String, dynamic> _$GitlabEventNoteToJson(GitlabEventNote instance) =>
<String, dynamic>{
'body': instance.body,
'noteable_type': instance.noteableType,
'noteable_iid': instance.noteableIid,
};
GitlabCommit _$GitlabCommitFromJson(Map<String, dynamic> json) => GitlabCommit()
..id = json['id'] as String?
..shortId = json['short_id'] as String?
..title = json['title'] as String?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..authorName = json['author_name'] as String?
..message = json['message'] as String?;
Map<String, dynamic> _$GitlabCommitToJson(GitlabCommit instance) =>
<String, dynamic>{
'id': instance.id,
'short_id': instance.shortId,
'title': instance.title,
'created_at': instance.createdAt?.toIso8601String(),
'author_name': instance.authorName,
'message': instance.message,
};
GitlabDiff _$GitlabDiffFromJson(Map<String, dynamic> json) => GitlabDiff()
..diff = json['diff'] as String?
..newPath = json['new_path'] as String?
..oldPath = json['old_path'] as String?;
Map<String, dynamic> _$GitlabDiffToJson(GitlabDiff instance) =>
<String, dynamic>{
'diff': instance.diff,
'new_path': instance.newPath,
'old_path': instance.oldPath,
};
GitlabIssue _$GitlabIssueFromJson(Map<String, dynamic> json) => GitlabIssue()
..title = json['title'] as String?
..iid = json['iid'] as int?
..projectId = json['project_id'] as int?
..author = json['author'] == null
? null
: GitlabUser.fromJson(json['author'] as Map<String, dynamic>)
..userNotesCount = json['user_notes_count'] as int?
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..labels =
(json['labels'] as List<dynamic>?)?.map((e) => e as String).toList();
Map<String, dynamic> _$GitlabIssueToJson(GitlabIssue instance) =>
<String, dynamic>{
'title': instance.title,
'iid': instance.iid,
'project_id': instance.projectId,
'author': instance.author,
'user_notes_count': instance.userNotesCount,
'updated_at': instance.updatedAt?.toIso8601String(),
'labels': instance.labels,
};
GitlabStarrer _$GitlabStarrerFromJson(Map<String, dynamic> json) =>
GitlabStarrer()
..starredSince = json['starred_since'] == null
? null
: DateTime.parse(json['starred_since'] as String)
..user = json['user'] == null
? null
: GitlabUser.fromJson(json['user'] as Map<String, dynamic>);
Map<String, dynamic> _$GitlabStarrerToJson(GitlabStarrer instance) =>
<String, dynamic>{
'starred_since': instance.starredSince?.toIso8601String(),
'user': instance.user,
};
GitlabBranch _$GitlabBranchFromJson(Map<String, dynamic> json) => GitlabBranch()
..name = json['name'] as String?
..merged = json['merged'] as bool?;
Map<String, dynamic> _$GitlabBranchToJson(GitlabBranch instance) =>
<String, dynamic>{
'name': instance.name,
'merged': instance.merged,
};

View File

@ -4,18 +4,21 @@ part 'gogs.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsUser {
GogsUser();
factory GogsUser.fromJson(Map<String, dynamic> json) =>
_$GogsUserFromJson(json);
int? id;
String? username;
String? fullName;
String? avatarUrl;
String? email;
GogsUser();
factory GogsUser.fromJson(Map<String, dynamic> json) =>
_$GogsUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsRepository {
GogsRepository();
factory GogsRepository.fromJson(Map<String, dynamic> json) =>
_$GogsRepositoryFromJson(json);
int? id;
String? fullName;
bool? private;
@ -29,13 +32,13 @@ class GogsRepository {
int? forksCount;
String? website;
int? watchersCount;
GogsRepository();
factory GogsRepository.fromJson(Map<String, dynamic> json) =>
_$GogsRepositoryFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsOrg {
GogsOrg();
factory GogsOrg.fromJson(Map<String, dynamic> json) =>
_$GogsOrgFromJson(json);
int? id;
String? username;
String? fullName;
@ -43,73 +46,72 @@ class GogsOrg {
String? description;
String? location;
String? website;
GogsOrg();
factory GogsOrg.fromJson(Map<String, dynamic> json) =>
_$GogsOrgFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsTree {
GogsTree({required this.type, required this.name});
factory GogsTree.fromJson(Map<String, dynamic> json) =>
_$GogsTreeFromJson(json);
String type;
String name;
String? path;
int? size;
String? downloadUrl;
GogsTree({required this.type, required this.name});
factory GogsTree.fromJson(Map<String, dynamic> json) =>
_$GogsTreeFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsBlob extends GogsTree {
String? content;
GogsBlob({required String type, required String name})
: super(name: name, type: type);
GogsBlob({required super.type, required super.name});
factory GogsBlob.fromJson(Map<String, dynamic> json) =>
_$GogsBlobFromJson(json);
String? content;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsBranch {
String? name;
GogsBranch();
factory GogsBranch.fromJson(Map<String, dynamic> json) =>
_$GogsBranchFromJson(json);
String? name;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsCommit {
GogsCommit();
factory GogsCommit.fromJson(Map<String, dynamic> json) =>
_$GogsCommitFromJson(json);
GogsUser? author;
GogsCommitDetail? commit;
String? sha;
String? htmlUrl;
GogsCommit();
factory GogsCommit.fromJson(Map<String, dynamic> json) =>
_$GogsCommitFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsCommitDetail {
String? message;
GogsCommitAuthor? author;
GogsCommitAuthor? committer;
GogsCommitDetail();
factory GogsCommitDetail.fromJson(Map<String, dynamic> json) =>
_$GogsCommitDetailFromJson(json);
String? message;
GogsCommitAuthor? author;
GogsCommitAuthor? committer;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsCommitAuthor {
String? name;
String? email;
DateTime? date;
GogsCommitAuthor();
factory GogsCommitAuthor.fromJson(Map<String, dynamic> json) =>
_$GogsCommitAuthorFromJson(json);
String? name;
String? email;
DateTime? date;
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsIssue {
GogsIssue();
factory GogsIssue.fromJson(Map<String, dynamic> json) =>
_$GogsIssueFromJson(json);
int? number;
String? state;
String? title;
@ -119,16 +121,13 @@ class GogsIssue {
DateTime? createdAt;
DateTime? updatedAt;
int? comments;
GogsIssue();
factory GogsIssue.fromJson(Map<String, dynamic> json) =>
_$GogsIssueFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GogsLabel {
String? name;
String? color;
GogsLabel();
factory GogsLabel.fromJson(Map<String, dynamic> json) =>
_$GogsLabelFromJson(json);
String? name;
String? color;
}

212
lib/models/gogs.g.dart Normal file
View File

@ -0,0 +1,212 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'gogs.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
GogsUser _$GogsUserFromJson(Map<String, dynamic> json) => GogsUser()
..id = json['id'] as int?
..username = json['username'] as String?
..fullName = json['full_name'] as String?
..avatarUrl = json['avatar_url'] as String?
..email = json['email'] as String?;
Map<String, dynamic> _$GogsUserToJson(GogsUser instance) => <String, dynamic>{
'id': instance.id,
'username': instance.username,
'full_name': instance.fullName,
'avatar_url': instance.avatarUrl,
'email': instance.email,
};
GogsRepository _$GogsRepositoryFromJson(Map<String, dynamic> json) =>
GogsRepository()
..id = json['id'] as int?
..fullName = json['full_name'] as String?
..private = json['private'] as bool?
..owner = json['owner'] == null
? null
: GogsUser.fromJson(json['owner'] as Map<String, dynamic>)
..htmlUrl = json['html_url'] as String?
..description = json['description'] as String?
..defaultBranch = json['default_branch'] as String?
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..starsCount = json['stars_count'] as int?
..forksCount = json['forks_count'] as int?
..website = json['website'] as String?
..watchersCount = json['watchers_count'] as int?;
Map<String, dynamic> _$GogsRepositoryToJson(GogsRepository instance) =>
<String, dynamic>{
'id': instance.id,
'full_name': instance.fullName,
'private': instance.private,
'owner': instance.owner,
'html_url': instance.htmlUrl,
'description': instance.description,
'default_branch': instance.defaultBranch,
'created_at': instance.createdAt?.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
'stars_count': instance.starsCount,
'forks_count': instance.forksCount,
'website': instance.website,
'watchers_count': instance.watchersCount,
};
GogsOrg _$GogsOrgFromJson(Map<String, dynamic> json) => GogsOrg()
..id = json['id'] as int?
..username = json['username'] as String?
..fullName = json['full_name'] as String?
..avatarUrl = json['avatar_url'] as String?
..description = json['description'] as String?
..location = json['location'] as String?
..website = json['website'] as String?;
Map<String, dynamic> _$GogsOrgToJson(GogsOrg instance) => <String, dynamic>{
'id': instance.id,
'username': instance.username,
'full_name': instance.fullName,
'avatar_url': instance.avatarUrl,
'description': instance.description,
'location': instance.location,
'website': instance.website,
};
GogsTree _$GogsTreeFromJson(Map<String, dynamic> json) => GogsTree(
type: json['type'] as String,
name: json['name'] as String,
)
..path = json['path'] as String?
..size = json['size'] as int?
..downloadUrl = json['download_url'] as String?;
Map<String, dynamic> _$GogsTreeToJson(GogsTree instance) => <String, dynamic>{
'type': instance.type,
'name': instance.name,
'path': instance.path,
'size': instance.size,
'download_url': instance.downloadUrl,
};
GogsBlob _$GogsBlobFromJson(Map<String, dynamic> json) => GogsBlob(
type: json['type'] as String,
name: json['name'] as String,
)
..path = json['path'] as String?
..size = json['size'] as int?
..downloadUrl = json['download_url'] as String?
..content = json['content'] as String?;
Map<String, dynamic> _$GogsBlobToJson(GogsBlob instance) => <String, dynamic>{
'type': instance.type,
'name': instance.name,
'path': instance.path,
'size': instance.size,
'download_url': instance.downloadUrl,
'content': instance.content,
};
GogsBranch _$GogsBranchFromJson(Map<String, dynamic> json) =>
GogsBranch()..name = json['name'] as String?;
Map<String, dynamic> _$GogsBranchToJson(GogsBranch instance) =>
<String, dynamic>{
'name': instance.name,
};
GogsCommit _$GogsCommitFromJson(Map<String, dynamic> json) => GogsCommit()
..author = json['author'] == null
? null
: GogsUser.fromJson(json['author'] as Map<String, dynamic>)
..commit = json['commit'] == null
? null
: GogsCommitDetail.fromJson(json['commit'] as Map<String, dynamic>)
..sha = json['sha'] as String?
..htmlUrl = json['html_url'] as String?;
Map<String, dynamic> _$GogsCommitToJson(GogsCommit instance) =>
<String, dynamic>{
'author': instance.author,
'commit': instance.commit,
'sha': instance.sha,
'html_url': instance.htmlUrl,
};
GogsCommitDetail _$GogsCommitDetailFromJson(Map<String, dynamic> json) =>
GogsCommitDetail()
..message = json['message'] as String?
..author = json['author'] == null
? null
: GogsCommitAuthor.fromJson(json['author'] as Map<String, dynamic>)
..committer = json['committer'] == null
? null
: GogsCommitAuthor.fromJson(
json['committer'] as Map<String, dynamic>);
Map<String, dynamic> _$GogsCommitDetailToJson(GogsCommitDetail instance) =>
<String, dynamic>{
'message': instance.message,
'author': instance.author,
'committer': instance.committer,
};
GogsCommitAuthor _$GogsCommitAuthorFromJson(Map<String, dynamic> json) =>
GogsCommitAuthor()
..name = json['name'] as String?
..email = json['email'] as String?
..date =
json['date'] == null ? null : DateTime.parse(json['date'] as String);
Map<String, dynamic> _$GogsCommitAuthorToJson(GogsCommitAuthor instance) =>
<String, dynamic>{
'name': instance.name,
'email': instance.email,
'date': instance.date?.toIso8601String(),
};
GogsIssue _$GogsIssueFromJson(Map<String, dynamic> json) => GogsIssue()
..number = json['number'] as int?
..state = json['state'] as String?
..title = json['title'] as String?
..body = json['body'] as String?
..user = json['user'] == null
? null
: GogsUser.fromJson(json['user'] as Map<String, dynamic>)
..labels = (json['labels'] as List<dynamic>?)
?.map((e) => GogsLabel.fromJson(e as Map<String, dynamic>))
.toList()
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..comments = json['comments'] as int?;
Map<String, dynamic> _$GogsIssueToJson(GogsIssue instance) => <String, dynamic>{
'number': instance.number,
'state': instance.state,
'title': instance.title,
'body': instance.body,
'user': instance.user,
'labels': instance.labels,
'created_at': instance.createdAt?.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
'comments': instance.comments,
};
GogsLabel _$GogsLabelFromJson(Map<String, dynamic> json) => GogsLabel()
..name = json['name'] as String?
..color = json['color'] as String?;
Map<String, dynamic> _$GogsLabelToJson(GogsLabel instance) => <String, dynamic>{
'name': instance.name,
'color': instance.color,
};

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:tuple/tuple.dart';
import '../utils/utils.dart';
class NotificationGroup {
NotificationGroup(this.fullName);
String? fullName;
List<GithubNotificationItem> items = [];
@ -20,8 +20,6 @@ class NotificationGroup {
}
String get key => '_$hashCode';
NotificationGroup(this.fullName);
}
class NotificationModel with ChangeNotifier {

View File

@ -1,23 +1,15 @@
import 'dart:async';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:go_router/go_router.dart';
import 'package:primer/primer.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:universal_io/io.dart';
class AppThemeType {
static const material = 0;
static const cupertino = 1;
static const values = [AppThemeType.material, AppThemeType.cupertino];
}
class AppBrightnessType {
static const followSystem = 0;
static const light = 1;
@ -36,77 +28,48 @@ class AppMarkdownType {
}
class PickerItem<T> {
PickerItem(this.value, {required this.text});
final T value;
final String? text;
PickerItem(this.value, {required this.text});
}
class PickerGroupItem<T> {
final T value;
final List<PickerItem<T>> items;
final Function(T value)? onChange;
final Function(T value)? onClose;
PickerGroupItem({
required this.value,
required this.items,
this.onChange,
this.onClose,
});
final T value;
final List<PickerItem<T>> items;
final Function(T value)? onChange;
final Function(T value)? onClose;
}
class SelectorItem<T> {
SelectorItem({required this.value, required this.text});
T value;
String text;
SelectorItem({required this.value, required this.text});
}
// No animation. For replacing route
// TODO: Go back
class StaticRoute extends PageRouteBuilder {
final WidgetBuilder? builder;
StaticRoute({this.builder})
: super(
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
pageBuilder: (context, animation, secondaryAnimation) {
return builder!(context);
},
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return child;
},
);
}
class Palette {
final Color primary;
final Color text;
final Color secondaryText;
final Color tertiaryText;
final Color background;
final Color grayBackground;
final Color border;
const Palette({
required this.primary,
required this.text,
required this.secondaryText,
required this.tertiaryText,
required this.background,
required this.grayBackground,
required this.border,
});
final WidgetBuilder? builder;
}
class ThemeModel with ChangeNotifier {
String? markdownCss;
int? _theme;
int? get theme => _theme;
bool get ready => _theme != null;
Brightness systemBrightness = Brightness.light;
void setSystemBrightness(Brightness v) {
if (v != systemBrightness) {
@ -178,49 +141,11 @@ class ThemeModel with ChangeNotifier {
notifyListeners();
}
final paletteLight = Palette(
primary: PrimerColors.blue500,
text: Colors.black,
secondaryText: Colors.grey.shade800,
tertiaryText: Colors.grey.shade600,
background: Colors.white,
grayBackground: Colors.grey.shade100,
border: Colors.grey.shade300,
);
final paletteDark = Palette(
primary: PrimerColors.blue500,
text: Colors.grey.shade300,
secondaryText: Colors.grey.shade400,
tertiaryText: Colors.grey.shade500,
background: Colors.black,
grayBackground: Colors.grey.shade900,
border: Colors.grey.shade700,
);
Palette get palette {
switch (brightness) {
case Brightness.dark:
return paletteDark;
case Brightness.light:
default:
return paletteLight;
}
}
Future<void> init() async {
markdownCss = await rootBundle.loadString('images/github-markdown.css');
final prefs = await SharedPreferences.getInstance();
final v = prefs.getInt(StorageKeys.iTheme);
Fimber.d('read theme: $v');
if (AppThemeType.values.contains(v)) {
_theme = v;
} else if (Platform.isIOS || Platform.isMacOS) {
_theme = AppThemeType.cupertino;
} else {
_theme = AppThemeType.material;
}
final b = prefs.getInt(StorageKeys.iBrightness);
Fimber.d('read brightness: $b');
if (AppBrightnessType.values.contains(b)) {
@ -238,46 +163,6 @@ class ThemeModel with ChangeNotifier {
notifyListeners();
}
Future<void> setTheme(int v) async {
_theme = v;
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(StorageKeys.iTheme, v);
Fimber.d('write theme: $v');
notifyListeners();
}
push(BuildContext context, String url, {bool replace = false}) {
// Fimber.d(url);
if (url.startsWith('/')) {
if (replace) {
context.replace(url);
} else {
context.push(url);
}
} else {
launchStringUrl(url);
}
}
Future<void> showWarning(BuildContext context, String message) async {
showCupertinoDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text(message),
actions: <Widget>[
CupertinoDialogAction(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
},
);
}
Future<bool?> showConfirm(BuildContext context, Widget content) {
return showCupertinoDialog(
context: context,
@ -302,37 +187,15 @@ class ThemeModel with ChangeNotifier {
);
},
);
// default:
// return showDialog(
// context: context,
// builder: (BuildContext context) {
// return AlertDialog(
// content: content,
// actions: <Widget>[
// FlatButton(
// child: const Text('CANCEL'),
// onPressed: () {
// Navigator.pop(context, false);
// },
// ),
// FlatButton(
// child: const Text('OK'),
// onPressed: () {
// Navigator.pop(context, true);
// },
// )
// ],
// );
// },
// );
}
static Timer? _debounce;
String? _selectedItem;
showPicker(BuildContext context, PickerGroupItem<String?> groupItem) async {
await showCupertinoModalPopup(
await AntPopup.show(
context: context,
closeOnMaskClick: true,
builder: (context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
@ -340,10 +203,10 @@ class ThemeModel with ChangeNotifier {
Container(
alignment: Alignment.bottomCenter,
decoration: BoxDecoration(
color: palette.background,
color: AntTheme.of(context).colorBackground,
border: Border(
bottom: BorderSide(
color: palette.grayBackground,
color: AntTheme.of(context).colorBox,
width: 0.0,
),
),
@ -379,7 +242,7 @@ class ThemeModel with ChangeNotifier {
SizedBox(
height: 216,
child: CupertinoPicker(
backgroundColor: palette.background,
backgroundColor: AntTheme.of(context).colorBackground,
itemExtent: 40,
scrollController: FixedExtentScrollController(
initialItem: groupItem.items
@ -400,7 +263,9 @@ class ThemeModel with ChangeNotifier {
},
children: <Widget>[
for (var v in groupItem.items)
Text(v.text!, style: TextStyle(color: palette.text)),
Text(v.text!,
style:
TextStyle(color: AntTheme.of(context).colorText)),
],
),
)
@ -411,33 +276,23 @@ class ThemeModel with ChangeNotifier {
}
showActions(BuildContext context, List<ActionItem> actionItems) async {
final value = await showCupertinoModalPopup<int>(
await AntActionSheet.show(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: const Text('Actions'),
actions: actionItems.asMap().entries.map((entry) {
return CupertinoActionSheetAction(
isDestructiveAction: entry.value.isDestructiveAction,
onPressed: () {
Navigator.pop(context, entry.key);
},
child: Text(entry.value.text!),
);
}).toList(),
cancelButton: CupertinoActionSheetAction(
isDefaultAction: true,
onPressed: () {
Navigator.pop(context);
extra: const Text('Actions'),
actions: [
for (final item in actionItems)
AntActionSheetAction(
text: Text(
item.text!,
style: TextStyle(
color: item.danger ? AntTheme.of(context).colorDanger : null),
),
onClick: () {
item.onTap?.call(context);
},
child: const Text('Cancel'),
key: null,
),
);
},
],
);
if (value != null) {
actionItems[value].onTap!(context);
}
}
}

View File

@ -36,9 +36,8 @@ import 'package:git_touch/screens/gh_gists_files.dart';
import 'package:git_touch/screens/gh_issue.dart';
import 'package:git_touch/screens/gh_issue_form.dart';
import 'package:git_touch/screens/gh_issues.dart';
import 'package:git_touch/screens/gh_meta.dart';
import 'package:git_touch/screens/gh_object.dart';
import 'package:git_touch/screens/gh_org_repos.dart';
import 'package:git_touch/screens/gh_orgs.dart';
import 'package:git_touch/screens/gh_pulls.dart';
import 'package:git_touch/screens/gh_releases.dart';
import 'package:git_touch/screens/gh_repo.dart';
@ -91,15 +90,21 @@ final router = GoRouter(
// common
GoRoute(
path: 'choose-code-theme',
builder: (context, state) => LoginScreen(),
builder: (context, state) => CodeThemeScreen(),
),
GoRoute(
path: 'login',
builder: (context, state) => CodeThemeScreen(),
builder: (context, state) => LoginScreen(),
),
GoRoute(
path: 'settings',
builder: (context, state) => SettingsScreen(),
routes: [
GoRoute(
path: 'github-meta',
builder: (context, state) => const GhMetaScreen(),
),
],
),
// github
@ -123,16 +128,14 @@ final router = GoRouter(
return GhStars(login);
case 'repositories':
return GhRepos(login);
case 'orgrepo':
return GhOrgReposScreen(login);
case 'organizations':
return GhUserOrganizationScreen(login);
return GhOrgs(login);
case 'gists':
return GhGistsScreen(login);
case 'events':
return GhEventsScreen(login);
default:
return GhUser(login);
return GhUserScreen(login);
}
},
routes: [
@ -176,7 +179,7 @@ final router = GoRouter(
GoRoute(
path: 'watchers',
builder: (context, state) =>
GhWachers(state.params['owner']!, state.params['name']!),
GhWatchers(state.params['owner']!, state.params['name']!),
),
GoRoute(
path: 'contributors',
@ -225,6 +228,13 @@ final router = GoRouter(
state.params['name']!,
),
routes: [
GoRoute(
path: 'new',
builder: (context, state) => GhIssueFormScreen(
state.params['owner']!,
state.params['name']!,
),
),
GoRoute(
path: ':number',
builder: (context, state) => GhIssueScreen(
@ -233,13 +243,6 @@ final router = GoRouter(
int.parse(state.params['number']!),
),
),
GoRoute(
path: 'new',
builder: (context, state) => GhIssueFormScreen(
state.params['owner']!,
state.params['name']!,
),
),
],
),
GoRoute(
@ -248,29 +251,27 @@ final router = GoRouter(
state.params['owner']!,
state.params['name']!,
),
),
GoRoute(
path: 'pull/:number',
builder: (context, state) => GhIssueScreen(
state.params['owner']!,
state.params['name']!,
int.parse(state.params['number']!),
),
routes: [
GoRoute(
path: ':number',
builder: (context, state) => GhIssueScreen(
state.params['owner']!,
state.params['name']!,
int.parse(state.params['number']!),
),
routes: [
GoRoute(
path: 'files',
builder: (context, state) {
return GhFilesScreen(
state.params['owner']!,
state.params['name']!,
int.parse(state.params['number']!),
);
},
),
],
path: 'files',
builder: (context, state) {
return GhFilesScreen(
state.params['owner']!,
state.params['name']!,
int.parse(state.params['number']!),
);
},
),
],
)
),
],
),
],
@ -351,6 +352,12 @@ final router = GoRouter(
prefix: state.queryParams['prefix'],
),
routes: [
GoRoute(
path: 'new',
builder: (context, state) => GlIssueFormScreen(
int.parse(state.params['id']!),
),
),
GoRoute(
path: ':iid',
builder: (context, state) => GlIssueScreen(
@ -358,12 +365,6 @@ final router = GoRouter(
int.parse(state.params['iid']!),
),
),
GoRoute(
path: 'new',
builder: (context, state) => GlIssueFormScreen(
int.parse(state.params['id']!),
),
),
]),
GoRoute(
path: 'merge_requests',

View File

@ -1,20 +1,16 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/theme.dart';
import 'package:provider/provider.dart';
class CommonScaffold extends StatelessWidget {
final Widget title;
final Widget body;
final Widget? action;
final PreferredSizeWidget? bottom;
const CommonScaffold({
required this.title,
required this.body,
this.action,
this.bottom,
});
final Widget title;
final Widget body;
final Widget? action;
@override
Widget build(BuildContext context) {
@ -22,26 +18,12 @@ class CommonScaffold extends StatelessWidget {
// FIXME: A hack to get brightness before MaterialApp been built
theme.setSystemBrightness(MediaQuery.of(context).platformBrightness);
switch (theme.theme) {
case AppThemeType.cupertino:
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: title,
trailing: action,
),
child: SafeArea(child: body),
);
default:
return Scaffold(
appBar: AppBar(
title: title,
actions: [
if (action != null) action!,
],
bottom: bottom,
),
body: body,
);
}
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: title,
trailing: action,
),
child: SafeArea(child: body),
);
}
}

View File

@ -1,30 +1,28 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/empty.dart';
import 'package:git_touch/widgets/error_reload.dart';
import 'package:git_touch/widgets/loading.dart';
import 'package:git_touch/widgets/empty.dart';
export 'package:git_touch/utils/utils.dart';
// This is a scaffold for infinite scroll screens
class ListStatefulScaffold<T, K> extends StatefulWidget {
final Widget title;
final Widget Function()? actionBuilder;
final Widget Function(T payload) itemBuilder;
final Future<ListPayload<T, K>> Function(K? cursor) fetch;
const ListStatefulScaffold({
required this.title,
required this.fetch,
required this.itemBuilder,
this.actionBuilder,
});
final Widget title;
final Widget Function()? actionBuilder;
final Widget Function(T payload) itemBuilder;
final Future<ListPayload<T, K>> Function(K? cursor) fetch;
@override
_ListStatefulScaffoldState<T, K> createState() =>
State<ListStatefulScaffold<T, K>> createState() =>
_ListStatefulScaffoldState();
}
@ -68,7 +66,7 @@ class _ListStatefulScaffoldState<T, K>
loading = true;
});
try {
final ListPayload<T, K> p = await widget.fetch(null);
final p = await widget.fetch(null);
items = p.items.toList();
cursor = p.cursor;
hasMore = p.hasMore;
@ -90,7 +88,7 @@ class _ListStatefulScaffoldState<T, K>
loadingMore = true;
});
try {
ListPayload<T, K> p = await widget.fetch(cursor);
final p = await widget.fetch(cursor);
items.addAll(p.items);
cursor = p.cursor;
hasMore = p.hasMore;
@ -106,20 +104,6 @@ class _ListStatefulScaffoldState<T, K>
}
}
Widget _buildItem(BuildContext context, int index) {
if (index == 2 * items.length) {
if (hasMore != false) {
return const Loading(more: true);
} else {
return Container();
}
} else if (index % 2 == 1) {
return CommonStyle.border;
} else {
return widget.itemBuilder(items[index ~/ 2]);
}
}
Widget _buildCupertinoSliver() {
if (error.isNotEmpty) {
return SliverToBoxAdapter(
@ -130,58 +114,35 @@ class _ListStatefulScaffoldState<T, K>
} else if (items.isEmpty) {
return SliverToBoxAdapter(child: EmptyWidget());
} else {
return SliverList(
delegate: SliverChildBuilderDelegate(
_buildItem,
childCount: 2 * items.length + 1,
),
return AntSliverList(
count: items.length + 1,
itemBuilder: (context, index) {
if (index == items.length) {
if (hasMore != false) {
return const Loading(more: true);
} else {
return Container();
}
}
return widget.itemBuilder(items[index]);
},
);
}
}
Widget _buildMaterial() {
if (error.isNotEmpty) {
return ErrorReload(text: error, onTap: _refresh);
} else if (loading && items.isEmpty) {
return const Loading(more: false);
} else if (items.isEmpty) {
return EmptyWidget();
} else {
return Scrollbar(
child: ListView.builder(
controller: _controller,
itemCount: 2 * items.length + 1,
itemBuilder: _buildItem,
),
);
}
}
Widget _buildBody() {
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
return CupertinoScrollbar(
child: CustomScrollView(
controller: _controller,
slivers: [
CupertinoSliverRefreshControl(onRefresh: _refresh),
_buildCupertinoSliver(),
],
),
);
default:
return RefreshIndicator(
onRefresh: _refresh,
child: _buildMaterial(),
);
}
}
@override
Widget build(BuildContext context) {
return CommonScaffold(
title: widget.title,
body: _buildBody(),
body: CupertinoScrollbar(
child: CustomScrollView(
controller: _controller,
slivers: [
CupertinoSliverRefreshControl(onRefresh: _refresh),
_buildCupertinoSliver(),
],
),
),
action: widget.actionBuilder?.call(),
);
}

View File

@ -1,19 +1,11 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import '../widgets/loading.dart';
import '../widgets/link.dart';
import '../widgets/error_reload.dart';
import 'package:git_touch/widgets/error_reload.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:git_touch/widgets/loading.dart';
class LongListPayload<T, K> {
T header;
int totalCount;
String? cursor;
List<K> leadingItems;
List<K>? trailingItems;
LongListPayload({
required this.header,
required this.totalCount,
@ -21,6 +13,11 @@ class LongListPayload<T, K> {
required this.leadingItems,
this.trailingItems,
});
T header;
int totalCount;
String? cursor;
List<K> leadingItems;
List<K>? trailingItems;
}
// This is a scaffold for issue and pull request
@ -28,13 +25,6 @@ class LongListPayload<T, K> {
// We should load leading and trailing items at first fetching, and do load more in the middle
// e.g. https://github.com/reactjs/rfcs/pull/68
class LongListStatefulScaffold<T, K> extends StatefulWidget {
final Widget title;
final Widget Function(T t)? trailingBuilder;
final Widget Function(T t) headerBuilder;
final Widget Function(K k) itemBuilder;
final Future<LongListPayload<T, K>> Function() onRefresh;
final Future<LongListPayload<T, K>> Function(String? cursor) onLoadMore;
const LongListStatefulScaffold({
required this.title,
this.trailingBuilder,
@ -43,9 +33,15 @@ class LongListStatefulScaffold<T, K> extends StatefulWidget {
required this.onRefresh,
required this.onLoadMore,
});
final Widget title;
final Widget Function(T t)? trailingBuilder;
final Widget Function(T t) headerBuilder;
final Widget Function(K k) itemBuilder;
final Future<LongListPayload<T, K>> Function() onRefresh;
final Future<LongListPayload<T, K>> Function(String? cursor) onLoadMore;
@override
_LongListStatefulScaffoldState<T, K> createState() =>
State<LongListStatefulScaffold<T, K>> createState() =>
_LongListStatefulScaffoldState();
}
@ -89,7 +85,7 @@ class _LongListStatefulScaffoldState<T, K>
loadingMore = true;
});
try {
LongListPayload<T?, K> p = await widget.onLoadMore(payload!.cursor);
final LongListPayload<T?, K> p = await widget.onLoadMore(payload!.cursor);
payload!.totalCount = p.totalCount;
payload!.cursor = p.cursor;
payload!.leadingItems.addAll(p.leadingItems);
@ -103,18 +99,16 @@ class _LongListStatefulScaffoldState<T, K>
}
Widget _buildItem(BuildContext context, int index) {
final theme = Provider.of<ThemeModel>(context);
if (index % 2 == 1) {
return CommonStyle.border;
}
int realIndex = index ~/ 2;
final realIndex = index ~/ 2;
if (realIndex < payload!.leadingItems.length) {
return widget.itemBuilder(payload!.leadingItems[realIndex]);
} else if (realIndex == payload!.leadingItems.length) {
var count = payload!.totalCount -
final count = payload!.totalCount -
payload!.leadingItems.length +
payload!.trailingItems!.length;
return Container(
@ -125,20 +119,21 @@ class _LongListStatefulScaffoldState<T, K>
child: Container(
padding: CommonStyle.padding,
decoration: BoxDecoration(
border: Border.all(color: theme.palette.text),
border: Border.all(color: AntTheme.of(context).colorText),
),
child: Column(
children: <Widget>[
Text('$count hidden items',
style:
TextStyle(color: theme.palette.text, fontSize: 15)),
style: TextStyle(
color: AntTheme.of(context).colorText, fontSize: 15)),
const Padding(padding: EdgeInsets.only(top: 4)),
loadingMore
? const CupertinoActivityIndicator()
: Text(
'Load more...',
style: TextStyle(
color: theme.palette.primary, fontSize: 16),
color: AntTheme.of(context).colorPrimary,
fontSize: 16),
),
],
),
@ -153,7 +148,7 @@ class _LongListStatefulScaffoldState<T, K>
}
int get _itemCount {
int count = payload!.leadingItems.length + payload!.trailingItems!.length;
var count = payload!.leadingItems.length + payload!.trailingItems!.length;
if (payload!.totalCount > count) {
count++;
}
@ -177,51 +172,27 @@ class _LongListStatefulScaffoldState<T, K>
@override
Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
List<Widget> slivers = [
CupertinoSliverRefreshControl(onRefresh: _refresh)
];
if (payload != null) {
slivers.add(
SliverToBoxAdapter(child: widget.headerBuilder(payload!.header)),
);
}
slivers.add(_buildSliver());
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: widget.title,
trailing: payload == null
? null
: widget.trailingBuilder!(payload!.header),
),
child: SafeArea(
child: CupertinoScrollbar(
child: CustomScrollView(slivers: slivers),
),
),
);
default:
return Scaffold(
appBar: AppBar(
title: widget.title,
actions: payload == null
? null
: [widget.trailingBuilder!(payload!.header)],
),
body: RefreshIndicator(
onRefresh: _refresh,
child: Scrollbar(
child: CustomScrollView(slivers: [
if (payload != null)
SliverToBoxAdapter(
child: widget.headerBuilder(payload!.header)),
_buildSliver(),
]),
),
),
);
final slivers = <Widget>[
CupertinoSliverRefreshControl(onRefresh: _refresh)
];
if (payload != null) {
slivers.add(
SliverToBoxAdapter(child: widget.headerBuilder(payload!.header)),
);
}
slivers.add(_buildSliver());
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: widget.title,
trailing:
payload == null ? null : widget.trailingBuilder!(payload!.header),
),
child: SafeArea(
child: CupertinoScrollbar(
child: CustomScrollView(slivers: slivers),
),
),
);
}
}

View File

@ -1,17 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/scaffolds/utils.dart';
class RefreshStatefulScaffold<T> extends StatefulWidget {
final Widget title;
final Widget? Function(T data, void Function(T newData) setData) bodyBuilder;
final Future<T> Function() fetch;
final Widget? Function(T data, void Function(T newData) setData)?
actionBuilder;
final Widget? action;
final canRefresh;
const RefreshStatefulScaffold({
required this.title,
required this.bodyBuilder,
@ -20,9 +12,16 @@ class RefreshStatefulScaffold<T> extends StatefulWidget {
this.action,
this.canRefresh = true,
}) : assert(actionBuilder == null || action == null);
final Widget title;
final Widget? Function(T data, void Function(T newData) setData) bodyBuilder;
final Future<T> Function() fetch;
final Widget? Function(T data, void Function(T newData) setData)?
actionBuilder;
final Widget? action;
final bool canRefresh;
@override
_RefreshStatefulScaffoldState<T> createState() =>
State<RefreshStatefulScaffold<T>> createState() =>
_RefreshStatefulScaffoldState();
}

View File

@ -1,22 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/scaffolds/common.dart';
class SingleScaffold extends StatelessWidget {
final Widget title;
final Widget body;
final Widget? action;
const SingleScaffold({
required this.title,
required this.body,
this.action,
});
final Widget title;
final Widget body;
final Widget? action;
@override
Widget build(BuildContext context) {
return CommonScaffold(
title: title,
body: Scrollbar(child: SingleChildScrollView(child: body)),
body: CupertinoScrollbar(child: SingleChildScrollView(child: body)),
action: action,
);
}

View File

@ -1,19 +1,8 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/scaffolds/utils.dart';
import 'package:provider/provider.dart';
class TabScaffold extends StatelessWidget {
final Widget title;
final Widget body;
final Widget? action;
final void Function() onRefresh;
final List<String> tabs;
final int activeTab;
final Function(int index) onTabSwitch;
const TabScaffold({
required this.title,
required this.body,
@ -23,57 +12,41 @@ class TabScaffold extends StatelessWidget {
required this.activeTab,
required this.onTabSwitch,
});
Widget _buildTitle(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
return DefaultTextStyle(
style: DefaultTextStyle.of(context).style.copyWith(fontSize: 14),
child: Row(
children: [
Expanded(
child: CupertinoSlidingSegmentedControl<int>(
groupValue: activeTab,
onValueChanged: (v) {
if (v == null) return;
onTabSwitch(v);
},
children: tabs.asMap().map((key, text) => MapEntry(
key,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(text),
))),
),
),
],
),
);
default:
return title;
}
}
final Widget title;
final Widget body;
final Widget? action;
final void Function() onRefresh;
final List<String> tabs;
final int activeTab;
final Function(int index) onTabSwitch;
@override
Widget build(BuildContext context) {
final scaffold = CommonScaffold(
title: _buildTitle(context),
return CommonScaffold(
title: DefaultTextStyle(
style: DefaultTextStyle.of(context).style.copyWith(fontSize: 14),
child: Row(
children: [
Expanded(
child: CupertinoSlidingSegmentedControl<int>(
groupValue: activeTab,
onValueChanged: (v) {
if (v == null) return;
onTabSwitch(v);
},
children: tabs.asMap().map((key, text) => MapEntry(
key,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(text),
))),
),
),
],
),
),
body: RefreshWrapper(body: body, onRefresh: onRefresh),
// action: action, // TODO:
bottom: TabBar(
onTap: onTabSwitch,
tabs: tabs.map((text) => Tab(text: text.toUpperCase())).toList(),
),
);
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
return scaffold;
default:
return DefaultTabController(
length: tabs.length,
child: scaffold,
);
}
}
}

View File

@ -1,15 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/scaffolds/tab.dart';
import 'package:git_touch/scaffolds/utils.dart';
class TabStatefulScaffold<T> extends StatefulWidget {
final Widget title;
final Widget Function(T payload, int activeTab) bodyBuilder;
final Future<T> Function(int activeTab) fetchData;
final List<String> tabs;
final Widget Function(T payload, void Function() refresh)? actionBuilder;
const TabStatefulScaffold({
required this.title,
required this.bodyBuilder,
@ -17,9 +11,14 @@ class TabStatefulScaffold<T> extends StatefulWidget {
required this.tabs,
this.actionBuilder,
});
final Widget title;
final Widget Function(T payload, int activeTab) bodyBuilder;
final Future<T> Function(int activeTab) fetchData;
final List<String> tabs;
final Widget Function(T payload, void Function() refresh)? actionBuilder;
@override
_TabStatefulScaffoldState<T> createState() => _TabStatefulScaffoldState();
State<TabStatefulScaffold<T>> createState() => _TabStatefulScaffoldState();
}
class _TabStatefulScaffoldState<T> extends State<TabStatefulScaffold<T>> {

View File

@ -1,55 +1,40 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/widgets/error_reload.dart';
import 'package:git_touch/widgets/loading.dart';
import 'package:provider/provider.dart';
class RefreshWrapper extends StatelessWidget {
final Widget body;
final void Function() onRefresh;
const RefreshWrapper({
required this.onRefresh,
required this.body,
});
final Widget body;
final void Function() onRefresh;
@override
Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
return CupertinoScrollbar(
child: CustomScrollView(
slivers: <Widget>[
CupertinoSliverRefreshControl(
onRefresh: onRefresh as Future<void> Function()?),
SliverToBoxAdapter(child: body),
],
),
);
default:
return RefreshIndicator(
onRefresh: onRefresh as Future<void> Function(),
child: Scrollbar(
child: SingleChildScrollView(child: body),
),
);
}
return CupertinoScrollbar(
child: CustomScrollView(
slivers: <Widget>[
CupertinoSliverRefreshControl(
onRefresh: onRefresh as Future<void> Function()?),
SliverToBoxAdapter(child: body),
],
),
);
}
}
class ErrorLoadingWrapper extends StatelessWidget {
final String error;
final bool loading;
final void Function() reload;
final Widget? Function() bodyBuilder;
const ErrorLoadingWrapper({
required this.error,
required this.loading,
required this.reload,
required this.bodyBuilder,
});
final String error;
final bool loading;
final void Function() reload;
final Widget? Function() bodyBuilder;
@override
Widget build(BuildContext context) {

View File

@ -1,69 +0,0 @@
// import 'package:flutter/material.dart';
// import 'package:git_touch/models/theme.dart';
// import 'package:git_touch/scaffolds/single.dart';
// import 'package:git_touch/widgets/app_bar_title.dart';
// import 'package:git_touch/widgets/table_view.dart';
// import 'package:launch_review/launch_review.dart';
// import 'package:package_info/package_info.dart';
// import 'package:provider/provider.dart';
// /// Unused
// class AboutScreen extends StatefulWidget {
// @override
// _AboutScreenState createState() => _AboutScreenState();
// }
// class _AboutScreenState extends State<AboutScreen> {
// var _version = '';
// @override
// void initState() {
// super.initState();
// PackageInfo.fromPlatform().then((info) {
// setState(() {
// _version = info.version;
// });
// });
// }
// @override
// Widget build(BuildContext context) {
// final theme = Provider.of<ThemeModel>(context);
// return SingleScaffold(
// title: AppBarTitle('About'),
// body: Column(
// children: <Widget>[
// SizedBox(height: 32),
// ClipRRect(
// borderRadius: BorderRadius.circular(24),
// child: Image.asset(
// 'images/icon.png',
// width: 96,
// ),
// ),
// SizedBox(height: 12),
// Text(
// 'GitTouch',
// style: TextStyle(fontSize: 20, color: theme.palette.text),
// ),
// SizedBox(height: 48),
// TableView(items: [
// TableViewItem(text: Text('Version'), rightWidget: Text(_version)),
// TableViewItem(text: Text('Source Code'), url: '/pd4d10/git-touch'),
// TableViewItem(
// text: Text('Feedback'), url: '/pd4d10/git-touch/issues/new'),
// TableViewItem(
// text: Text('Rate This App'),
// onTap: () {
// LaunchReview.launch(
// androidAppId: 'io.github.pd4d10.gittouch',
// iOSAppId: '1452042346',
// );
// },
// ),
// ]),
// ],
// ),
// );
// }
// }

View File

@ -1,23 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/commit_item.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class BbCommitsScreen extends StatelessWidget {
const BbCommitsScreen(this.owner, this.name, this.ref);
final String owner;
final String name;
final String ref;
const BbCommitsScreen(this.owner, this.name, this.ref);
@override
Widget build(BuildContext context) {
final auth = Provider.of<AuthModel>(context);
return ListStatefulScaffold<BbCommit, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.commits),
title: Text(AppLocalizations.of(context)!.commits),
fetch: (nextUrl) async {
final res = await context.read<AuthModel>().fetchBbWithPage(
nextUrl ?? '/repositories/$owner/$name/commits/$ref');

View File

@ -1,17 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/repository_item.dart';
import 'package:git_touch/widgets/repo_item.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class BbExploreScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<BbRepo, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.explore),
title: Text(AppLocalizations.of(context)!.explore),
fetch: (nextUrl) async {
final res = await context.read<AuthModel>().fetchBbWithPage(
nextUrl ?? '/repositories?role=member&sort=-updated_on');
@ -24,7 +23,7 @@ class BbExploreScreen extends StatelessWidget {
);
},
itemBuilder: (v) {
return RepositoryItem.bb(payload: v);
return RepoItem.bb(payload: v);
},
);
}

View File

@ -1,29 +1,29 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.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/utils/utils.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:git_touch/widgets/comment_item.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:tuple/tuple.dart';
class BbIssueScreen extends StatelessWidget {
const BbIssueScreen(this.owner, this.name, this.number, {this.isPr = false});
final String owner;
final String name;
final String number;
final bool isPr;
const BbIssueScreen(this.owner, this.name, this.number, {this.isPr = false});
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<Tuple2<BbIssues, Iterable<BbComment>>>(
title: Text("Issue: #$number"),
title: Text('Issue: #$number'),
fetch: () async {
final auth = context.read<AuthModel>();
final res = await Future.wait([
@ -45,7 +45,6 @@ class BbIssueScreen extends StatelessWidget {
bodyBuilder: (data, _) {
final issue = data.item1;
final comments = data.item2;
final theme = context.read<ThemeModel>();
return Column(children: <Widget>[
Container(
padding: CommonStyle.padding,
@ -65,7 +64,7 @@ class BbIssueScreen extends StatelessWidget {
'$owner / $name',
style: TextStyle(
fontSize: 17,
color: theme.palette.secondaryText,
color: AntTheme.of(context).colorTextSecondary,
),
),
const SizedBox(width: 4),
@ -73,7 +72,7 @@ class BbIssueScreen extends StatelessWidget {
'#$number',
style: TextStyle(
fontSize: 17,
color: theme.palette.tertiaryText,
color: AntTheme.of(context).colorWeak,
),
),
],

View File

@ -1,19 +1,19 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class BbIssueCommentScreen extends StatefulWidget {
const BbIssueCommentScreen(this.owner, this.name, this.number);
final String owner;
final String name;
final String number;
const BbIssueCommentScreen(this.owner, this.name, this.number);
@override
_BbIssueCommentScreenState createState() => _BbIssueCommentScreenState();
State<BbIssueCommentScreen> createState() => _BbIssueCommentScreenState();
}
class _BbIssueCommentScreenState extends State<BbIssueCommentScreen> {
@ -21,7 +21,6 @@ class _BbIssueCommentScreenState extends State<BbIssueCommentScreen> {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context);
return CommonScaffold(
title: const Text('New Comment'),
@ -30,7 +29,7 @@ class _BbIssueCommentScreenState extends State<BbIssueCommentScreen> {
Padding(
padding: CommonStyle.padding,
child: CupertinoTextField(
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.body,
onChanged: (v) {
setState(() {
@ -40,9 +39,10 @@ class _BbIssueCommentScreenState extends State<BbIssueCommentScreen> {
maxLines: 10,
),
),
CupertinoButton.filled(
AntButton(
color: AntTheme.of(context).colorPrimary,
child: const Text('Comment'),
onPressed: () async {
onClick: () async {
await auth.fetchBb(
'/repositories/${widget.owner}/${widget.name}/issues/${widget.number}/comments',
isPost: true,
@ -51,8 +51,7 @@ class _BbIssueCommentScreenState extends State<BbIssueCommentScreen> {
},
);
Navigator.pop(context, true);
await theme.push(
context,
await context.pushUrl(
'/bitbucket/${widget.owner}/${widget.name}/issues/${widget.number}',
replace: true,
);

View File

@ -1,19 +1,19 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class BbIssueFormScreen extends StatefulWidget {
const BbIssueFormScreen(this.owner, this.name);
final String owner;
final String name;
const BbIssueFormScreen(this.owner, this.name);
@override
_BbIssueFormScreenState createState() => _BbIssueFormScreenState();
State<BbIssueFormScreen> createState() => _BbIssueFormScreenState();
}
class _BbIssueFormScreenState extends State<BbIssueFormScreen> {
@ -22,7 +22,6 @@ class _BbIssueFormScreenState extends State<BbIssueFormScreen> {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context);
return CommonScaffold(
title: Text(AppLocalizations.of(context)!.submitAnIssue),
@ -31,7 +30,7 @@ class _BbIssueFormScreenState extends State<BbIssueFormScreen> {
Padding(
padding: CommonStyle.padding,
child: CupertinoTextField(
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.title,
onChanged: (v) {
setState(() {
@ -43,7 +42,7 @@ class _BbIssueFormScreenState extends State<BbIssueFormScreen> {
Padding(
padding: CommonStyle.padding,
child: CupertinoTextField(
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.body,
onChanged: (v) {
setState(() {
@ -53,9 +52,10 @@ class _BbIssueFormScreenState extends State<BbIssueFormScreen> {
maxLines: 10,
),
),
CupertinoButton.filled(
AntButton(
color: AntTheme.of(context).colorPrimary,
child: Text(AppLocalizations.of(context)!.submit),
onPressed: () async {
onClick: () async {
await auth.fetchBbJson(
'/repositories/${widget.owner}/${widget.name}/issues',
isPost: true,
@ -64,8 +64,7 @@ class _BbIssueFormScreenState extends State<BbIssueFormScreen> {
return BbIssues.fromJson(v);
});
Navigator.pop(context, true);
await theme.push(
context,
await context.pushUrl(
'/bitbucket/${widget.owner}/${widget.name}/issues',
replace: true,
);

View File

@ -1,22 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/issue_item.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class BbIssuesScreen extends StatelessWidget {
const BbIssuesScreen(this.owner, this.name);
final String owner;
final String name;
const BbIssuesScreen(this.owner, this.name);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<BbIssues, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.issues),
title: Text(AppLocalizations.of(context)!.issues),
actionBuilder: () {
return ActionEntry(
iconData: Octicons.plus, url: '/bitbucket/$owner/$name/issues/new');
@ -34,7 +34,7 @@ class BbIssuesScreen extends StatelessWidget {
);
},
itemBuilder: (v) {
int issueNumber =
final issueNumber =
int.parse(v.issueLink!.replaceFirst(RegExp(r'.*\/'), ''));
return IssueItem(
avatarUrl: v.reporter!.avatarUrl,

View File

@ -1,29 +1,30 @@
import 'dart:convert';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:universal_io/io.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/blob_view.dart';
import 'package:git_touch/widgets/object_tree.dart';
import 'package:provider/provider.dart';
import 'package:path/path.dart' as p;
import 'package:provider/provider.dart';
import 'package:universal_io/io.dart';
class BbObjectScreen extends StatelessWidget {
const BbObjectScreen(this.owner, this.name, this.ref, {this.path});
final String owner;
final String name;
final String ref;
final String? path;
const BbObjectScreen(this.owner, this.name, this.ref, {this.path});
@override
Widget build(BuildContext context) {
final auth = Provider.of<AuthModel>(context);
return ListStatefulScaffold<dynamic, String?>(
title: AppBarTitle(path ?? 'Files'),
title: Text(path ?? 'Files'),
fetch: (next) async {
final res = await auth.fetchBb(
next ?? '/repositories/$owner/$name/src/$ref/${path ?? ''}');
@ -52,7 +53,7 @@ class BbObjectScreen extends StatelessWidget {
if (pl is String) {
return BlobView(path, text: pl);
} else if (pl is BbTree) {
return ObjectTreeItem(
return createObjectTreeItem(
name: p.basename(pl.path),
type: pl.type,
// size: v.type == 'commit_file' ? v.size : null,

View File

@ -1,22 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/issue_item.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class BbPullsScreen extends StatelessWidget {
const BbPullsScreen(this.owner, this.name);
final String owner;
final String name;
const BbPullsScreen(this.owner, this.name);
@override
Widget build(BuildContext context) {
final auth = Provider.of<AuthModel>(context);
return ListStatefulScaffold<BbPulls, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.pullRequests),
title: Text(AppLocalizations.of(context)!.pullRequests),
fetch: (nextUrl) async {
final res = await context.read<AuthModel>().fetchBbWithPage(
nextUrl ?? '/repositories/$owner/$name/pullrequests');
@ -29,7 +28,7 @@ class BbPullsScreen extends StatelessWidget {
);
},
itemBuilder: (v) {
int pullNumber =
final pullNumber =
int.parse(v.pullRequestLink!.replaceFirst(RegExp(r'.*\/'), ''));
return IssueItem(
avatarUrl: v.author!.avatarUrl,

View File

@ -1,30 +1,31 @@
import 'dart:convert';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/markdown_view.dart';
import 'package:git_touch/widgets/repo_header.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class BbRepoScreen extends StatelessWidget {
const BbRepoScreen(this.owner, this.name, {this.branch});
final String owner;
final String name;
final String? branch;
const BbRepoScreen(this.owner, this.name, {this.branch});
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<Tuple3<BbRepo, String?, List<BbBranch>>>(
title: AppBarTitle(AppLocalizations.of(context)!.repository),
title: Text(AppLocalizations.of(context)!.repository),
fetch: () async {
final auth = context.read<AuthModel>();
final r = await auth.fetchBbJson('/repositories/$owner/$name');
@ -56,36 +57,44 @@ class BbRepoScreen extends StatelessWidget {
homepageUrl: p.website,
),
CommonStyle.border,
TableView(
items: [
TableViewItem(
leftIconData: Octicons.code,
text: const Text('Code'),
rightWidget: Text(filesize(p.size)),
url:
'/bitbucket/$owner/$name/src/${branch ?? p.mainbranch!.name}',
AntList(
children: [
AntListItem(
prefix: const Icon(Octicons.code),
extra: Text(filesize(p.size)),
onClick: () {
context.push(
'/bitbucket/$owner/$name/src/${branch ?? p.mainbranch!.name}');
},
child: const Text('Code'),
),
TableViewItem(
leftIconData: Octicons.issue_opened,
text: const Text('Issues'),
url: '/bitbucket/$owner/$name/issues',
AntListItem(
prefix: const Icon(Octicons.issue_opened),
child: const Text('Issues'),
onClick: () {
context.push('/bitbucket/$owner/$name/issues');
},
),
TableViewItem(
leftIconData: Octicons.git_pull_request,
text: const Text('Pull requests'),
url: '/bitbucket/$owner/$name/pulls',
AntListItem(
prefix: const Icon(Octicons.git_pull_request),
child: const Text('Pull requests'),
onClick: () {
context.push('/bitbucket/$owner/$name/pulls');
},
),
TableViewItem(
leftIconData: Octicons.history,
text: const Text('Commits'),
url:
'/bitbucket/$owner/$name/commits/${branch ?? p.mainbranch!.name}',
AntListItem(
prefix: const Icon(Octicons.history),
child: const Text('Commits'),
onClick: () {
context.push(
'/bitbucket/$owner/$name/commits/${branch ?? p.mainbranch!.name}');
},
),
TableViewItem(
leftIconData: Octicons.git_branch,
text: Text(AppLocalizations.of(context)!.branches),
rightWidget: Text('${(branch ?? p.mainbranch!.name)!}${branches.length}'),
onTap: () async {
AntListItem(
prefix: const Icon(Octicons.git_branch),
extra: Text(
'${(branch ?? p.mainbranch!.name)!}${branches.length}'),
onClick: () async {
if (branches.length < 2) return;
await theme.showPicker(
@ -97,14 +106,15 @@ class BbRepoScreen extends StatelessWidget {
.toList(),
onClose: (ref) {
if (ref != branch) {
theme.push(
context, '/bitbucket/$owner/$name?branch=$ref',
context.pushUrl(
'/bitbucket/$owner/$name?branch=$ref',
replace: true);
}
},
),
);
},
child: Text(AppLocalizations.of(context)!.branches),
),
],
),

View File

@ -1,18 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/bitbucket.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/user_item.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:flutter_gen/gen_l10n/S.dart';
class BbTeamsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<BbUser, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.teams),
title: Text(AppLocalizations.of(context)!.teams),
fetch: (nextUrl) async {
final res = await context
.read<AuthModel>()

View File

@ -1,19 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.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/utils/utils.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/repository_item.dart';
import 'package:git_touch/widgets/repo_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';
class BbUserScreen extends StatelessWidget {
const BbUserScreen(this.login, {this.isTeam = false});
final String? login;
final bool isTeam;
const BbUserScreen(this.login, {this.isTeam = false});
bool get isViewer => login == null;
@override
@ -59,9 +59,7 @@ class BbUserScreen extends StatelessWidget {
),
CommonStyle.border,
Column(
children: <Widget>[
for (var v in repos) RepositoryItem.bb(payload: v)
],
children: <Widget>[for (var v in repos) RepoItem.bb(payload: v)],
)
],
);

View File

@ -1,19 +1,18 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_highlight/flutter_highlight.dart';
import 'package:flutter_highlight/theme_map.dart';
import 'package:git_touch/models/code.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/single.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class CodeThemeScreen extends StatelessWidget {
String _getCode(bool isDark) => '''// ${isDark ? 'Dark' : 'Light'} Mode
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class MyApp extends StatelessWidget {
@override
@ -35,23 +34,22 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
var codeProvider = Provider.of<CodeModel>(context);
var theme = Provider.of<ThemeModel>(context);
final codeProvider = Provider.of<CodeModel>(context);
final theme = Provider.of<ThemeModel>(context);
return SingleScaffold(
title: AppBarTitle(AppLocalizations.of(context)!.codeTheme),
title: Text(AppLocalizations.of(context)!.codeTheme),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CommonStyle.verticalGap,
TableView(
headerText: AppLocalizations.of(context)!.fontStyle,
hasIcon: false,
items: [
TableViewItem(
text: Text(AppLocalizations.of(context)!.fontSize),
rightWidget: Text(codeProvider.fontSize.toString()),
onTap: () {
AntList(
mode: AntListMode.card,
header: Text(AppLocalizations.of(context)!.fontStyle),
children: [
AntListItem(
extra: Text(codeProvider.fontSize.toString()),
onClick: () {
theme.showPicker(
context,
PickerGroupItem(
@ -66,11 +64,11 @@ class MyApp extends StatelessWidget {
),
);
},
child: Text(AppLocalizations.of(context)!.fontSize),
),
TableViewItem(
text: Text(AppLocalizations.of(context)!.fontFamily),
rightWidget: Text(codeProvider.fontFamily),
onTap: () {
AntListItem(
extra: Text(codeProvider.fontFamily),
onClick: () {
theme.showPicker(
context,
PickerGroupItem(
@ -78,23 +76,24 @@ class MyApp extends StatelessWidget {
items: CodeModel.fontFamilies
.map((v) => PickerItem(v, text: v))
.toList(),
onChange: (String? value) {
onChange: (value) {
codeProvider.setFontFamily(value!);
},
),
);
},
child: Text(AppLocalizations.of(context)!.fontFamily),
),
],
),
CommonStyle.verticalGap,
TableView(
headerText: AppLocalizations.of(context)!.syntaxHighlighting,
items: [
TableViewItem(
text: Text(AppLocalizations.of(context)!.light),
rightWidget: Text(codeProvider.theme),
onTap: () {
AntList(
mode: AntListMode.card,
header: Text(AppLocalizations.of(context)!.syntaxHighlighting),
children: [
AntListItem(
extra: Text(codeProvider.theme),
onClick: () {
theme.showPicker(
context,
PickerGroupItem(
@ -108,11 +107,20 @@ class MyApp extends StatelessWidget {
),
);
},
child: Text(AppLocalizations.of(context)!.light),
),
TableViewItem(
text: Text(AppLocalizations.of(context)!.dark),
rightWidget: Text(codeProvider.themeDark),
onTap: () {
AntListItem(
child: HighlightView(
_getCode(false),
language: 'dart',
theme: themeMap[codeProvider.theme]!,
textStyle: codeProvider.fontStyle,
padding: CommonStyle.padding,
),
),
AntListItem(
extra: Text(codeProvider.themeDark),
onClick: () {
theme.showPicker(
context,
PickerGroupItem(
@ -126,23 +134,19 @@ class MyApp extends StatelessWidget {
),
);
},
child: Text(AppLocalizations.of(context)!.dark),
),
AntListItem(
child: HighlightView(
_getCode(true),
language: 'dart',
theme: themeMap[codeProvider.themeDark]!,
textStyle: codeProvider.fontStyle,
padding: CommonStyle.padding,
),
)
],
),
HighlightView(
_getCode(false),
language: 'dart',
theme: themeMap[codeProvider.theme]!,
textStyle: codeProvider.fontStyle,
padding: CommonStyle.padding,
),
HighlightView(
_getCode(true),
language: 'dart',
theme: themeMap[codeProvider.themeDark]!,
textStyle: codeProvider.fontStyle,
padding: CommonStyle.padding,
),
],
),
);

View File

@ -1,31 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/blob_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GeBlobScreen extends StatelessWidget {
const GeBlobScreen(this.owner, this.name, this.sha, this.path);
final String owner;
final String name;
final String sha;
final String path;
const GeBlobScreen(this.owner, this.name, this.sha, this.path);
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<String?>(
title: AppBarTitle(AppLocalizations.of(context)!.file),
title: Text(AppLocalizations.of(context)!.file),
fetch: () async {
final auth = context.read<AuthModel>();
final res = await auth.fetchGitee('/repos/$owner/$name/git/blobs/$sha');
return GiteeBlob.fromJson(res).content;
},
action: const ActionEntry(iconData: Ionicons.cog, url: '/choose-code-theme'),
action:
const ActionEntry(iconData: Ionicons.cog, url: '/choose-code-theme'),
bodyBuilder: (content, _) {
return BlobView(path, base64Text: content);
},

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
@ -7,20 +9,17 @@ import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/files_item.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
class GeCommitScreen extends StatelessWidget {
const GeCommitScreen(this.owner, this.name, this.sha);
final String owner;
final String name;
final String sha;
const GeCommitScreen(this.owner, this.name, this.sha);
@override
Widget build(BuildContext context) {
final theme = context.watch<ThemeModel>();
return RefreshStatefulScaffold<GiteeCommit>(
title: Text("Commit: ${sha.substring(0, 7)}"),
title: Text('Commit: ${sha.substring(0, 7)}'),
fetch: () async {
final auth = context.read<AuthModel>();
final items = await auth.fetchGitee('/repos/$owner/$name/commits/$sha');
@ -48,7 +47,7 @@ class GeCommitScreen extends StatelessWidget {
'$owner / $name',
style: TextStyle(
fontSize: 17,
color: theme.palette.secondaryText,
color: AntTheme.of(context).colorTextSecondary,
),
),
const SizedBox(width: 4),
@ -56,7 +55,7 @@ class GeCommitScreen extends StatelessWidget {
sha.substring(0, 7),
style: TextStyle(
fontSize: 17,
color: theme.palette.tertiaryText,
color: AntTheme.of(context).colorWeak,
),
),
],

View File

@ -1,22 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/commit_item.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GeCommitsScreen extends StatelessWidget {
const GeCommitsScreen(this.owner, this.name, {this.branch});
final String owner;
final String name;
final String? branch;
const GeCommitsScreen(this.owner, this.name, {this.branch});
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteeCommit, int>(
title: AppBarTitle(AppLocalizations.of(context)!.commits),
title: Text(AppLocalizations.of(context)!.commits),
fetch: (page) async {
final res = await context.read<AuthModel>().fetchGiteeWithPage(
'/repos/$owner/$name/commits?sha=$branch',

View File

@ -1,22 +1,21 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:provider/provider.dart';
class GeContributorsScreen extends StatelessWidget {
const GeContributorsScreen(this.owner, this.name);
final String owner;
final String name;
const GeContributorsScreen(this.owner, this.name);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteeContributor, int>(
title: AppBarTitle(AppLocalizations.of(context)!.contributors),
title: Text(AppLocalizations.of(context)!.contributors),
fetch: (page) async {
page = page ?? 1;
final res = await context
@ -35,7 +34,6 @@ class GeContributorsScreen extends StatelessWidget {
);
},
itemBuilder: (v) {
final theme = context.read<ThemeModel>();
return Container(
padding: CommonStyle.padding,
child: Row(
@ -51,7 +49,7 @@ class GeContributorsScreen extends StatelessWidget {
Text(
v.name!,
style: TextStyle(
color: theme.palette.primary,
color: AntTheme.of(context).colorPrimary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
@ -62,11 +60,10 @@ class GeContributorsScreen extends StatelessWidget {
if (v.contributions != null)
DefaultTextStyle(
style: TextStyle(
color: theme.palette.secondaryText,
color: AntTheme.of(context).colorTextSecondary,
fontSize: 16,
),
child: Text(
"Contributions: ${v.contributions}"),
child: Text('Contributions: ${v.contributions}'),
),
],
),

View File

@ -1,24 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/files_item.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:provider/provider.dart';
class GeFilesScreen extends StatelessWidget {
const GeFilesScreen(this.owner, this.name, this.pullNumber);
final String owner;
final String name;
final String pullNumber;
const GeFilesScreen(this.owner, this.name, this.pullNumber);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteePullFile, int>(
title: AppBarTitle(AppLocalizations.of(context)!.files),
title: Text(AppLocalizations.of(context)!.files),
actionBuilder: () {
return ActionButton(
title: 'Actions',

View File

@ -1,52 +1,50 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:git_touch/widgets/comment_item.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:tuple/tuple.dart';
class GeIssueScreen extends StatelessWidget {
const GeIssueScreen(this.owner, this.name, this.number, {this.isPr = false});
final String owner;
final String name;
final String number;
final bool isPr;
const GeIssueScreen(this.owner, this.name, this.number, {this.isPr = false});
List<ActionItem> _buildCommentActionItem(
BuildContext context, GiteeComment comment) {
final auth = context.read<AuthModel>();
final theme = context.read<ThemeModel>();
return [
ActionItem(
iconData: Octicons.pencil,
text: 'Edit',
onTap: (_) {
final uri = Uri(
path: '/gitee/$owner/$name/issues/$number/comment',
queryParameters: {
'body': comment.body,
'id': comment.id.toString(),
},
).toString();
theme.push(context, uri);
}),
text: 'Edit',
onTap: (_) {
final uri = Uri(
path: '/gitee/$owner/$name/issues/$number/comment',
queryParameters: {
'body': comment.body,
'id': comment.id.toString(),
},
).toString();
context.pushUrl(uri);
},
),
ActionItem(
iconData: Octicons.trashcan,
text: 'Delete',
onTap: (_) async {
await auth.fetchGitee(
'/repos/$owner/$name/issues/comments/${comment.id}',
requestType: 'DELETE');
await theme.push(context, '/gitee/$owner/$name/issues/$number',
await context.pushUrl('/gitee/$owner/$name/issues/$number',
replace: true);
},
),
@ -56,7 +54,7 @@ class GeIssueScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<Tuple2<GiteeIssue, List<GiteeComment>>>(
title: Text("Issue: #$number"),
title: Text('Issue: #$number'),
fetch: () async {
final auth = context.read<AuthModel>();
final items = await Future.wait([
@ -73,7 +71,6 @@ class GeIssueScreen extends StatelessWidget {
bodyBuilder: (data, _) {
final issue = data.item1;
final comments = data.item2;
final theme = context.read<ThemeModel>();
return Column(children: <Widget>[
Container(
padding: CommonStyle.padding,
@ -93,7 +90,7 @@ class GeIssueScreen extends StatelessWidget {
'$owner / $name',
style: TextStyle(
fontSize: 17,
color: theme.palette.secondaryText,
color: AntTheme.of(context).colorTextSecondary,
),
),
const SizedBox(width: 4),
@ -101,7 +98,7 @@ class GeIssueScreen extends StatelessWidget {
'#$number',
style: TextStyle(
fontSize: 17,
color: theme.palette.tertiaryText,
color: AntTheme.of(context).colorWeak,
),
),
],

View File

@ -1,23 +1,23 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GeIssueCommentScreen extends StatefulWidget {
const GeIssueCommentScreen(this.owner, this.name, this.number,
{this.isPr = false, this.body = '', this.id = ''});
final String owner;
final String name;
final String number;
final bool isPr;
final String body;
final String id;
const GeIssueCommentScreen(this.owner, this.name, this.number,
{this.isPr = false, this.body = '', this.id = ''});
@override
_GeIssueCommentScreenState createState() => _GeIssueCommentScreenState();
State<GeIssueCommentScreen> createState() => _GeIssueCommentScreenState();
}
class _GeIssueCommentScreenState extends State<GeIssueCommentScreen> {
@ -35,7 +35,6 @@ class _GeIssueCommentScreenState extends State<GeIssueCommentScreen> {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context);
return CommonScaffold(
title: Text(isEdit ? 'Update Comment' : 'New Comment'),
@ -45,14 +44,15 @@ class _GeIssueCommentScreenState extends State<GeIssueCommentScreen> {
padding: CommonStyle.padding,
child: CupertinoTextField(
controller: _controller,
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.body,
maxLines: 10,
),
),
CupertinoButton.filled(
AntButton(
color: AntTheme.of(context).colorPrimary,
child: const Text('Comment'),
onPressed: () async {
onClick: () async {
if (!isEdit) {
await auth.fetchGitee(
'/repos/${widget.owner}/${widget.name}/${widget.isPr ? 'pulls' : 'issues'}/${widget.number}/comments',
@ -67,8 +67,7 @@ class _GeIssueCommentScreenState extends State<GeIssueCommentScreen> {
);
}
Navigator.pop(context, '');
await theme.push(
context,
await context.pushUrl(
'/gitee/${widget.owner}/${widget.name}/${widget.isPr ? 'pulls' : 'issues'}/${widget.number}',
replace: true,
);

View File

@ -1,19 +1,19 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GeIssueFormScreen extends StatefulWidget {
const GeIssueFormScreen(this.owner, this.name);
final String owner;
final String name;
const GeIssueFormScreen(this.owner, this.name);
@override
_GeIssueFormScreenState createState() => _GeIssueFormScreenState();
State<GeIssueFormScreen> createState() => _GeIssueFormScreenState();
}
class _GeIssueFormScreenState extends State<GeIssueFormScreen> {
@ -22,7 +22,6 @@ class _GeIssueFormScreenState extends State<GeIssueFormScreen> {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context);
return CommonScaffold(
title: Text(AppLocalizations.of(context)!.submitAnIssue),
@ -31,7 +30,7 @@ class _GeIssueFormScreenState extends State<GeIssueFormScreen> {
Padding(
padding: CommonStyle.padding,
child: CupertinoTextField(
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.title,
onChanged: (v) {
setState(() {
@ -43,7 +42,7 @@ class _GeIssueFormScreenState extends State<GeIssueFormScreen> {
Padding(
padding: CommonStyle.padding,
child: CupertinoTextField(
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.body,
onChanged: (v) {
setState(() {
@ -63,8 +62,7 @@ class _GeIssueFormScreenState extends State<GeIssueFormScreen> {
).then((v) {
return GiteeIssue.fromJson(v);
});
await theme.push(
context,
await context.pushUrl(
'/gitee/${widget.owner}/${widget.name}/issues/${res.number}',
replace: true,
);

View File

@ -1,23 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/hex_color_tag.dart';
import 'package:git_touch/widgets/issue_item.dart';
import 'package:git_touch/widgets/label.dart';
import 'package:provider/provider.dart';
class GeIssuesScreen extends StatelessWidget {
const GeIssuesScreen(this.owner, this.name, {this.isPr = false});
final String owner;
final String name;
final bool isPr;
const GeIssuesScreen(this.owner, this.name, {this.isPr = false});
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteeIssue, int>(
title: AppBarTitle(isPr ? 'Pull Requests' : 'Issues'),
title: Text(isPr ? 'Pull Requests' : 'Issues'),
fetch: (page) async {
final res = await context
.read<AuthModel>()
@ -44,7 +44,7 @@ class GeIssuesScreen extends StatelessWidget {
? null
: Wrap(spacing: 4, runSpacing: 4, children: [
for (var label in p.labels!)
MyLabel(name: label.name, cssColor: label.color)
HexColorTag(name: label.name!, color: label.color!)
]),
),
);

View File

@ -1,52 +1,50 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:git_touch/widgets/comment_item.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:tuple/tuple.dart';
class GePullScreen extends StatelessWidget {
const GePullScreen(this.owner, this.name, this.number, {this.isPr = false});
final String owner;
final String name;
final String number;
final bool isPr;
const GePullScreen(this.owner, this.name, this.number, {this.isPr = false});
List<ActionItem> _buildCommentActionItem(
BuildContext context, GiteeComment comment) {
final auth = context.read<AuthModel>();
final theme = context.read<ThemeModel>();
return [
ActionItem(
iconData: Octicons.pencil,
text: 'Edit',
onTap: (_) {
final uri = Uri(
path: '/gitee/$owner/$name/pulls/$number/comment',
queryParameters: {
'body': comment.body,
'id': comment.id.toString(),
},
).toString();
theme.push(context, uri);
}),
text: 'Edit',
onTap: (_) {
final uri = Uri(
path: '/gitee/$owner/$name/pulls/$number/comment',
queryParameters: {
'body': comment.body,
'id': comment.id.toString(),
},
).toString();
context.pushUrl(uri);
},
),
ActionItem(
iconData: Octicons.trashcan,
text: 'Delete',
onTap: (_) async {
await auth.fetchGitee(
'/repos/$owner/$name/pulls/comments/${comment.id}',
requestType: 'DELETE');
await theme.push(context, '/gitee/$owner/$name/pulls/$number',
await context.pushUrl('/gitee/$owner/$name/pulls/$number',
replace: true);
},
),
@ -58,7 +56,7 @@ class GePullScreen extends StatelessWidget {
return RefreshStatefulScaffold<
Tuple4<GiteePull, List<GiteeComment>, List<GiteePullFile>,
List<GiteeCommit>>>(
title: Text("Pull Request: #$number"),
title: Text('Pull Request: #$number'),
fetch: () async {
final auth = context.read<AuthModel>();
final items = await Future.wait([
@ -82,24 +80,24 @@ class GePullScreen extends StatelessWidget {
final comments = data.item2;
final files = data.item3;
final commits = data.item4;
final theme = context.read<ThemeModel>();
var additions = 0;
var deletions = 0;
for (var file in files) {
for (final file in files) {
additions += int.parse(file.additions!);
deletions += int.parse(file.deletions!);
}
return Column(children: <Widget>[
Container(
padding: CommonStyle.padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LinkWidget(
url: '/gitee/$owner/$name',
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
return Column(
children: <Widget>[
Container(
padding: CommonStyle.padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LinkWidget(
url: '/gitee/$owner/$name',
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LinkWidget(
@ -115,7 +113,8 @@ class GePullScreen extends StatelessWidget {
'$owner / $name',
style: TextStyle(
fontSize: 17,
color: theme.palette.secondaryText,
color: AntTheme.of(context)
.colorTextSecondary,
),
),
const SizedBox(width: 4),
@ -123,7 +122,7 @@ class GePullScreen extends StatelessWidget {
'#$number',
style: TextStyle(
fontSize: 17,
color: theme.palette.tertiaryText,
color: AntTheme.of(context).colorWeak,
),
),
],
@ -149,7 +148,8 @@ class GePullScreen extends StatelessWidget {
LinkWidget(
url: '/gitee/$owner/$name/pulls/$number/files',
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
padding:
const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
@ -157,7 +157,8 @@ class GePullScreen extends StatelessWidget {
Text(
'${files.length} files changed',
style: TextStyle(
color: theme.palette.secondaryText,
color: AntTheme.of(context)
.colorTextSecondary,
fontSize: 17,
),
),
@ -165,22 +166,25 @@ class GePullScreen extends StatelessWidget {
children: <Widget>[
Text(
'+$additions',
style: const TextStyle(
color: Colors.green,
style: TextStyle(
color: AntTheme.of(context)
.colorSuccess,
fontSize: 15,
),
),
const SizedBox(width: 2),
Text(
'-$deletions',
style: const TextStyle(
color: Colors.red,
style: TextStyle(
color: AntTheme.of(context)
.colorDanger,
fontSize: 15,
),
),
Icon(
Ionicons.chevron_forward,
color: theme.palette.border,
color:
AntTheme.of(context).colorBorder,
),
],
)
@ -189,72 +193,74 @@ class GePullScreen extends StatelessWidget {
),
),
CommonStyle.border,
ListTileTheme(
contentPadding: EdgeInsets.zero,
child: ExpansionTile(
title: Text(
'Commits',
style: TextStyle(
color: theme.palette.primary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
children: [
for (var commit in commits) ...[
LinkWidget(
url:
'/gitee/$owner/$name/commits/${commit.sha}',
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
commit.sha!.substring(0, 7),
style: TextStyle(
color: theme.palette.primary,
fontSize: 17,
fontFamily:
CommonStyle.monospace,
),
),
],
Column(
// title: Text(
// 'Commits',
// style: TextStyle(
// color: AntTheme.of(context).colorPrimary,
// fontSize: 18,
// fontWeight: FontWeight.w600,
// ),
// ),
children: [
for (var commit in commits) ...[
LinkWidget(
url:
'/gitee/$owner/$name/commits/${commit.sha}',
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
commit.sha!.substring(0, 7),
style: TextStyle(
color: AntTheme.of(context)
.colorPrimary,
fontSize: 17,
fontFamily: CommonStyle.monospace,
),
),
),
)
]
],
)),
]),
],
),
),
)
]
],
),
],
),
),
),
),
CommonStyle.border,
],
)),
Column(
children: [
for (var comment in comments) ...[
Padding(
padding: const EdgeInsets.only(left: 10),
child: CommentItem(
avatar: Avatar(
url: comment.user!.avatarUrl,
linkUrl: '/gitee/${comment.user!.login}',
),
createdAt: DateTime.parse(comment.createdAt!),
body: comment.body,
login: comment.user!.login,
prefix: 'gitee',
commentActionItemList:
_buildCommentActionItem(context, comment),
)),
CommonStyle.border,
const SizedBox(height: 16),
],
)),
Column(children: [
for (var comment in comments) ...[
Padding(
padding: const EdgeInsets.only(left: 10),
child: CommentItem(
avatar: Avatar(
url: comment.user!.avatarUrl,
linkUrl: '/gitee/${comment.user!.login}',
),
createdAt: DateTime.parse(comment.createdAt!),
body: comment.body,
login: comment.user!.login,
prefix: 'gitee',
commentActionItemList:
_buildCommentActionItem(context, comment),
)),
CommonStyle.border,
const SizedBox(height: 16),
],
]),
]);
],
),
],
);
},
);
}

View File

@ -1,22 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/hex_color_tag.dart';
import 'package:git_touch/widgets/issue_item.dart';
import 'package:git_touch/widgets/label.dart';
import 'package:provider/provider.dart';
class GePullsScreen extends StatelessWidget {
const GePullsScreen(this.owner, this.name, {this.isPr = false});
final String owner;
final String name;
final bool isPr;
const GePullsScreen(this.owner, this.name, {this.isPr = false});
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteePull, int>(
title: AppBarTitle(isPr ? 'Pull Requests' : 'Issues'),
title: Text(isPr ? 'Pull Requests' : 'Issues'),
fetch: (page) async {
final res = await context
.read<AuthModel>()
@ -39,7 +38,7 @@ class GePullsScreen extends StatelessWidget {
? null
: Wrap(spacing: 4, runSpacing: 4, children: [
for (var label in p.labels!)
MyLabel(name: label.name, cssColor: label.color)
HexColorTag(name: label.name!, color: label.color!)
]),
),
);

View File

@ -1,39 +1,41 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/entry_item.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/table_view.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_gen/gen_l10n/S.dart';
class StatusPayload {
StatusPayload(this.isWatching, this.isStarred);
bool isWatching;
bool isStarred;
StatusPayload(this.isWatching, this.isStarred);
}
class GeRepoScreen extends StatelessWidget {
const GeRepoScreen(this.owner, this.name, {this.branch});
final String owner;
final String name;
final String? branch;
const GeRepoScreen(this.owner, this.name, {this.branch});
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<
Tuple4<GiteeRepo, MarkdownViewData, List<GiteeBranch>, StatusPayload>>(
title: AppBarTitle(AppLocalizations.of(context)!.repository),
title: Text(AppLocalizations.of(context)!.repository),
fetch: () async {
final auth = context.read<AuthModel>();
final repo = await auth.fetchGitee('/repos/$owner/$name').then((v) {
@ -41,8 +43,8 @@ class GeRepoScreen extends StatelessWidget {
});
md() => auth.fetchGitee('/repos/$owner/$name/readme').then((v) {
return (v['content'] as String?)?.base64ToUtf8 ?? '';
});
return (v['content'] as String?)?.base64ToUtf8 ?? '';
});
html() => md().then((v) async {
final res = await http.post(
Uri.parse('${auth.activeAccount!.domain}/api/v5/markdown'),
@ -56,14 +58,14 @@ class GeRepoScreen extends StatelessWidget {
await auth.fetchGitee('/repos/$owner/$name/branches').then((v) {
return [for (var branch in v) GiteeBranch.fromJson(branch)];
});
bool isStarred = await auth
final isStarred = await auth
.fetchGitee('/user/starred/$owner/$name', requestType: 'NO CONTENT')
.then((v) => v.statusCode == HttpStatus.noContent);
bool isWatching = await auth
final isWatching = await auth
.fetchGitee('/user/subscriptions/$owner/$name',
requestType: 'NO CONTENT')
.then((v) => v.statusCode == HttpStatus.noContent);
StatusPayload statusPayload = StatusPayload(isWatching, isStarred);
final statusPayload = StatusPayload(isWatching, isStarred);
return Tuple4(repo, readmeData, branches, statusPayload);
},
bodyBuilder: (t, setData) {
@ -86,7 +88,7 @@ class GeRepoScreen extends StatelessWidget {
active: t.item4.isWatching,
text: t.item4.isWatching ? 'Ignore' : 'Watch',
onTap: () async {
final String watchType =
final watchType =
t.item4.isWatching ? 'ignoring' : 'watching';
await context.read<AuthModel>().fetchGitee(
'/user/subscriptions/$owner/$name?watch_type=$watchType',
@ -114,54 +116,63 @@ class GeRepoScreen extends StatelessWidget {
Row(
children: <Widget>[
EntryItem(
count: p.watchersCount,
count: p.watchersCount!,
text: 'Watchers',
url: '/gitee/$owner/$name/watchers',
),
EntryItem(
count: p.stargazersCount,
count: p.stargazersCount!,
text: 'Stars',
url: '/gitee/$owner/$name/stargazers',
),
EntryItem(
count: p.forksCount,
count: p.forksCount!,
text: 'Forks',
url: '/gitee/$owner/$name/forks',
),
],
),
CommonStyle.border,
TableView(
items: [
TableViewItem(
leftIconData: Octicons.code,
text: const Text('Code'),
rightWidget: Text(p.license ?? ''),
url: '/gitee/$owner/$name/tree/${branch ?? p.defaultBranch}',
AntList(
children: [
AntListItem(
prefix: const Icon(Octicons.code),
extra: Text(p.license ?? ''),
onClick: () {
context.push(
'/gitee/$owner/$name/tree/${branch ?? p.defaultBranch}');
},
child: const Text('Code'),
),
TableViewItem(
leftIconData: Octicons.issue_opened,
text: const Text('Issues'),
rightWidget: Text(numberFormat.format(p.openIssuesCount)),
url: '/gitee/$owner/$name/issues',
AntListItem(
prefix: const Icon(Octicons.issue_opened),
extra: Text(numberFormat.format(p.openIssuesCount)),
onClick: () {
context.push('/gitee/$owner/$name/issues');
},
child: const Text('Issues'),
),
if (p.pullRequestsEnabled!)
TableViewItem(
leftIconData: Octicons.git_pull_request,
text: const Text('Pull requests'),
url: '/gitee/$owner/$name/pulls',
AntListItem(
prefix: const Icon(Octicons.git_pull_request),
child: const Text('Pull requests'),
onClick: () {
context.push('/gitee/$owner/$name/pulls');
},
),
TableViewItem(
leftIconData: Octicons.history,
text: const Text('Commits'),
url:
'/gitee/$owner/$name/commits?branch=${branch ?? p.defaultBranch}',
AntListItem(
prefix: const Icon(Octicons.history),
child: const Text('Commits'),
onClick: () {
context.push(
'/gitee/$owner/$name/commits?branch=${branch ?? p.defaultBranch}');
},
),
TableViewItem(
leftIconData: Octicons.git_branch,
text: Text(AppLocalizations.of(context)!.branches),
rightWidget: Text('${(branch ?? p.defaultBranch)!}${branches.length}'),
onTap: () async {
AntListItem(
prefix: const Icon(Octicons.git_branch),
extra: Text(
'${(branch ?? p.defaultBranch)!}${branches.length}'),
onClick: () async {
if (branches.length < 2) return;
await theme.showPicker(
@ -173,19 +184,22 @@ class GeRepoScreen extends StatelessWidget {
.toList(),
onClose: (ref) {
if (ref != branch) {
theme.push(
context, '/gitee/$owner/$name?branch=$ref',
context.pushUrl('/gitee/$owner/$name?branch=$ref',
replace: true);
}
},
),
);
},
child: Text(AppLocalizations.of(context)!.branches),
),
AntListItem(
prefix: const Icon(Octicons.organization),
child: const Text('Contributors'),
onClick: () {
context.push('/gitee/$owner/$name/contributors');
},
),
TableViewItem(
leftIconData: Octicons.organization,
text: const Text('Contributors'),
url: '/gitee/$owner/$name/contributors'),
],
),
CommonStyle.verticalGap,

View File

@ -1,16 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/widgets/repo_item.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/repository_item.dart';
import 'package:timeago/timeago.dart' as timeago;
class GeReposScreen extends StatelessWidget {
final String api;
final String title;
const GeReposScreen(String owner)
: api = '/users/$owner/repos',
title = 'Repositories';
@ -20,11 +16,13 @@ class GeReposScreen extends StatelessWidget {
const GeReposScreen.forks(String owner, String name)
: api = '/repos/$owner/$name/forks',
title = 'Forks';
final String api;
final String title;
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteeRepo, int>(
title: AppBarTitle(title),
title: Text(title),
fetch: (page) async {
final res =
await context.read<AuthModel>().fetchGiteeWithPage(api, page: page);
@ -35,7 +33,7 @@ class GeReposScreen extends StatelessWidget {
);
},
itemBuilder: (v) {
return RepositoryItem(
return RepoItem(
owner: v.namespace!.path,
avatarUrl: v.owner!.avatarUrl,
name: v.path,

View File

@ -1,22 +1,21 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/issue_item.dart';
import 'package:git_touch/widgets/loading.dart';
import 'package:git_touch/widgets/repository_item.dart';
import 'package:git_touch/widgets/repo_item.dart';
import 'package:git_touch/widgets/user_item.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:timeago/timeago.dart' as timeago;
class GeSearchScreen extends StatefulWidget {
@override
_GeSearchScreenState createState() => _GeSearchScreenState();
State<GeSearchScreen> createState() => _GeSearchScreenState();
}
class _GeSearchScreenState extends State<GeSearchScreen> {
@ -42,7 +41,7 @@ class _GeSearchScreenState extends State<GeSearchScreen> {
Future<void> _query() async {
if (_loading || _keyword.isEmpty) return;
var keyword = _controller!.text;
final keyword = _controller!.text;
setState(() {
_loading = true;
});
@ -71,37 +70,6 @@ class _GeSearchScreenState extends State<GeSearchScreen> {
}
}
Widget _buildInput() {
final theme = Provider.of<ThemeModel>(context);
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
return Container(
color: theme.palette.background,
child: CupertinoTextField(
prefix: Row(
children: const <Widget>[
SizedBox(width: 8),
Icon(Octicons.search, size: 20, color: PrimerColors.gray400),
],
),
placeholder: AppLocalizations.of(context)!.search,
clearButtonMode: OverlayVisibilityMode.editing,
textInputAction: TextInputAction.go,
onSubmitted: (_) => _query(),
controller: _controller,
),
);
default:
return TextField(
decoration: InputDecoration.collapsed(
hintText: AppLocalizations.of(context)!.search),
textInputAction: TextInputAction.go,
onSubmitted: (_) => _query(),
controller: _controller,
);
}
}
_onTabSwitch(int? index) {
setState(() {
_activeTab = index;
@ -116,7 +84,7 @@ class _GeSearchScreenState extends State<GeSearchScreen> {
Widget _buildItem(p) {
switch (_activeTab) {
case 0:
return RepositoryItem(
return RepoItem(
owner: p.namespace.path,
avatarUrl: p.owner.avatarUrl,
name: p.path,
@ -139,7 +107,7 @@ class _GeSearchScreenState extends State<GeSearchScreen> {
author: p.user.login,
avatarUrl: p.user.avatarUrl,
commentCount: p.comments,
subtitle: '#' + p.number,
subtitle: '#${p.number}',
title: p.title,
updatedAt: DateTime.parse(p.updatedAt),
url:
@ -150,29 +118,41 @@ class _GeSearchScreenState extends State<GeSearchScreen> {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context).theme;
final scaffold = CommonScaffold(
title: _buildInput(),
return CommonScaffold(
title: Container(
color: AntTheme.of(context).colorBackground,
child: CupertinoTextField(
prefix: Row(
children: const <Widget>[
SizedBox(width: 8),
Icon(Octicons.search, size: 20, color: PrimerColors.gray400),
],
),
placeholder: AppLocalizations.of(context)!.search,
clearButtonMode: OverlayVisibilityMode.editing,
textInputAction: TextInputAction.go,
onSubmitted: (_) => _query(),
controller: _controller,
),
),
body: SingleChildScrollView(
child: Column(
children: [
if (theme == AppThemeType.cupertino)
Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: CupertinoSlidingSegmentedControl(
groupValue: _activeTab,
onValueChanged: _onTabSwitch,
children: tabs.asMap().map((key, text) => MapEntry(
key,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(text, style: const TextStyle(fontSize: 14)),
))),
),
Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: CupertinoSlidingSegmentedControl(
groupValue: _activeTab,
onValueChanged: _onTabSwitch,
children: tabs.asMap().map((key, text) => MapEntry(
key,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(text, style: const TextStyle(fontSize: 14)),
))),
),
),
),
if (_loading)
const Loading()
else
@ -180,19 +160,6 @@ class _GeSearchScreenState extends State<GeSearchScreen> {
],
),
),
bottom: TabBar(
onTap: _onTabSwitch,
tabs: tabs.map((text) => Tab(text: text.toUpperCase())).toList(),
),
);
if (theme == AppThemeType.material) {
return DefaultTabController(
length: tabs.length,
child: scaffold,
);
} else {
return scaffold;
}
}
}

View File

@ -1,25 +1,24 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/object_tree.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GeTreeScreen extends StatelessWidget {
const GeTreeScreen(this.owner, this.name, this.sha);
final String owner;
final String name;
final String sha;
const GeTreeScreen(this.owner, this.name, this.sha);
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<List<GiteeTreeItem>>(
title: AppBarTitle(AppLocalizations.of(context)!.files),
title: Text(AppLocalizations.of(context)!.files),
fetch: () async {
final res = await context
.read<AuthModel>()
@ -31,24 +30,19 @@ class GeTreeScreen extends StatelessWidget {
return items;
},
bodyBuilder: (data, _) {
return TableView(
items: [
return AntList(
children: [
for (var item in data)
ObjectTreeItem(
createObjectTreeItem(
type: item.type,
name: item.path,
size: item.size,
downloadUrl: '', // TODO:
url: (() {
switch (item.type) {
case 'tree':
return '/gitee/$owner/$name/tree/${item.sha}?path=${item.path.urlencode}';
case 'blob':
return '/gitee/$owner/$name/blob/${item.sha}?path=${item.path.urlencode}';
default:
return null;
}
})(),
url: item.type == 'tree'
? '/gitee/$owner/$name/tree/${item.sha}?path=${item.path.urlencode}'
: item.type == 'blob'
? '/gitee/$owner/$name/blob/${item.sha}?path=${item.path.urlencode}'
: '',
)
],
);

View File

@ -1,23 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/widgets/entry_item.dart';
import 'package:git_touch/widgets/repository_item.dart';
import 'package:git_touch/widgets/repo_item.dart';
import 'package:git_touch/widgets/user_header.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:tuple/tuple.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:tuple/tuple.dart';
class GeUserScreen extends StatelessWidget {
const GeUserScreen(this.login, {this.isViewer = false});
final String login;
final bool isViewer;
const GeUserScreen(this.login, {this.isViewer = false});
@override
Widget build(BuildContext context) {
@ -33,7 +33,7 @@ class GeUserScreen extends StatelessWidget {
[for (var v in res[1]) GiteeRepo.fromJson(v)],
);
},
title: AppBarTitle(isViewer ? 'Me' : login),
title: Text(isViewer ? 'Me' : login),
action: isViewer
? const ActionEntry(
iconData: Ionicons.cog,
@ -65,30 +65,30 @@ class GeUserScreen extends StatelessWidget {
CommonStyle.border,
Row(children: [
EntryItem(
count: user.publicRepos,
count: user.publicRepos!,
text: 'Repositories',
url: '/gitee/$login?tab=repositories',
),
EntryItem(
count: user.stared,
count: user.stared!,
text: 'Stars',
url: '/gitee/$login?tab=stars',
),
EntryItem(
count: user.followers,
count: user.followers!,
text: 'Followers',
url: '/gitee/$login?tab=followers',
),
EntryItem(
count: user.following,
count: user.following!,
text: 'Following',
url: '/gitee/$login?tab=following',
),
]),
// TableView(
// AntList(
// hasIcon: true,
// items: [
// TableViewItem(
// AntListItem(
// leftIconData: Octicons.home,
// text: Text('Organizations'),
// url: '/gitee/$login?tab=organizations',
@ -99,7 +99,7 @@ class GeUserScreen extends StatelessWidget {
Column(
children: <Widget>[
for (var v in repos)
RepositoryItem(
RepoItem(
owner: v.namespace!.path,
avatarUrl: v.owner!.avatarUrl,
name: v.path,

View File

@ -1,21 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/user_item.dart';
import 'package:git_touch/models/auth.dart';
import 'package:provider/provider.dart';
class GeUsersScreen extends StatelessWidget {
final String api;
final String title;
const GeUsersScreen.followers(String login)
: api = '/users/$login/followers',
title = 'Followers';
const GeUsersScreen.following(String login)
: api = '/users/$login/following',
title = "Following";
title = 'Following';
// GeUsersScreen.member(String login)
// : api = '/orgs/$login/members',
// title = "Members";
@ -25,11 +21,13 @@ class GeUsersScreen extends StatelessWidget {
const GeUsersScreen.watchers(String owner, String repo)
: api = '/repos/$owner/$repo/subscribers',
title = 'Watchers';
final String api;
final String title;
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteeListUser, int>(
title: AppBarTitle(title),
title: Text(title),
fetch: (page) async {
final res =
await context.read<AuthModel>().fetchGiteeWithPage(api, page: page);

View File

@ -1,27 +1,26 @@
import 'package:ferry/ferry.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/graphql/__generated__/github.data.gql.dart';
import 'package:git_touch/graphql/__generated__/github.req.gql.dart';
import 'package:git_touch/graphql/__generated__/github.var.gql.dart';
import 'package:git_touch/graphql/__generated__/schema.schema.gql.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/commit_item.dart';
import 'package:gql_github/commits.data.gql.dart';
import 'package:gql_github/commits.req.gql.dart';
import 'package:gql_github/schema.schema.gql.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GhCommits extends StatelessWidget {
const GhCommits(this.owner, this.name, {this.branch});
final String owner;
final String name;
final String? branch;
const GhCommits(this.owner, this.name, {this.branch});
Widget _buildStatus(GStatusState? state) {
const size = 18.0;
switch (state) {
case GStatusState.SUCCESS:
return const Icon(Octicons.check, color: GithubPalette.open, size: size);
return const Icon(Octicons.check,
color: GithubPalette.open, size: size);
case GStatusState.FAILURE:
return const Icon(Octicons.x, color: GithubPalette.closed, size: size);
default:
@ -32,7 +31,7 @@ class GhCommits extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GCommitsRefCommit_history_nodes, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.commits),
title: Text(AppLocalizations.of(context)!.commits),
fetch: (cursor) async {
final req = GCommitsReq((b) {
b.vars.owner = owner;
@ -41,8 +40,8 @@ class GhCommits extends StatelessWidget {
b.vars.ref = branch ?? '';
b.vars.after = cursor;
});
final OperationResponse<GCommitsData, GCommitsVars?> res =
await context.read<AuthModel>().gqlClient.request(req).first;
final res =
await context.read<AuthModel>().ghGqlClient.request(req).first;
final ref = res.data!.repository!.defaultBranchRef ??
res.data!.repository!.ref!;
final history = (ref.target as GCommitsRefCommit).history;

View File

@ -1,25 +1,24 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/files_item.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/widgets/files_item.dart';
import 'package:provider/provider.dart';
class GhComparisonScreen extends StatelessWidget {
const GhComparisonScreen(this.owner, this.name, this.before, this.head);
final String owner;
final String name;
final String before;
final String head;
const GhComparisonScreen(this.owner, this.name, this.before, this.head);
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold(
title: AppBarTitle(AppLocalizations.of(context)!.files),
title: Text(AppLocalizations.of(context)!.files),
fetch: () async {
final res = await context.read<AuthModel>().ghClient.getJSON(
'/repos/$owner/$name/compare/$before...$head',
@ -43,7 +42,7 @@ class GhComparisonScreen extends StatelessWidget {
additions: vs.additions,
deletions: vs.deletions,
status: vs.status,
patch: vs.patch ?? "No text to be shown here",
patch: vs.patch ?? 'No text to be shown here',
))
.toList(),
);

View File

@ -1,22 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/contributor_item.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GhContributorsScreen extends StatelessWidget {
const GhContributorsScreen(this.owner, this.name);
final String owner;
final String name;
const GhContributorsScreen(this.owner, this.name);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GithubContributorItem, int>(
title: AppBarTitle(AppLocalizations.of(context)!.contributors),
title: Text(AppLocalizations.of(context)!.contributors),
fetch: (page) async {
page = page ?? 1;
final res = await context
@ -34,7 +33,7 @@ class GhContributorsScreen extends StatelessWidget {
);
},
itemBuilder: (v) {
final String? login = v.login;
final login = v.login;
return ContributorItem(
avatarUrl: v.avatarUrl,
commits: v.contributions,

View File

@ -1,31 +1,30 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/event_item.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:provider/provider.dart';
class GhEventsScreen extends StatelessWidget {
final String login;
const GhEventsScreen(this.login);
final String login;
@override
Widget build(context) {
return ListStatefulScaffold<GithubEvent, int>(
title: AppBarTitle(AppLocalizations.of(context)!.events),
title: Text(AppLocalizations.of(context)!.events),
itemBuilder: (payload) => EventItem(payload),
fetch: (page) async {
page = page ?? 1;
final events = await context.read<AuthModel>().ghClient.getJSON(
'/users/$login/events?page=$page&per_page=$PAGE_SIZE',
'/users/$login/events?page=$page&per_page=$kPageSize',
convert: (dynamic vs) =>
[for (var v in vs) GithubEvent.fromJson(v)]);
return ListPayload(
cursor: page + 1,
hasMore: events.length == PAGE_SIZE,
hasMore: events.length == kPageSize,
items: events,
);
},

View File

@ -1,24 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/files_item.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:provider/provider.dart';
class GhFilesScreen extends StatelessWidget {
const GhFilesScreen(this.owner, this.name, this.pullNumber);
final String owner;
final String name;
final int pullNumber;
const GhFilesScreen(this.owner, this.name, this.pullNumber);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GithubFilesItem, int>(
title: AppBarTitle(AppLocalizations.of(context)!.files),
title: Text(AppLocalizations.of(context)!.files),
actionBuilder: () {
return ActionButton(
title: 'Actions',

View File

@ -1,23 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/blob_view.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/blob_view.dart';
class GistObjectScreen extends StatelessWidget {
const GistObjectScreen(this.login, this.id, this.file,
{this.raw, this.content});
final String login;
final String id;
final String file;
final String? raw;
final String? content;
const GistObjectScreen(this.login, this.id, this.file, {this.raw, this.content});
@override
Widget build(BuildContext context) {
return CommonScaffold(
title: AppBarTitle(file),
title: Text(file),
action: const ActionEntry(
iconData: Ionicons.cog,
url: '/choose-code-theme',

View File

@ -1,30 +1,27 @@
import 'package:ferry/ferry.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/graphql/__generated__/github.var.gql.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/gists_item.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/graphql/__generated__/github.data.gql.dart';
import 'package:git_touch/graphql/__generated__/github.req.gql.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/gists_item.dart';
import 'package:gql_github/gists.data.gql.dart';
import 'package:gql_github/gists.req.gql.dart';
import 'package:provider/provider.dart';
class GhGistsScreen extends StatelessWidget {
final String login;
const GhGistsScreen(this.login);
final String login;
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GGistsData_user_gists_nodes, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.gists),
title: Text(AppLocalizations.of(context)!.gists),
fetch: (page) async {
final req = GGistsReq((b) => b
..vars.login = login
..vars.after = page);
final OperationResponse<GGistsData, GGistsVars?> res =
await context.read<AuthModel>().gqlClient.request(req).first;
final res =
await context.read<AuthModel>().ghGqlClient.request(req).first;
final gists = res.data!.user!.gists;
return ListPayload(
cursor: gists.pageInfo.endCursor,

View File

@ -1,52 +1,49 @@
import 'package:ferry/ferry.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/graphql/__generated__/github.data.gql.dart';
import 'package:git_touch/graphql/__generated__/github.req.gql.dart';
import 'package:git_touch/graphql/__generated__/github.var.gql.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/object_tree.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:gql_github/gist.data.gql.dart';
import 'package:gql_github/gist.req.gql.dart';
import 'package:provider/provider.dart';
class GhGistsFilesScreen extends StatelessWidget {
const GhGistsFilesScreen(this.login, this.id);
final String id;
final String login;
const GhGistsFilesScreen(this.login, this.id);
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<GGistData_user_gist?>(
title: AppBarTitle(AppLocalizations.of(context)!.files),
title: Text(AppLocalizations.of(context)!.files),
fetch: () async {
final req = GGistReq((b) => b
..vars.login = login
..vars.name = id);
final OperationResponse<GGistData, GGistVars?> res =
await context.read<AuthModel>().gqlClient.request(req).first;
final res =
await context.read<AuthModel>().ghGqlClient.request(req).first;
final gist = res.data!.user!.gist;
return gist;
},
bodyBuilder: (payload, _) {
return TableView(
items: payload!.files!.map((v) {
return AntList(
children: payload!.files!.map((v) {
final uri = Uri(
path: '/github/$login/gists/$id/${v.name}',
queryParameters: {
'content': v.text,
},
).toString();
return ObjectTreeItem(
return createObjectTreeItem(
url: uri,
type: 'file',
name: v.name ?? '',
downloadUrl: null,
size: v.size,
);
}),
}).toList(),
);
},
);

View File

@ -1,26 +1,25 @@
import 'package:ferry/ferry.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/graphql/__generated__/github.data.gql.dart';
import 'package:git_touch/graphql/__generated__/github.req.gql.dart';
import 'package:git_touch/graphql/__generated__/github.var.gql.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/long_list.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/comment_item.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:git_touch/widgets/timeline_item.dart';
import 'package:github/github.dart' as github;
import 'package:gql_github/issue.data.gql.dart';
import 'package:gql_github/issue.req.gql.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:github/github.dart' as github;
import '../scaffolds/long_list.dart';
import '../widgets/comment_item.dart';
class GhIssueScreen extends StatelessWidget {
const GhIssueScreen(this.owner, this.name, this.number);
final String owner;
final String name;
final int number;
const GhIssueScreen(this.owner, this.name, this.number);
Widget _buildHeader(
BuildContext context, {
@ -30,7 +29,6 @@ class GhIssueScreen extends StatelessWidget {
required Widget body,
Iterable<Widget> extraWidgets = const [],
}) {
final theme = Provider.of<ThemeModel>(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
@ -49,7 +47,7 @@ class GhIssueScreen extends StatelessWidget {
'$owner / $name',
style: TextStyle(
fontSize: 17,
color: theme.palette.secondaryText,
color: AntTheme.of(context).colorTextSecondary,
),
),
const SizedBox(width: 4),
@ -57,7 +55,7 @@ class GhIssueScreen extends StatelessWidget {
'#$number',
style: TextStyle(
fontSize: 17,
color: theme.palette.tertiaryText,
color: AntTheme.of(context).colorWeak,
),
),
],
@ -94,8 +92,7 @@ class GhIssueScreen extends StatelessWidget {
b.vars.number = number;
b.vars.cursor = cursor;
});
OperationResponse<GIssueData, GIssueVars?> res =
await context.read<AuthModel>().gqlClient.request(req).first;
final res = await context.read<AuthModel>().ghGqlClient.request(req).first;
return res.data!.repository!;
}
@ -136,7 +133,6 @@ class GhIssueScreen extends StatelessWidget {
}
},
headerBuilder: (p) {
final theme = Provider.of<ThemeModel>(context);
if (p.issueOrPullRequest!.G__typename == 'Issue') {
final issue = p.issueOrPullRequest
as GIssueData_repository_issueOrPullRequest__asIssue;
@ -225,24 +221,24 @@ class GhIssueScreen extends StatelessWidget {
children: <Widget>[
Text('${pr.changedFiles} files changed',
style: TextStyle(
color: theme.palette.secondaryText,
color: AntTheme.of(context).colorTextSecondary,
fontSize: 17,
)),
Row(
children: <Widget>[
Text('+${pr.additions}',
style: const TextStyle(
color: Colors.green,
style: TextStyle(
color: AntTheme.of(context).colorSuccess,
fontSize: 15,
)),
const SizedBox(width: 2),
Text('-${pr.deletions}',
style: const TextStyle(
color: Colors.red,
style: TextStyle(
color: AntTheme.of(context).colorDanger,
fontSize: 15,
)),
Icon(Ionicons.chevron_forward,
color: theme.palette.border),
color: AntTheme.of(context).colorBorder),
],
)
],

View File

@ -1,19 +1,19 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:github/github.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GhIssueFormScreen extends StatefulWidget {
const GhIssueFormScreen(this.owner, this.name);
final String owner;
final String name;
const GhIssueFormScreen(this.owner, this.name);
@override
_GhIssueFormScreenState createState() => _GhIssueFormScreenState();
State<GhIssueFormScreen> createState() => _GhIssueFormScreenState();
}
class _GhIssueFormScreenState extends State<GhIssueFormScreen> {
@ -22,8 +22,6 @@ class _GhIssueFormScreenState extends State<GhIssueFormScreen> {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
return CommonScaffold(
title: Text(AppLocalizations.of(context)!.submitAnIssue),
body: Column(
@ -31,7 +29,7 @@ class _GhIssueFormScreenState extends State<GhIssueFormScreen> {
Padding(
padding: CommonStyle.padding,
child: CupertinoTextField(
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.title,
onChanged: (v) {
setState(() {
@ -43,7 +41,7 @@ class _GhIssueFormScreenState extends State<GhIssueFormScreen> {
Padding(
padding: CommonStyle.padding,
child: CupertinoTextField(
style: TextStyle(color: theme.palette.text),
style: TextStyle(color: AntTheme.of(context).colorText),
placeholder: AppLocalizations.of(context)!.body,
onChanged: (v) {
setState(() {
@ -53,17 +51,17 @@ class _GhIssueFormScreenState extends State<GhIssueFormScreen> {
maxLines: 10,
),
),
CupertinoButton.filled(
AntButton(
color: AntTheme.of(context).colorPrimary,
child: Text(AppLocalizations.of(context)!.submit),
onPressed: () async {
onClick: () async {
final slug = RepositorySlug(widget.owner, widget.name);
final res = await context
.read<AuthModel>()
.ghClient
.issues
.create(slug, IssueRequest(title: _title, body: _body));
await theme.push(
context,
await context.pushUrl(
'/github/${widget.owner}/${widget.name}/issues/${res.number}',
replace: true,
);

View File

@ -1,26 +1,24 @@
import 'package:ferry/ferry.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/graphql/__generated__/github.data.gql.dart';
import 'package:git_touch/graphql/__generated__/github.req.gql.dart';
import 'package:git_touch/graphql/__generated__/github.var.gql.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/hex_color_tag.dart';
import 'package:git_touch/widgets/issue_item.dart';
import 'package:git_touch/widgets/label.dart';
import 'package:gql_github/issues.data.gql.dart';
import 'package:gql_github/issues.req.gql.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GhIssuesScreen extends StatelessWidget {
const GhIssuesScreen(this.owner, this.name);
final String owner;
final String name;
const GhIssuesScreen(this.owner, this.name);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GIssuesData_repository_issues_nodes, String?>(
title: AppBarTitle(AppLocalizations.of(context)!.issues),
title: Text(AppLocalizations.of(context)!.issues),
actionBuilder: () => ActionEntry(
iconData: Octicons.plus,
url: '/github/$owner/$name/issues/new',
@ -31,8 +29,8 @@ class GhIssuesScreen extends StatelessWidget {
b.vars.name = name;
b.vars.cursor = cursor;
});
final OperationResponse<GIssuesData, GIssuesVars?> res =
await context.read<AuthModel>().gqlClient.request(req).first;
final res =
await context.read<AuthModel>().ghGqlClient.request(req).first;
final issues = res.data!.repository!.issues;
return ListPayload(
cursor: issues.pageInfo.endCursor,
@ -52,7 +50,7 @@ class GhIssuesScreen extends StatelessWidget {
? null
: Wrap(spacing: 4, runSpacing: 4, children: [
for (var label in p.labels!.nodes!)
MyLabel(name: label.name, cssColor: label.color)
HexColorTag(name: label.name, color: label.color)
]),
url: '/github/$owner/$name/issues/${p.number}',
);

34
lib/screens/gh_meta.dart Normal file
View File

@ -0,0 +1,34 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:gql_github/meta.data.gql.dart';
import 'package:gql_github/meta.req.gql.dart';
import 'package:provider/provider.dart';
class GhMetaScreen extends StatelessWidget {
const GhMetaScreen({super.key});
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<GMetaData_meta>(
title: const Text('Meta'),
fetch: () async {
final req = GMetaReq();
final res =
await context.read<AuthModel>().ghGqlClient.request(req).first;
return res.data!.meta;
},
bodyBuilder: (meta, _) {
return AntList(
children: [
AntListItem(
extra: Text(meta.gitHubServicesSha),
child: const Text('Service SHA'),
),
],
);
},
);
}
}

View File

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/event_item.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:provider/provider.dart';
class GhNewsScreen extends StatefulWidget {
@override
@ -21,7 +20,7 @@ class GhNewsScreenState extends State<GhNewsScreen> {
Future.microtask(() async {
// Check if there are unread notification items.
// 1 item is enough since count is not displayed for now.
var items = await context
final items = await context
.read<AuthModel>()
.ghClient
.getJSON('/notifications?per_page=1');
@ -35,7 +34,7 @@ class GhNewsScreenState extends State<GhNewsScreen> {
@override
Widget build(context) {
return ListStatefulScaffold<GithubEvent, int>(
title: AppBarTitle(AppLocalizations.of(context)!.news),
title: Text(AppLocalizations.of(context)!.news),
itemBuilder: (payload) => EventItem(payload),
fetch: (page) async {
page = page ?? 1;
@ -43,12 +42,12 @@ class GhNewsScreenState extends State<GhNewsScreen> {
final login = auth.activeAccount!.login;
final events = await auth.ghClient.getJSON(
'/users/$login/received_events?page=$page&per_page=$PAGE_SIZE',
'/users/$login/received_events?page=$page&per_page=$kPageSize',
convert: (dynamic vs) => [for (var v in vs) GithubEvent.fromJson(v)],
);
return ListPayload(
cursor: page + 1,
hasMore: events.length == PAGE_SIZE,
hasMore: events.length == kPageSize,
items: events,
);
},

View File

@ -1,18 +1,17 @@
import 'package:flutter/material.dart';
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/tab_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:github/github.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/notification.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/github.dart';
import '../widgets/notification_item.dart';
import '../widgets/list_group.dart';
import '../widgets/empty.dart';
import '../utils/utils.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/scaffolds/tab_stateful.dart';
import 'package:git_touch/widgets/empty.dart';
import 'package:git_touch/widgets/list_group.dart';
import 'package:git_touch/widgets/notification_item.dart';
import 'package:github/github.dart';
import 'package:provider/provider.dart';
class GhNotificationScreen extends StatefulWidget {
@override
@ -30,9 +29,9 @@ class GhNotificationScreenState extends State<GhNotificationScreen> {
context.read<NotificationModel>().setCount(ns.length);
}
Map<String, NotificationGroup> groupMap = {};
final groupMap = <String, NotificationGroup>{};
for (var item in ns) {
for (final item in ns) {
final repo = item.repository!.fullName ?? ''; // TODO: nullable
if (groupMap[repo] == null) {
groupMap[repo] = NotificationGroup(repo);
@ -56,7 +55,7 @@ class GhNotificationScreenState extends State<GhNotificationScreen> {
schema +=
'${group.key}: repository(owner: "${group.owner}", name: "${group.name}") {';
for (var item in group.items) {
for (final item in group.items) {
switch (item.subject!.type) {
case 'Issue':
schema += '''
@ -82,13 +81,13 @@ ${item.key}: pullRequest(number: ${item.subject!.number}) {
if (schema == '{}') return groupMap;
// Fimber.d(schema);
var data = await context.read<AuthModel>().query(schema);
final data = await context.read<AuthModel>().query(schema);
groupMap.forEach((repo, group) {
for (var item in group.items) {
var groupData = data[group.key];
for (final item in group.items) {
final groupData = data[group.key];
if (groupData == null) continue;
var itemData = data[group.key][item.key];
final itemData = data[group.key][item.key];
if (itemData != null) {
item.state = itemData['state'];
}
@ -105,7 +104,6 @@ ${item.key}: pullRequest(number: ${item.subject!.number}) {
MapEntry<String, NotificationGroup> entry,
Map<String, NotificationGroup> groupMap,
) {
final theme = Provider.of<ThemeModel>(context);
final group = entry.value;
return ListGroup(
title: Row(
@ -116,7 +114,7 @@ ${item.key}: pullRequest(number: ${item.subject!.number}) {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: theme.palette.text,
color: AntTheme.of(context).colorText,
),
),
GestureDetector(
@ -131,7 +129,7 @@ ${item.key}: pullRequest(number: ${item.subject!.number}) {
},
child: Icon(
Ionicons.checkmark_done,
color: theme.palette.tertiaryText,
color: AntTheme.of(context).colorWeak,
size: 24,
),
),
@ -156,7 +154,7 @@ ${item.key}: pullRequest(number: ${item.subject!.number}) {
@override
Widget build(context) {
return TabStatefulScaffold<Map<String, NotificationGroup>>(
title: AppBarTitle(AppLocalizations.of(context)!.notification),
title: Text(AppLocalizations.of(context)!.notification),
tabs: [
AppLocalizations.of(context)!.unread,
AppLocalizations.of(context)!.participating,

View File

@ -1,29 +1,29 @@
import 'package:antd_mobile/antd_mobile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/blob_view.dart';
import 'package:git_touch/widgets/object_tree.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:github/github.dart';
import 'package:provider/provider.dart';
class GhObjectScreen extends StatelessWidget {
const GhObjectScreen(this.owner, this.name, this.ref, {this.path, this.raw});
final String owner;
final String name;
final String ref;
final String? path;
final String? raw;
const GhObjectScreen(this.owner, this.name, this.ref, {this.path, this.raw});
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<RepositoryContents>(
// canRefresh: !_isImage, // TODO:
title: AppBarTitle(path ?? 'Files'),
title: Text(path ?? 'Files'),
fetch: () async {
// Do not request again for images
if (path != null &&
@ -59,8 +59,8 @@ class GhObjectScreen extends StatelessWidget {
},
bodyBuilder: (data, _) {
if (data.isDirectory) {
return TableView(
items: data.tree!.map((v) {
return AntList(
children: data.tree!.map((v) {
// if (item.type == 'commit') return null;
final uri = Uri(
path: '/github/$owner/$name/blob/$ref',
@ -69,14 +69,14 @@ class GhObjectScreen extends StatelessWidget {
...(v.downloadUrl == null ? {} : {'raw': v.downloadUrl}),
},
).toString();
return ObjectTreeItem(
return createObjectTreeItem(
name: v.name ?? '',
type: v.type ?? '',
url: uri.toString(),
downloadUrl: v.downloadUrl,
size: v.type == 'file' ? v.size : null,
);
}),
}).toList(),
);
} else {
// TODO: Markdown base path

View File

@ -1,56 +0,0 @@
import 'package:flutter/material.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/repository_item.dart';
import 'package:provider/provider.dart';
import 'package:github/github.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:flutter_gen/gen_l10n/S.dart';
/// There are some restrictions of organization repos with OAuth
///
/// https://help.github.com/en/github/setting-up-and-managing-organizations-and-teams/restricting-access-to-your-organizations-data
///
/// So we use RESTful API here for repos of org
class GhOrgReposScreen extends StatelessWidget {
final String owner;
const GhOrgReposScreen(this.owner);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<Repository, int>(
title: AppBarTitle(AppLocalizations.of(context)!.repositories),
fetch: (page) async {
page = page ?? 1;
final rs = await context
.read<AuthModel>()
.ghClient
.getJSON<List, List<Repository>>(
'/orgs/$owner/repos?sort=updated&page=$page',
convert: (vs) => [for (var v in vs) Repository.fromJson(v)],
);
return ListPayload(
cursor: page + 1,
items: rs,
hasMore: rs.isNotEmpty, // TODO:
);
},
itemBuilder: (v) {
return RepositoryItem.gh(
owner: v.owner!.login,
avatarUrl: v.owner!.avatarUrl,
name: v.name,
description: v.description,
starCount: v.stargazersCount,
forkCount: v.forksCount,
primaryLanguageName: v.language,
primaryLanguageColor: null,
note: 'Updated ${timeago.format(v.updatedAt!)}',
isPrivate: v.isPrivate,
isFork: v.isFork,
);
},
);
}
}

View File

@ -1,45 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/user_item.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:flutter_gen/gen_l10n/S.dart';
class GhUserOrganizationScreen extends StatelessWidget {
final String login;
const GhUserOrganizationScreen(this.login);
@override
Widget build(BuildContext context) {
return ListStatefulScaffold<GithubUserOrganizationItem, int>(
title: AppBarTitle(AppLocalizations.of(context)!.organizations),
fetch: (page) async {
page = page ?? 1;
final res = await context
.read<AuthModel>()
.ghClient
.getJSON<List, List<GithubUserOrganizationItem>>(
'/users/$login/orgs?page=$page',
convert: (vs) =>
[for (var v in vs) GithubUserOrganizationItem.fromJson(v)],
);
return ListPayload(
cursor: page + 1,
items: res,
hasMore: res.isNotEmpty,
);
},
itemBuilder: (v) {
return UserItem.github(
avatarUrl: v.avatarUrl,
login: v.login,
name: null, // TODO: organization name
bio: v.description == null ? null : Text(v.description!),
);
},
);
}
}

Some files were not shown because too many files have changed in this diff Show More