Compare commits
2 commits
62dccf75c8
...
dfc2de6e33
Author | SHA1 | Date | |
---|---|---|---|
dfc2de6e33 | |||
887ef4cfd4 |
2 changed files with 125 additions and 46 deletions
32
features.org
Normal file
32
features.org
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
* Features non exhaustive list of TODOs
|
||||||
|
|
||||||
|
Some todos collected from source code, sorted roughly in the order of importance
|
||||||
|
- [-] *Most important features* [0%]
|
||||||
|
- [-] Navigation stack with loaded items and urls [1/3]
|
||||||
|
- [X] Capture the back key
|
||||||
|
- [ ] Add buttons to go back & forth
|
||||||
|
- [ ] Show toast at the end of history and exit after double click on back arrow
|
||||||
|
- [ ] Line wrap togglable for text files and menus [0/3]
|
||||||
|
- [ ] Add toolbar with disable wrap button
|
||||||
|
- [ ] Menu should be nonwrapping by default
|
||||||
|
- [ ] Add option to wrap only filenames and not info items as they may be just decoration
|
||||||
|
- [ ] [[https://github.com/llfbandit/app_links/blob/master/doc/README_android.md][Register to handle]] ~gopher://~ protocol
|
||||||
|
- [ ] Keep toolbar url up to date
|
||||||
|
- [ ] Fix line reading from socket
|
||||||
|
- [ ] Bookmarks
|
||||||
|
- [ ] Picture view with option to save to file
|
||||||
|
- [ ] Binary file screen with option to save to file
|
||||||
|
- [ ] Option to "Open in app" for all files that we don't know how to handle (pdf, exec binaries, sound files)
|
||||||
|
- [ ] Homepage or custom homepage
|
||||||
|
- [ ] Working user input
|
||||||
|
- [ ] *Configuration options*
|
||||||
|
- [ ] Option to select whether back arrow goes Up directory tree ro back in navigation stack
|
||||||
|
- [ ] *Nice to haves* [0%]
|
||||||
|
- [ ] Icon
|
||||||
|
- [ ] Preferences for navigation stack depth (both loaded & url stacks)
|
||||||
|
- [ ] Preferences for
|
||||||
|
- [ ] color scheme
|
||||||
|
- [ ] font -seperate for ASCII art and plain text files
|
||||||
|
- [ ] font size
|
||||||
|
- [ ] Separate gopher lib back to library
|
||||||
|
|
|
@ -7,16 +7,31 @@ class _GopherBrowserState extends State<GopherBrowser> {
|
||||||
// TODO: We must have button: Menu! to attempt to render any text file as menu
|
// TODO: We must have button: Menu! to attempt to render any text file as menu
|
||||||
// Because when we visit from direct url we don't know what file type we get
|
// Because when we visit from direct url we don't know what file type we get
|
||||||
// Other detection methods could be applied later
|
// Other detection methods could be applied later
|
||||||
|
// This may not be the most common use case so maybe hide it in some hayburger menu
|
||||||
String currentUrl = "gopher://treebrary.org";
|
String currentUrl = "gopher://treebrary.org";
|
||||||
// Directory listing doesn't have to end with / so we can never
|
// Directory listing doesn't have to end with / so we can never
|
||||||
// be sure whether we're visiting menu or not.
|
// be sure whether we're visiting menu or not.
|
||||||
// When true, we try to parse anything as menu
|
// When true, we try to parse anything as menu
|
||||||
bool forceMenu = false;
|
bool forceMenu = false;
|
||||||
|
|
||||||
|
/// Only links to session history, will be much bigger than [_loadedStack]
|
||||||
|
// Probably remove from here
|
||||||
|
final List<ParsedGopherItem> _urlStack = List<ParsedGopherItem>.empty(growable: true);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_currentItem = ParsedGopherItem.parseUrl(widget.initialUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
late ParsedGopherItem _currentItem;
|
||||||
void gotoUrl(String link) {
|
void gotoUrl(String link) {
|
||||||
|
print("GoTo: $link from: $currentUrl");
|
||||||
if (link == currentUrl) return;
|
if (link == currentUrl) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
print("prev: $currentUrl Submitted: $link");
|
print("prev: Submitted: $link");
|
||||||
|
_currentItem = ParsedGopherItem.parseUrl(link);
|
||||||
|
_urlStack.add(_currentItem);
|
||||||
currentUrl = link;
|
currentUrl = link;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -24,17 +39,21 @@ class _GopherBrowserState extends State<GopherBrowser> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
print("GopherBrowseState: $currentUrl");
|
print("GopherBrowseState: $currentUrl");
|
||||||
return Column(
|
return Builder(builder: (context) {
|
||||||
children: [
|
return Column(
|
||||||
TextField(onSubmitted: gotoUrl),
|
children: [
|
||||||
Expanded(
|
TextField(
|
||||||
|
onSubmitted: gotoUrl,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
child: GopherLoader(
|
child: GopherLoader(
|
||||||
key: ValueKey(currentUrl),
|
key: ValueKey(currentUrl),
|
||||||
forceMenu: forceMenu,
|
forceMenu: forceMenu,
|
||||||
parsedGopherItem: ParsedGopherItem.parseUrl(currentUrl),
|
parsedGopherItem: _currentItem,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,57 +68,85 @@ class GopherBrowser extends StatefulWidget {
|
||||||
class _GopherLoaderState extends State<GopherLoader> {
|
class _GopherLoaderState extends State<GopherLoader> {
|
||||||
Future<LoadedGopherItem>? _data;
|
Future<LoadedGopherItem>? _data;
|
||||||
|
|
||||||
|
final List<LoadedGopherItem> _loadedHistory =
|
||||||
|
List<LoadedGopherItem>.empty(growable: true);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu);
|
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu).then(
|
||||||
print("Reiniterd");
|
(value) {
|
||||||
|
print("Pushed beacuse of initState");
|
||||||
|
return _onLoadFinished(value);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
print("Reinitred GopherLoaderState");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
LoadedGopherItem _onLoadFinished(LoadedGopherItem data) {
|
||||||
void didChangeDependencies() {
|
_loadedHistory.add(data);
|
||||||
super.didChangeDependencies();
|
print("Added to histor: ${_loadedHistory.length}");
|
||||||
print("did:change: _data $_data");
|
return data;
|
||||||
setState(() {
|
|
||||||
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu);
|
|
||||||
});
|
|
||||||
|
|
||||||
print('didChangeDependencies, mounted: ${1} done? ');
|
|
||||||
print("$_data");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToNext(ParsedGopherItem item) {
|
void _goToNext(ParsedGopherItem item, {String? why}) {
|
||||||
|
print("Going to next: $why");
|
||||||
setState(() {
|
setState(() {
|
||||||
print("requesting view of: ${item.url} ${item.documentType}");
|
print("requesting view of: ${item.url} ${item.documentType}");
|
||||||
_data = item.load(forceMenu: false);
|
_data = item.load(forceMenu: false).then(
|
||||||
|
(value) {
|
||||||
|
print("Pushed beacuse of gotoNext");
|
||||||
|
return _onLoadFinished(value);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onBackButton(bool popable, object) {
|
||||||
|
print("Gonna pop i guess");
|
||||||
|
// TODO: toast if at the end of history, make time stamp and quit if pressed back button quickly again
|
||||||
|
// TODO: Setting on whether back button goes up a directory or back in navigation stack -> may be the same but might not be
|
||||||
|
if (_loadedHistory.length > 1) {
|
||||||
|
_loadedHistory.removeLast();
|
||||||
|
setState(() {
|
||||||
|
_data = Future<LoadedGopherItem>.delayed(
|
||||||
|
Duration.zero, () => _loadedHistory.last);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// TODO: Toast here
|
||||||
|
print("At the end of stack");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Center(
|
return PopScope(
|
||||||
child: FutureBuilder<LoadedGopherItem>(
|
canPop: false,
|
||||||
future: _data,
|
onPopInvokedWithResult: _onBackButton,
|
||||||
builder: (cont, snap) {
|
child: FutureBuilder<LoadedGopherItem>(
|
||||||
print("${snap.connectionState}");
|
future: _data,
|
||||||
if (snap.connectionState != ConnectionState.done) {
|
builder: (cont, snap) {
|
||||||
return const CircularProgressIndicator();
|
print("${snap.connectionState}");
|
||||||
|
if (snap.connectionState != ConnectionState.done) {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
} else {
|
||||||
|
if (snap.hasData) {
|
||||||
|
print(
|
||||||
|
"data & doctype: ${snap.data!}, ${snap.data!.parsed.documentType}");
|
||||||
|
return switch (snap.data!.parsed.documentType) {
|
||||||
|
DocumentType.TextFile => GopherText(snap.data!),
|
||||||
|
DocumentType.Directory => GopherDirectory(
|
||||||
|
parsedDirectory: snap.data!,
|
||||||
|
onItemClick: _goToNext,
|
||||||
|
),
|
||||||
|
_ => Container(color: Colors.yellow),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
if (snap.hasData) {
|
return Text("Errored ${snap.error}");
|
||||||
print("data & doctype: ${snap.data!}, ${snap.data!.parsed.documentType}, ");
|
|
||||||
return switch (snap.data!.parsed.documentType) {
|
|
||||||
DocumentType.TextFile => GopherText(snap.data!),
|
|
||||||
DocumentType.Directory => GopherDirectory(
|
|
||||||
parsedDirectory: snap.data!,
|
|
||||||
onItemClick: goToNext,
|
|
||||||
),
|
|
||||||
_ => Container(color: Colors.yellow),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return const Text("Errored");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue