Skip to content

Commit

Permalink
feat: implement random sort and dynamic timeline sorting via dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
akhileshthite committed May 9, 2024
1 parent 078e7e1 commit 22310ee
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 19 deletions.
25 changes: 25 additions & 0 deletions db.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,31 @@ export class ActivityPubDB extends EventTarget {
await tx.done
}

async * searchNotesRandom ({ limit = DEFAULT_LIMIT } = {}) {
const tx = this.db.transaction(NOTES_STORE, 'readonly')
const store = tx.objectStore(NOTES_STORE)
const totalNotes = await store.count()

for (let i = 0; i < limit; i++) {
const randomSkip = Math.floor(Math.random() * totalNotes)
let cursor = await store.openCursor()
if (cursor) {
if (randomSkip > 0) { // Only advance if randomSkip is greater than 0
await cursor.advance(randomSkip)
if (cursor) {
yield cursor.value
cursor = await cursor.continue()
}
} else {
// If randomSkip is 0, yield the first item directly
yield cursor.value
cursor = await cursor.continue() // Continue to the next for proper iteration
}
}
}
await tx.done
}

async ingestActor (url, isInitial = false) {
console.log(`Starting ingestion for actor from URL: ${url}`)
const actor = await this.getActor(url)
Expand Down
12 changes: 11 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@
</style>
<div class="container">
<sidebar-nav></sidebar-nav>
<reader-timeline></reader-timeline>
<div class="reader-container">
<div class="sort-container">
<label for="sortOrder">Sort Timeline:</label>
<select id="sortOrder" class="sort-dropdown">
<option value="latest">Newest</option>
<option value="oldest">Oldest</option>
<option value="random">Random</option>
</select>
</div>
<reader-timeline></reader-timeline>
</div>
<div class="right-column">
<!-- This is an empty column to balance the layout -->
</div>
Expand Down
2 changes: 1 addition & 1 deletion theme-selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class ThemeSelector extends HTMLElement {
const style = document.createElement('style')
style.textContent = `
select {
padding: 4px;
padding: 2px;
margin: 6px 0;
border: 1px solid var(--rdp-border-color);
border-radius: 4px;
Expand Down
34 changes: 33 additions & 1 deletion timeline.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,35 @@ body {
background: var(--bg-color);
}

.reader-container {
margin-top: 20px;
display: flex;
flex-direction: column;
align-items: flex-start;
}

.sort-container {
width: 100%;
display: flex;
justify-content: flex-start;
}

.sort-container label {
color: var(--rdp-text-color);
font-size: 0.875rem;
margin-right: 4px;
display: flex;
align-items: center;
margin-left: 48px;
}

.sort-dropdown {
padding: 2px;
border: 1px solid var(--rdp-border-color);
border-radius: 4px;
width: 75px;
}

reader-timeline {
flex: 1;
max-width: 600px;
Expand All @@ -17,9 +46,12 @@ reader-timeline {
}

@media screen and (max-width: 768px) {
.reader-container {
margin-top: 160px;
}

reader-timeline {
width: 100%;
max-width: 100%;
margin-top: 150px;
}
}
95 changes: 79 additions & 16 deletions timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ class ReaderTimeline extends HTMLElement {
skip = 0
limit = 32
hasMoreItems = true
sort = 'latest'
loadMoreBtn = null
randomNotes = [] // Cache for random notes
randomIndex = 0 // Current index in the random notes cache

constructor () {
super()
this.loadMoreBtn = document.createElement('button')
this.loadMoreBtn.textContent = 'Load More..'
this.loadMoreBtn.textContent = 'Load More...'
this.loadMoreBtn.className = 'load-more-btn'

this.loadMoreBtnWrapper = document.createElement('div')
Expand All @@ -22,9 +25,42 @@ class ReaderTimeline extends HTMLElement {
}

connectedCallback () {
this.initializeSortOrder()
this.initializeDefaultFollowedActors().then(() => this.initTimeline())
}

initializeSortOrder () {
const params = new URLSearchParams(window.location.search)
this.sort = params.get('sort') || 'latest'

const sortOrderSelect = document.getElementById('sortOrder')
if (sortOrderSelect) {
sortOrderSelect.value = this.sort
sortOrderSelect.addEventListener('change', (event) => {
this.sort = event.target.value
this.updateURL()
this.resetTimeline()
})
}
}

updateURL () {
const url = new URL(window.location)
url.searchParams.set('sort', this.sort)
window.history.pushState({}, '', url)
}

async resetTimeline () {
this.skip = 0
this.randomIndex = 0
this.randomNotes = []
this.hasMoreItems = true
while (this.firstChild) {
this.removeChild(this.firstChild)
}
this.loadMore()
}

async initializeDefaultFollowedActors () {
const defaultActors = [
'https://social.distributed.press/v1/@announcements@social.distributed.press/',
Expand All @@ -35,14 +71,9 @@ class ReaderTimeline extends HTMLElement {
// "https://staticpub.mauve.moe/about.jsonld",
]

// Check if followed actors have already been initialized
const hasFollowedActors = await db.hasFollowedActors()
if (!hasFollowedActors) {
await Promise.all(
defaultActors.map(async (actorUrl) => {
await db.followActor(actorUrl)
})
)
await Promise.all(defaultActors.map(actorUrl => db.followActor(actorUrl)))
}
}

Expand All @@ -56,20 +87,52 @@ class ReaderTimeline extends HTMLElement {
}

async loadMore () {
// Remove the button before loading more items
this.loadMoreBtnWrapper.remove()

let count = 0
for await (const note of db.searchNotes({}, { skip: this.skip, limit: this.limit })) {
count++
this.appendNoteElement(note)

if (this.sort === 'random' && this.randomNotes.length === 0) {
const allNotes = []
for await (const note of db.searchNotesRandom(this.limit)) {
allNotes.push(note)
}
this.randomNotes = allNotes.sort(() => Math.random() - 0.5)
}

const notesToShow = this.sort === 'random'
? this.randomNotes.slice(this.randomIndex, this.randomIndex + this.limit)
: await this.fetchSortedNotes()

for (const note of notesToShow) {
if (note) {
this.appendNoteElement(note)
count++
}
}

// Update skip value and determine if there are more items
this.skip += this.limit
this.hasMoreItems = count === this.limit
this.updateIndexes(count)
this.appendLoadMoreIfNeeded()
}

async fetchSortedNotes () {
const notesGenerator = db.searchNotes({}, { skip: this.skip, limit: this.limit, sort: this.sort === 'oldest' ? 1 : -1 })
const notes = []
for await (const note of notesGenerator) {
notes.push(note)
}
return notes
}

updateIndexes (count) {
if (this.sort === 'random') {
this.randomIndex += this.limit
this.hasMoreItems = this.randomIndex < this.randomNotes.length
} else {
this.skip += this.limit
this.hasMoreItems = count === this.limit
}
}

// Append the button at the end if there are more items
appendLoadMoreIfNeeded () {
if (this.hasMoreItems) {
this.appendChild(this.loadMoreBtnWrapper)
}
Expand Down

0 comments on commit 22310ee

Please sign in to comment.