Jump to a date from the search box
Beside the search because it answers the same question by another route, and because an encrypted room cannot be searched at all - which leaves the date as the only way back into one. Local midnight, not UTC: somebody picking the 3rd means the 3rd where they are, and a UTC midnight is still the 2nd across most of the Americas. A day with nothing after it asks again backwards, so "take me to Tuesday" in a room that went quiet in March lands on the last thing anybody said rather than on nothing. From there it is the path a search result already takes. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+1
-1
Submodule nobilis updated: ae2e8189b4...1fe92d12fc
@@ -6676,6 +6676,33 @@ video.lightbox-media {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
/* --- Jump to a date --------------------------------------------------- */
|
||||
|
||||
/* Under the search box, because it answers the same question by a different
|
||||
route - and in an encrypted room, which the server cannot search, it is the
|
||||
only route. */
|
||||
.search-date {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.search-date input[type='date'] {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--surface-text);
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/* The picker's own button, which renders as a dark glyph on a dark field
|
||||
without this. */
|
||||
.search-date input[type='date']::-webkit-calendar-picker-indicator {
|
||||
filter: invert(0.8);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* --- Message source --------------------------------------------------- */
|
||||
|
||||
/* The event behind a message, for when what is on screen is wrong. Built like
|
||||
|
||||
@@ -19,6 +19,13 @@ interface DiscordThread {
|
||||
messageCount?: number
|
||||
}
|
||||
|
||||
/** Today, as a `date` field spells it - local, not UTC. */
|
||||
function today(): string {
|
||||
const d = new Date()
|
||||
const pad = (n: number): string => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
|
||||
}
|
||||
|
||||
/** What Discord's own search hands back. */
|
||||
interface DiscordSearch {
|
||||
results: Message[]
|
||||
@@ -586,6 +593,9 @@ export function ConversationTools({ buffer }: { buffer: BufferEntry }): JSX.Elem
|
||||
const [jumping, setJumping] = useState(false)
|
||||
const [calling, setCalling] = useState(false)
|
||||
const [searchOpen, setSearchOpen] = useState(false)
|
||||
/** The day somebody asked to read back to, as the date field spells it. */
|
||||
const [date, setDate] = useState('')
|
||||
const [jumpingToDate, setJumpingToDate] = useState(false)
|
||||
/** This room, or every room this account is in. */
|
||||
const [scope, setScope] = useState<'room' | 'account'>('room')
|
||||
const [roomNames, setRoomNames] = useState<Record<string, string>>({})
|
||||
@@ -768,6 +778,47 @@ export function ConversationTools({ buffer }: { buffer: BufferEntry }): JSX.Elem
|
||||
setSearchOpen(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads back to a particular day.
|
||||
*
|
||||
* The server is asked where that day is; everything after that is the path
|
||||
* a search result already takes. Forwards from midnight, because a date
|
||||
* means "that day" rather than "whatever came before it" - asking backwards
|
||||
* lands on the last message of the day before.
|
||||
*
|
||||
* A day after everything in the room finds nothing forwards, which is the
|
||||
* one case worth a second question: asking backwards from it lands on the
|
||||
* last thing anybody said, which is the honest answer to "take me to
|
||||
* Tuesday" in a room that went quiet in March.
|
||||
*/
|
||||
const jumpToDate = async (day: string): Promise<void> => {
|
||||
// Local midnight rather than UTC: somebody picking the 3rd means the 3rd
|
||||
// where they are, and a UTC midnight is the 2nd for most of the Americas.
|
||||
const midnight = new Date(`${day}T00:00:00`)
|
||||
if (Number.isNaN(midnight.getTime())) return
|
||||
const ts = Math.floor(midnight.getTime() / 1000)
|
||||
|
||||
setJumpingToDate(true)
|
||||
try {
|
||||
let found = await window.moho
|
||||
.rpc<{ eventId: string }>('matrixEventAtDate', { bufferId: buffer.id, ts, forwards: true })
|
||||
.catch(() => null)
|
||||
if (!found) {
|
||||
found = await window.moho
|
||||
.rpc<{ eventId: string }>('matrixEventAtDate', { bufferId: buffer.id, ts, forwards: false })
|
||||
.catch(() => null)
|
||||
}
|
||||
if (!found) {
|
||||
store.toast('info', 'Nothing was said in this room around then')
|
||||
return
|
||||
}
|
||||
await jumpTo(found.eventId)
|
||||
setDate('')
|
||||
} finally {
|
||||
setJumpingToDate(false)
|
||||
}
|
||||
}
|
||||
|
||||
const call = (): void => {
|
||||
if (inCall) {
|
||||
void store.leaveVoice(buffer.accountId)
|
||||
@@ -1043,6 +1094,37 @@ export function ConversationTools({ buffer }: { buffer: BufferEntry }): JSX.Elem
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Reading back to a particular day, which until now meant
|
||||
scrolling to it. Beside the search because it answers the same
|
||||
question - "where was that" - and because an encrypted room
|
||||
cannot be searched at all, which leaves the date as the only
|
||||
way back into it.
|
||||
|
||||
Matrix only: it is the one protocol here whose server will say
|
||||
where in a room a given day is. */}
|
||||
{isMatrix && (
|
||||
<div className="search-date">
|
||||
<label className="small muted" htmlFor="jump-date">
|
||||
Or jump to a date
|
||||
</label>
|
||||
<div className="popover-field">
|
||||
<Icon name="calendar_month" size={16} />
|
||||
<input
|
||||
id="jump-date"
|
||||
type="date"
|
||||
value={date}
|
||||
// Nothing was said in this room tomorrow.
|
||||
max={today()}
|
||||
disabled={jumpingToDate}
|
||||
onChange={(e) => {
|
||||
setDate(e.target.value)
|
||||
if (e.target.value) void jumpToDate(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Why an encrypted room finds only what this window already had:
|
||||
the server holds ciphertext and cannot read it. Element says the
|
||||
same thing, and silence here reads as a broken search. */}
|
||||
|
||||
Reference in New Issue
Block a user