Get Super basic navigation stack going

This commit is contained in:
Felisp 2024-11-07 01:51:58 +01:00
parent 887ef4cfd4
commit dfc2de6e33
2 changed files with 108 additions and 55 deletions

View file

@ -1,13 +1,17 @@
- [ ] Most important features [0%]
- [ ] Navigation stack with loaded items and urls
- Capture the back key
- Add buttons to go back & forth
- Figure out a way to exit with back arrow
- Maybe go with back arrow until the begginging of navigation stack and then double click?
- [ ] [[https://github.com/llfbandit/app_links/blob/master/doc/README_android.md][Register to handle]] ~gopher://~ protocol
- [ ] Line wrap togglable for text files and menus
* 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
@ -15,7 +19,9 @@
- [ ] 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
- [ ] Nice to haves
- [ ] *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

View file

@ -7,16 +7,31 @@ class _GopherBrowserState extends State<GopherBrowser> {
// 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
// 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";
// Directory listing doesn't have to end with / so we can never
// be sure whether we're visiting menu or not.
// When true, we try to parse anything as menu
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) {
print("GoTo: $link from: $currentUrl");
if (link == currentUrl) return;
setState(() {
print("prev: $currentUrl Submitted: $link");
print("prev: Submitted: $link");
_currentItem = ParsedGopherItem.parseUrl(link);
_urlStack.add(_currentItem);
currentUrl = link;
});
}
@ -24,17 +39,21 @@ class _GopherBrowserState extends State<GopherBrowser> {
@override
Widget build(BuildContext context) {
print("GopherBrowseState: $currentUrl");
return Column(
children: [
TextField(onSubmitted: gotoUrl),
Expanded(
return Builder(builder: (context) {
return Column(
children: [
TextField(
onSubmitted: gotoUrl,
),
Expanded(
child: GopherLoader(
key: ValueKey(currentUrl),
forceMenu: forceMenu,
parsedGopherItem: ParsedGopherItem.parseUrl(currentUrl),
)),
],
);
key: ValueKey(currentUrl),
forceMenu: forceMenu,
parsedGopherItem: _currentItem,
)),
],
);
});
}
}
@ -49,57 +68,85 @@ class GopherBrowser extends StatefulWidget {
class _GopherLoaderState extends State<GopherLoader> {
Future<LoadedGopherItem>? _data;
final List<LoadedGopherItem> _loadedHistory =
List<LoadedGopherItem>.empty(growable: true);
@override
void initState() {
super.initState();
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu);
print("Reiniterd");
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu).then(
(value) {
print("Pushed beacuse of initState");
return _onLoadFinished(value);
},
);
print("Reinitred GopherLoaderState");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("did:change: _data $_data");
setState(() {
_data = widget.parsedGopherItem.load(forceMenu: widget.forceMenu);
});
print('didChangeDependencies, mounted: ${1} done? ');
print("$_data");
LoadedGopherItem _onLoadFinished(LoadedGopherItem data) {
_loadedHistory.add(data);
print("Added to histor: ${_loadedHistory.length}");
return data;
}
void goToNext(ParsedGopherItem item) {
void _goToNext(ParsedGopherItem item, {String? why}) {
print("Going to next: $why");
setState(() {
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
Widget build(BuildContext context) {
return Center(
child: FutureBuilder<LoadedGopherItem>(
future: _data,
builder: (cont, snap) {
print("${snap.connectionState}");
if (snap.connectionState != ConnectionState.done) {
return const CircularProgressIndicator();
return PopScope(
canPop: false,
onPopInvokedWithResult: _onBackButton,
child: FutureBuilder<LoadedGopherItem>(
future: _data,
builder: (cont, snap) {
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 {
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 {
return const Text("Errored");
}
return Text("Errored ${snap.error}");
}
}));
}
}),
);
}
}