import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:function_types/function_types.dart'; import 'package:intl/intl.dart'; import 'package:path/path.dart' as p; import 'package:time/time.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:gitjournal/analytics.dart'; import 'package:gitjournal/apis/githost_factory.dart'; import 'package:gitjournal/error_reporting.dart'; import 'package:gitjournal/setup/button.dart'; import 'package:gitjournal/setup/error.dart'; import 'package:gitjournal/setup/loading.dart'; import 'package:gitjournal/utils/logger.dart'; //import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class GitHostSetupRepoSelector extends StatefulWidget { final GitHost gitHost; final UserInfo userInfo; final Func1 onDone; GitHostSetupRepoSelector({ @required this.gitHost, @required this.userInfo, @required this.onDone, }); @override GitHostSetupRepoSelectorState createState() { return GitHostSetupRepoSelectorState(); } } class GitHostSetupRepoSelectorState extends State { String errorMessage = ""; List repos = []; var fetchedRepos = false; GitHostRepo selectedRepo; var _textController = TextEditingController(); bool createRepo = false; @override void initState() { super.initState(); _textController.addListener(() { setState(() { selectedRepo = null; createRepo = false; }); }); _initStateAysnc(); } void _initStateAysnc() async { Log.d("Starting RepoSelector"); try { var allRepos = await widget.gitHost.listRepos(); allRepos.sort((GitHostRepo a, GitHostRepo b) { if (a.updatedAt != null && b.updatedAt != null) { return a.updatedAt.compareTo(b.updatedAt); } if (a.updatedAt == null && b.updatedAt == null) { return a.fullName.compareTo(b.fullName); } if (a.updatedAt == null) { return 1; } return -1; }); if (!mounted) return; setState(() { repos = allRepos.reversed.toList(); fetchedRepos = true; }); var repo = repos.firstWhere( (r) => r.fullName.endsWith('/journal'), orElse: () => null, ); if (repo != null) { setState(() { selectedRepo = repo; }); } else { setState(() { _textController.text = "journal"; createRepo = true; }); } } on Exception catch (e, stacktrace) { _handleGitHostException(e, stacktrace); return; } } void _handleGitHostException(Exception e, StackTrace stacktrace) { Log.d("GitHostSetupAutoConfigure: " + e.toString()); setState(() { errorMessage = e.toString(); logEvent(Event.GitHostSetupError, parameters: { 'errorMessage': errorMessage, }); logException(e, stacktrace); }); } @override Widget build(BuildContext context) { if (errorMessage != null && errorMessage.isNotEmpty) { return GitHostSetupErrorPage(errorMessage); } if (!fetchedRepos) { return GitHostSetupLoadingPage("Loading"); } var q = _textController.text.toLowerCase(); var filteredRepos = repos.where((r) { var repoName = r.fullName.split('/').last; return repoName.toLowerCase().contains(q); }).toList(); var repoExists = filteredRepos.indexWhere((r) { var l = r.fullName.split('/'); var username = l.first; var repoName = l.last; return repoName.toLowerCase() == _textController.text && username == widget.userInfo.username; }) != -1; var createRepoTile = _textController.text.isNotEmpty && !repoExists; Widget repoBuilder = ListView( children: [ if (createRepoTile) _buildCreateRepoTile(), for (var repo in filteredRepos) _RepoTile( repo: repo, onTap: () { setState(() { selectedRepo = repo; createRepo = false; }); }, selected: repo == selectedRepo, ), ], padding: const EdgeInsets.all(0.0), ); // Remove Overflow animation repoBuilder = NotificationListener( onNotification: (OverscrollIndicatorNotification overScroll) { overScroll.disallowGlow(); return false; }, child: repoBuilder, ); // Add a Filtering bar // text: Type to search or create var textField = TextField( controller: _textController, maxLines: 1, decoration: InputDecoration( hintText: tr('setup.repoSelector.hint'), border: const OutlineInputBorder(), suffixIcon: IconButton( onPressed: () => _textController.clear(), icon: const Icon(Icons.clear), ), ), ); bool canContinue = selectedRepo != null || createRepo; var columns = Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( tr('setup.repoSelector.title'), style: Theme.of(context).textTheme.headline6, ), const SizedBox(height: 16.0), textField, const SizedBox(height: 8.0), Expanded(child: repoBuilder), const SizedBox(height: 8.0), Opacity( opacity: canContinue ? 1.0 : 0.0, child: GitHostSetupButton( text: tr('setup.next'), onPressed: () async { if (selectedRepo != null) { widget.onDone(selectedRepo); return; } try { var repoName = _textController.text.trim(); var repo = await widget.gitHost.createRepo(repoName); widget.onDone(repo); return; } catch (e, stacktrace) { _handleGitHostException(e, stacktrace); } }, ), ), const SizedBox(height: 32.0), ], ); return SafeArea(child: Center(child: columns)); } Widget _buildCreateRepoTile() { var repoName = _textController.text.trim(); var fullRepoName = p.join(widget.userInfo.username, repoName); return ListTile( leading: const Icon(Icons.add), title: Align( child: Text(tr('setup.repoSelector.create', args: [fullRepoName])), alignment: const Alignment(-1.3, 0), ), contentPadding: const EdgeInsets.all(0.0), onTap: () { setState(() { createRepo = true; selectedRepo = null; }); }, selected: createRepo, ); } } class _RepoTile extends StatelessWidget { final GitHostRepo repo; final Function onTap; final bool selected; _RepoTile({ @required this.repo, @required this.onTap, @required this.selected, }); @override Widget build(BuildContext context) { var theme = Theme.of(context); var textTheme = theme.textTheme; /* var iconsRow = Row( children: [ if (repo.license != null) _IconText(repo.license, FontAwesomeIcons.balanceScale), if (repo.license != null) const SizedBox(width: 8.0), _IconText(repo.forks.toString(), FontAwesomeIcons.codeBranch), const SizedBox(width: 8.0), _IconText(repo.stars.toString(), FontAwesomeIcons.star), ], crossAxisAlignment: CrossAxisAlignment.center, ); return Card( margin: const EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 4.0), elevation: 0.0, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Text( repo.fullName, style: textTheme.headline6.copyWith(color: theme.primaryColorDark), ), if (repo.description != null) const SizedBox(height: 8.0), if (repo.description != null) Text(repo.description), const SizedBox(height: 16.0), iconsRow, ], mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, ), ), ); */ return ListTile( title: Text(repo.fullName), trailing: _SmartDateTime(repo.updatedAt, textTheme.caption), selected: selected, contentPadding: const EdgeInsets.all(0.0), onTap: onTap, ); } } /* class _IconText extends StatelessWidget { final String text; final IconData iconData; _IconText(this.text, this.iconData); @override Widget build(BuildContext context) { var iconTheme = Theme.of(context).iconTheme; var iconColor = iconTheme.color.withAlpha(150); var textTheme = Theme.of(context).textTheme; return Row( children: [ FaIcon(iconData, size: 16, color: iconColor), const SizedBox(width: 4.0), Text(text, style: textTheme.caption), ], textBaseline: TextBaseline.alphabetic, crossAxisAlignment: CrossAxisAlignment.baseline, mainAxisSize: MainAxisSize.min, ); } } */ class _SmartDateTime extends StatelessWidget { final DateTime dt; final TextStyle style; _SmartDateTime(this.dt, this.style); static final _dateFormat = DateFormat('d MMM yyyy'); static final _dateFormatWithoutYear = DateFormat('d MMM'); static final thirtyDaysAgo = DateTime.now().subtract(30.days); @override Widget build(BuildContext context) { if (dt == null) { return Container(); } String text; if (dt.isAfter(thirtyDaysAgo)) { Locale locale = Localizations.localeOf(context); text = timeago.format(dt, locale: locale.languageCode); } else if (dt.year == DateTime.now().year) { text = _dateFormatWithoutYear.format(dt); } else { text = _dateFormat.format(dt); } return Text(text, style: style); } } /* const _langColors = { "Mercury": "#ff2b2b", "TypeScript": "#2b7489", "PureBasic": "#5a6986", "Objective-C++": "#6866fb", "Self": "#0579aa", "NewLisp": "#87AED7", "Fortran": "#4d41b1", "Ceylon": "#dfa535", "Rebol": "#358a5b", "Frege": "#00cafe", "AspectJ": "#a957b0", "Omgrofl": "#cabbff", "HolyC": "#ffefaf", "Shell": "#89e051", "HiveQL": "#dce200", "AppleScript": "#101F1F", "Eiffel": "#946d57", "XQuery": "#5232e7", "RUNOFF": "#665a4e", "RAML": "#77d9fb", "MTML": "#b7e1f4", "Elixir": "#6e4a7e", "SAS": "#B34936", "MQL4": "#62A8D6", "MQL5": "#4A76B8", "Agda": "#315665", "wisp": "#7582D1", "Dockerfile": "#384d54", "SRecode Template": "#348a34", "D": "#ba595e", "PowerBuilder": "#8f0f8d", "Kotlin": "#F18E33", "Opal": "#f7ede0", "TI Program": "#A0AA87", "Crystal": "#000100", "Objective-C": "#438eff", "Batchfile": "#C1F12E", "Oz": "#fab738", "Mirah": "#c7a938", "ZIL": "#dc75e5", "Objective-J": "#ff0c5a", "ANTLR": "#9DC3FF", "Roff": "#ecdebe", "Ragel": "#9d5200", "FreeMarker": "#0050b2", "Gosu": "#82937f", "Zig": "#ec915c", "Ruby": "#701516", "Nemerle": "#3d3c6e", "Jupyter Notebook": "#DA5B0B", "Component Pascal": "#B0CE4E", "Nextflow": "#3ac486", "Brainfuck": "#2F2530", "SystemVerilog": "#DAE1C2", "APL": "#5A8164", "Hack": "#878787", "Go": "#00ADD8", "Ring": "#2D54CB", "PHP": "#4F5D95", "Cirru": "#ccccff", "SQF": "#3F3F3F", "ZAP": "#0d665e", "Glyph": "#c1ac7f", "1C Enterprise": "#814CCC", "WebAssembly": "#04133b", "Java": "#b07219", "MAXScript": "#00a6a6", "Scala": "#c22d40", "Makefile": "#427819", "Perl": "#0298c3", "Jsonnet": "#0064bd", "Arc": "#aa2afe", "LLVM": "#185619", "GDScript": "#355570", "Verilog": "#b2b7f8", "Factor": "#636746", "Haxe": "#df7900", "Forth": "#341708", "Red": "#f50000", "YARA": "#220000", "Hy": "#7790B2", "mcfunction": "#E22837", "Volt": "#1F1F1F", "AngelScript": "#C7D7DC", "LSL": "#3d9970", "eC": "#913960", "Terra": "#00004c", "CoffeeScript": "#244776", "HTML": "#e34c26", "Lex": "#DBCA00", "UnrealScript": "#a54c4d", "Idris": "#b30000", "Swift": "#ffac45", "Modula-3": "#223388", "C": "#555555", "AutoHotkey": "#6594b9", "P4": "#7055b5", "Isabelle": "#FEFE00", "G-code": "#D08CF2", "Metal": "#8f14e9", "Clarion": "#db901e", "Vue": "#2c3e50", "JSONiq": "#40d47e", "Boo": "#d4bec1", "AutoIt": "#1C3552", "Genie": "#fb855d", "Clojure": "#db5855", "EQ": "#a78649", "Visual Basic": "#945db7", "CSS": "#563d7c", "Prolog": "#74283c", "SourcePawn": "#5c7611", "AMPL": "#E6EFBB", "Shen": "#120F14", "wdl": "#42f1f4", "Harbour": "#0e60e3", "Yacc": "#4B6C4B", "Tcl": "#e4cc98", "Quake": "#882233", "BlitzMax": "#cd6400", "PigLatin": "#fcd7de", "xBase": "#403a40", "Lasso": "#999999", "Processing": "#0096D8", "VHDL": "#adb2cb", "Elm": "#60B5CC", "Dhall": "#dfafff", "Propeller Spin": "#7fa2a7", "Rascal": "#fffaa0", "Alloy": "#64C800", "IDL": "#a3522f", "Slice": "#003fa2", "YASnippet": "#32AB90", "ATS": "#1ac620", "Ada": "#02f88c", "Nu": "#c9df40", "LFE": "#4C3023", "SuperCollider": "#46390b", "Oxygene": "#cdd0e3", "ASP": "#6a40fd", "Assembly": "#6E4C13", "Gnuplot": "#f0a9f0", "FLUX": "#88ccff", "C#": "#178600", "Turing": "#cf142b", "Vala": "#fbe5cd", "ECL": "#8a1267", "ObjectScript": "#424893", "NetLinx": "#0aa0ff", "Perl 6": "#0000fb", "MATLAB": "#e16737", "Emacs Lisp": "#c065db", "Stan": "#b2011d", "SaltStack": "#646464", "Gherkin": "#5B2063", "QML": "#44a51c", "Pike": "#005390", "DataWeave": "#003a52", "LOLCODE": "#cc9900", "ooc": "#b0b77e", "XSLT": "#EB8CEB", "XC": "#99DA07", "J": "#9EEDFF", "Mask": "#f97732", "EmberScript": "#FFF4F3", "TeX": "#3D6117", "Pep8": "#C76F5B", "R": "#198CE7", "Cuda": "#3A4E3A", "KRL": "#28430A", "Vim script": "#199f4b", "Lua": "#000080", "Asymptote": "#4a0c0c", "Ren'Py": "#ff7f7f", "Golo": "#88562A", "PostScript": "#da291c", "Fancy": "#7b9db4", "OCaml": "#3be133", "ColdFusion": "#ed2cd6", "Pascal": "#E3F171", "F#": "#b845fc", "API Blueprint": "#2ACCA8", "ActionScript": "#882B0F", "F*": "#572e30", "Fantom": "#14253c", "Zephir": "#118f9e", "Click": "#E4E6F3", "Smalltalk": "#596706", "Ballerina": "#FF5000", "DM": "#447265", "Ioke": "#078193", "PogoScript": "#d80074", "LiveScript": "#499886", "JavaScript": "#f1e05a", "Wollok": "#a23738", "Rust": "#dea584", "ABAP": "#E8274B", "ZenScript": "#00BCD1", "Slash": "#007eff", "Erlang": "#B83998", "Pan": "#cc0000", "LookML": "#652B81", "Scheme": "#1e4aec", "Squirrel": "#800000", "Nim": "#37775b", "Python": "#3572A5", "Max": "#c4a79c", "Solidity": "#AA6746", "Common Lisp": "#3fb68b", "Dart": "#00B4AB", "Nix": "#7e7eff", "Nearley": "#990000", "Nit": "#009917", "Chapel": "#8dc63f", "Groovy": "#e69f56", "Dylan": "#6c616e", "E": "#ccce35", "Parrot": "#f3ca0a", "Grammatical Framework": "#79aa7a", "Game Maker Language": "#71b417", "VCL": "#148AA8", "Papyrus": "#6600cc", "C++": "#f34b7d", "NetLinx+ERB": "#747faa", "Common Workflow Language": "#B5314C", "Clean": "#3F85AF", "X10": "#4B6BEF", "Puppet": "#302B6D", "Jolie": "#843179", "PLSQL": "#dad8d8", "sed": "#64b970", "Pawn": "#dbb284", "Standard ML": "#dc566d", "PureScript": "#1D222D", "Julia": "#a270ba", "nesC": "#94B0C7", "q": "#0040cd", "Haskell": "#5e5086", "NCL": "#28431f", "Io": "#a9188d", "Rouge": "#cc0088", "Racket": "#3c5caa", "NetLogo": "#ff6375", "AGS Script": "#B9D9FF", "Meson": "#007800", "Dogescript": "#cca760", "PowerShell": "#012456" }; */