diff --git a/routes/_components/DynamicPageBanner.html b/routes/_components/DynamicPageBanner.html
new file mode 100644
index 0000000..bed32f2
--- /dev/null
+++ b/routes/_components/DynamicPageBanner.html
@@ -0,0 +1,44 @@
+
- {{{hydratedContent}}}
+
+ {{{emojifiedContent}}}
{{/if}}
{{#if originalMediaAttachments && originalMediaAttachments.length}}
@@ -295,6 +295,9 @@
const relativeFormat = new IntlRelativeFormat('en-US');
export default {
+ oncreate() {
+ this.hashtagifyContent()
+ },
components: {
Avatar,
Media,
@@ -311,15 +314,9 @@
originalStatus: (status) => status.reblog ? status.reblog : status,
originalAccount: (originalStatus) => originalStatus.account,
originalMediaAttachments: (originalStatus) => originalStatus.media_attachments,
- hydratedContent: (originalStatus) => {
+ emojifiedContent: (originalStatus) => {
let status = originalStatus
let content = status.content
- if (status.tags && status.tags.length) {
- for (let tag of status.tags) {
- let {name, url} = tag
- content = replaceAll(content, url, `/tags/${name}`)
- }
- }
if (status.emojis && status.emojis.length) {
for (let emoji of status.emojis) {
let { shortcode, url } = emoji
@@ -337,11 +334,34 @@
methods: {
onClickSpoilerButton() {
this.set({spoilerShown: !this.get('spoilerShown')})
+ this.hashtagifyContent()
this.fire('recalculateHeight')
},
onClickSensitiveMediaButton() {
this.set({sensitiveShown: !this.get('sensitiveShown')})
this.fire('recalculateHeight')
+ },
+ hashtagifyContent() {
+ if (!this.refs.contentNode) {
+ return
+ }
+ let status = this.get('originalStatus')
+ mark('hydrateHashtags')
+ if (status.tags && status.tags.length) {
+ let anchorTags = Array.from(this.refs.contentNode.querySelectorAll(
+ 'a[class~=hashtag][href^=http]'))
+ for (let tag of status.tags) {
+ let { name } = tag
+ for (let anchorTag of anchorTags) {
+ if (anchorTag.getAttribute('href').endsWith(`/tags/${name}`)) {
+ anchorTag.setAttribute('href', `/tags/${name}`)
+ anchorTag.removeAttribute('target')
+ anchorTag.removeAttribute('rel')
+ }
+ }
+ }
+ }
+ stop('hydrateHashtags')
}
}
}
diff --git a/routes/_components/Timeline.html b/routes/_components/Timeline.html
index 76d3118..2744716 100644
--- a/routes/_components/Timeline.html
+++ b/routes/_components/Timeline.html
@@ -38,6 +38,22 @@
this.fire('initialized')
}))
})
+ this.observe('statuses', statuses => {
+ let cachedAccountNames = this.store.get('cachedAccountNames') || {}
+ for (let status of statuses) {
+ cachedAccountNames[status.account.id] = {
+ username: status.account.username,
+ acct: status.account.acct
+ }
+ if (status.reblog) {
+ cachedAccountNames[status.reblog.account.id] = {
+ username: status.reblog.account.username,
+ acct: status.reblog.account.acct
+ }
+ }
+ }
+ this.store.set({'cachedAccountNames': cachedAccountNames})
+ })
},
data: () => ({
StatusListItem: StatusListItem,
@@ -58,6 +74,9 @@
} else if (timeline.startsWith('tag/')) {
let tag = timeline.split('/').slice(-1)[0]
return `#${tag} timeline for ${$currentInstance}`
+ } else if (timeline.startsWith('account/')) {
+ let account = timeline.split('/').slice(-1)[0]
+ return `Account #${account} on ${$currentInstance}`
}
}
},
diff --git a/routes/_utils/database/database.js b/routes/_utils/database/database.js
index 99725c2..4003043 100644
--- a/routes/_utils/database/database.js
+++ b/routes/_utils/database/database.js
@@ -1,6 +1,8 @@
+/*
import worker from 'workerize-loader!./databaseCore'
-const database = process.browser && worker()
+export const database = process.browser && worker()
+*/
-export {
- database
-}
\ No newline at end of file
+import * as dbCore from './databaseCore'
+
+export { dbCore as database }
\ No newline at end of file
diff --git a/routes/_utils/mastodon/timelines.js b/routes/_utils/mastodon/timelines.js
index f37ca1c..0e30737 100644
--- a/routes/_utils/mastodon/timelines.js
+++ b/routes/_utils/mastodon/timelines.js
@@ -1,24 +1,29 @@
import { get, paramsString } from '../ajax'
import { basename } from './utils'
-function getTimelineUrlName(timeline) {
+function getTimelineUrlPath(timeline) {
switch (timeline) {
case 'local':
case 'federated':
- return 'public'
+ return 'timelines/public'
case 'home':
- return 'home'
- default:
- return 'tag'
+ return 'timelines/home'
+ }
+ if (timeline.startsWith('tag/')) {
+ return 'timelines/tag'
+ } else if (timeline.startsWith('account/')) {
+ return 'accounts'
}
}
export function getTimeline(instanceName, accessToken, timeline, maxId, since) {
- let timelineUrlName = getTimelineUrlName(timeline)
- let url = `${basename(instanceName)}/api/v1/timelines/${timelineUrlName}`
+ let timelineUrlName = getTimelineUrlPath(timeline)
+ let url = `${basename(instanceName)}/api/v1/${timelineUrlName}`
if (timeline.startsWith('tag/')) {
url += '/' + timeline.split('/').slice(-1)[0]
+ } else if (timeline.startsWith('account/')) {
+ url += '/' + timeline.split('/').slice(-1)[0] +'/statuses'
}
let params = {}
diff --git a/routes/_utils/mastodon/user.js b/routes/_utils/mastodon/user.js
index 056f862..ee14ede 100644
--- a/routes/_utils/mastodon/user.js
+++ b/routes/_utils/mastodon/user.js
@@ -6,4 +6,11 @@ export function getVerifyCredentials(instanceName, accessToken) {
return get(url, {
'Authorization': `Bearer ${accessToken}`
})
+}
+
+export function getAccount(instanceName, accessToken, accountId) {
+ let url = `${basename(instanceName)}/api/v1/accounts/${accountId}`
+ return get(url, {
+ 'Authorization': `Bearer ${accessToken}`
+ })
}
\ No newline at end of file
diff --git a/routes/_utils/store.js b/routes/_utils/store.js
index acf988c..5114569 100644
--- a/routes/_utils/store.js
+++ b/routes/_utils/store.js
@@ -1,5 +1,9 @@
import { Store } from 'svelte/store.js'
+const DONT_STORE_THESE_KEYS = [
+ 'cachedAccountNames'
+]
+
const LS = process.browser && localStorage
class LocalStorageStore extends Store {
@@ -18,7 +22,8 @@ class LocalStorageStore extends Store {
this.set(newState)
this.onchange((state, changed) => {
Object.keys(changed).forEach(change => {
- if (!this._computed[change]) { // TODO: better way to ignore computed values?
+ if (!DONT_STORE_THESE_KEYS.includes(change) &&
+ !this._computed[change]) { // TODO: better way to ignore computed values?
this.lastChanged[change] = true
}
})
@@ -74,6 +79,12 @@ store.compute(
}, loggedInInstances[currentInstance])
})
+store.compute(
+ 'accessToken',
+ ['currentInstanceData'],
+ (currentInstanceData) => currentInstanceData.access_token
+)
+
store.compute(
'currentTheme',
['currentInstance', 'instanceThemes'],
diff --git a/routes/accounts/[accountId].html b/routes/accounts/[accountId].html
new file mode 100644
index 0000000..dc6816f
--- /dev/null
+++ b/routes/accounts/[accountId].html
@@ -0,0 +1,63 @@
+<:Head>
+
{{'Pinafore – ' + (cachedProfileName || profileName || '')}}
+
+
+
+ {{#if $isUserLoggedIn}}
+
+
+ {{else}}
+
+
+ Profile
+
+ A user timeline will appear here when logged in.
+
+
+ {{/if}}
+
+
\ No newline at end of file
diff --git a/routes/tags/[tagName].html b/routes/tags/[tagName].html
index e29203e..96bfa61 100644
--- a/routes/tags/[tagName].html
+++ b/routes/tags/[tagName].html
@@ -8,10 +8,7 @@
dynamicLabel="{{'#' + params.tagName}}"
dynamicIcon="#fa-hashtag" >
{{#if $isUserLoggedIn}}
-
-
{{'#' + params.tagName}}
-
-
+
{{else}}
@@ -23,37 +20,13 @@
{{/if}}
-
\ No newline at end of file