Skip to content

anchors n/n: Open at first unread; open /near/ links at message #1517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8f54d58
msglist [nfc]: Build end-of-feed widgets in a helper method
gnprice May 8, 2025
17b0099
msglist [nfc]: Say we'll show "loading" even when fetch is at other end
gnprice May 2, 2025
ff45a29
msglist [nfc]: Use `fetched` getter when reading
gnprice May 2, 2025
8db87ce
msglist [nfc]: Use an enum for fetched/fetching/backoff state
gnprice May 2, 2025
2040b7d
msglist [nfc]: Split unfetched vs fetchInitial states, just for asserts
gnprice May 4, 2025
6ab3f3d
msglist [nfc]: Unify fetch/cooldown status as busyFetchingMore
gnprice May 2, 2025
44221b4
msglist [nfc]: Rename backoff state to share between older/newer
gnprice May 2, 2025
2882ac1
msglist [nfc]: Rename fetchingMore status from fetchOlder
gnprice May 2, 2025
b9d6d23
msglist [nfc]: Pull out a _setStatus method
gnprice May 2, 2025
28ec5da
msglist [nfc]: Introduce haveNewest in model, always true for now
gnprice May 2, 2025
2c81f30
msglist: Set haveNewest from response, like haveOldest
gnprice May 2, 2025
69d5074
test [nfc]: Generalize a helper eg.getMessagesResult
gnprice May 17, 2025
e175077
msglist [nfc]: Rearrange to follow normal ordering of class members
gnprice May 18, 2025
78baf81
msglist [nfc]: Document narrow field; make setter private
gnprice May 8, 2025
3dd187e
msglist: Send positive numAfter for fetchInitial
gnprice May 8, 2025
c0b44d2
msglist: Make initial fetch from any anchor, in model
gnprice May 8, 2025
4058e7f
msglist [nfc]: Cut default for MessageListView.anchor
gnprice May 17, 2025
3c11eb3
msglist test [nfc]: Simplify a bit by cutting redundant default narrow
gnprice May 17, 2025
77a74cc
msglist [nfc]: Factor out _fetchMore from fetchOlder
gnprice May 2, 2025
5edbc0e
msglist: Add fetchNewer method to model
gnprice May 2, 2025
4c47629
wip msglist call fetchNewer; TODO test
gnprice May 2, 2025
daac68e
wip msglist show loading indicator at bottom too; TODO test
gnprice May 14, 2025
86b0705
wip start on widget tests of end-cap; TODO check lack of newest hides…
gnprice May 14, 2025
ec7b14c
wip msglist page take anchor; TODO test
gnprice May 13, 2025
2845faf
wip jumpToEnd; TODO test, doc; TODO discuss criterion a bit more
gnprice May 9, 2025
2b42fab
wip internal_link [nfc]: Introduce NarrowLink type
gnprice May 13, 2025
e3af8f3
wip nfc propagate InternalLink up; TODO update doc; test?
gnprice May 13, 2025
a074719
wip parse /near/; TODO test
gnprice May 13, 2025
dbbeb49
wip pass /near/ through to new page; TODO indicate the target somehow…
gnprice May 13, 2025
3d75129
wip/link and fetch at first unread from widget, behind flag; TODO a r…
gnprice May 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions lib/model/internal_link.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@ Uri narrowLink(PerAccountStore store, Narrow narrow, {int? nearMessageId}) {
return result;
}

/// The result of parsing some URL within a Zulip realm,
/// when the URL corresponds to some page in this app.
sealed class InternalLink {
InternalLink({required this.realmUrl});

final Uri realmUrl;
}

/// The result of parsing some URL that points to a narrow on a Zulip realm,
/// when the narrow is of a type that this app understands.
class NarrowLink extends InternalLink {
NarrowLink(this.narrow, this.nearMessageId, {required super.realmUrl});

final Narrow narrow;
final int? nearMessageId;
}

/// A [Narrow] from a given URL, on `store`'s realm.
///
/// `url` must already be a result from [PerAccountStore.tryResolveUrl]
Expand All @@ -124,7 +141,7 @@ Uri narrowLink(PerAccountStore store, Narrow narrow, {int? nearMessageId}) {
/// better, but some kinds will be rare, even unheard-of:
/// #narrow/stream/1-announce/stream/1-announce (duplicated operator)
// TODO(#252): handle all valid narrow links, returning a search narrow
Narrow? parseInternalLink(Uri url, PerAccountStore store) {
InternalLink? parseInternalLink(Uri url, PerAccountStore store) {
if (!_isInternalLink(url, store.realmUrl)) return null;

final (category, segments) = _getCategoryAndSegmentsFromFragment(url.fragment);
Expand Down Expand Up @@ -155,7 +172,7 @@ bool _isInternalLink(Uri url, Uri realmUrl) {
return (category, segments);
}

Narrow? _interpretNarrowSegments(List<String> segments, PerAccountStore store) {
NarrowLink? _interpretNarrowSegments(List<String> segments, PerAccountStore store) {
assert(segments.isNotEmpty);
assert(segments.length.isEven);

Expand All @@ -164,6 +181,7 @@ Narrow? _interpretNarrowSegments(List<String> segments, PerAccountStore store) {
ApiNarrowDm? dmElement;
ApiNarrowWith? withElement;
Set<IsOperand> isElementOperands = {};
int? nearMessageId;

for (var i = 0; i < segments.length; i += 2) {
final (operator, negated) = _parseOperator(segments[i]);
Expand Down Expand Up @@ -201,24 +219,26 @@ Narrow? _interpretNarrowSegments(List<String> segments, PerAccountStore store) {
// It is fine to have duplicates of the same [IsOperand].
isElementOperands.add(IsOperand.fromRawString(operand));

case _NarrowOperator.near: // TODO(#82): support for near
continue;
case _NarrowOperator.near:
if (nearMessageId != null) return null;
nearMessageId = int.tryParse(operand, radix: 10);

case _NarrowOperator.unknown:
return null;
}
}

final Narrow? narrow;
if (isElementOperands.isNotEmpty) {
if (streamElement != null || topicElement != null || dmElement != null || withElement != null) {
return null;
}
if (isElementOperands.length > 1) return null;
switch (isElementOperands.single) {
case IsOperand.mentioned:
return const MentionsNarrow();
narrow = const MentionsNarrow();
case IsOperand.starred:
return const StarredMessagesNarrow();
narrow = const StarredMessagesNarrow();
case IsOperand.dm:
case IsOperand.private:
case IsOperand.alerted:
Expand All @@ -230,17 +250,20 @@ Narrow? _interpretNarrowSegments(List<String> segments, PerAccountStore store) {
}
} else if (dmElement != null) {
if (streamElement != null || topicElement != null || withElement != null) return null;
return DmNarrow.withUsers(dmElement.operand, selfUserId: store.selfUserId);
narrow = DmNarrow.withUsers(dmElement.operand, selfUserId: store.selfUserId);
} else if (streamElement != null) {
final streamId = streamElement.operand;
if (topicElement != null) {
return TopicNarrow(streamId, topicElement.operand, with_: withElement?.operand);
narrow = TopicNarrow(streamId, topicElement.operand, with_: withElement?.operand);
} else {
if (withElement != null) return null;
return ChannelNarrow(streamId);
narrow = ChannelNarrow(streamId);
}
} else {
return null;
}
return null;

return NarrowLink(narrow, nearMessageId, realmUrl: store.realmUrl);
}

@JsonEnum(fieldRename: FieldRename.kebab, alwaysCreate: true)
Expand Down
Loading