diff --git a/src/components/Feed/Feed.vue b/src/components/Feed/Feed.vue
new file mode 100644
index 0000000..92e6ce0
--- /dev/null
+++ b/src/components/Feed/Feed.vue
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
diff --git a/src/components/PageHeader.vue b/src/components/PageHeader.vue
index 1df7afb..29a6d74 100644
--- a/src/components/PageHeader.vue
+++ b/src/components/PageHeader.vue
@@ -108,6 +108,7 @@ export default defineComponent({
}
.addon {
flex-grow: 1;
+ text-align: right;
}
}
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 36fa601..d6d7ac9 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -66,7 +66,7 @@ export default defineComponent({
},
data() {
return {
- cachedPages: ['Feed', 'Notifications', 'Messages', 'Inbox', 'Settings'],
+ cachedPages: ['FeedPage', 'Notifications', 'Messages', 'Inbox', 'Settings'],
mobileMenuOpen: false,
}
},
diff --git a/src/nostr/NostrStore.js b/src/nostr/NostrStore.js
index 8138a7a..8b9657b 100644
--- a/src/nostr/NostrStore.js
+++ b/src/nostr/NostrStore.js
@@ -6,7 +6,7 @@ import FetchQueue from 'src/nostr/FetchQueue'
import {NoteOrder, useNoteStore} from 'src/nostr/store/NoteStore'
import {useProfileStore} from 'src/nostr/store/ProfileStore'
import {useContactStore} from 'src/nostr/store/ContactStore'
-import {useSettingsStore} from 'stores/Settings'
+import {useSettingsStore, RELAYS} from 'stores/Settings'
import {useStatStore} from 'src/nostr/store/StatStore'
import {Observable} from 'src/nostr/utils'
import {CloseAfter} from 'src/nostr/Relay'
@@ -61,7 +61,8 @@ export const useNostrStore = defineStore('nostr', {
actions: {
init() {
const settings = useSettingsStore()
- this.client = markRaw(new NostrClient(settings.relays))
+ // FIXME Use relays from settings
+ this.client = markRaw(new NostrClient(RELAYS))
this.client.connect()
this.profileQueue = profileQueue(this.client)
@@ -172,8 +173,8 @@ export const useNostrStore = defineStore('nostr', {
{
kinds: [EventKind.NOTE],
authors: [pubkey],
- },
- limit
+ limit,
+ }
)
},
@@ -191,14 +192,13 @@ export const useNostrStore = defineStore('nostr', {
return followers
},
- fetchFollowers(pubkey, opts = {}) {
- const limit = opts.limit || 500
+ fetchFollowers(pubkey, limit = 500) {
return this.fetch(
{
kinds: [EventKind.CONTACT],
'#p': [pubkey],
+ limit,
},
- limit
)
},
@@ -231,8 +231,8 @@ export const useNostrStore = defineStore('nostr', {
{
kinds: [EventKind.REACTION],
authors: [pubkey],
- },
- limit
+ limit,
+ }
)
},
diff --git a/src/pages/Feed.vue b/src/pages/Feed.vue
index 09982b3..6167070 100644
--- a/src/pages/Feed.vue
+++ b/src/pages/Feed.vue
@@ -2,26 +2,18 @@
@@ -29,27 +21,11 @@
-
+
+
+
+
+
@@ -57,16 +33,12 @@
import {defineComponent} from 'vue'
import PageHeader from 'components/PageHeader.vue'
import PostEditor from 'components/CreatePost/PostEditor.vue'
-import Thread from 'components/Post/Thread.vue'
-import BaseIcon from 'components/BaseIcon/index.vue'
-import AsyncLoadButton from 'components/AsyncLoadButton.vue'
-import ListPlaceholder from 'components/ListPlaceholder.vue'
+import Feed from 'components/Feed/Feed.vue'
import {useAppStore} from 'stores/App'
import {useNostrStore} from 'src/nostr/NostrStore'
-import Defer from 'src/utils/Defer'
import {EventKind} from 'src/nostr/model/Event'
-import DateUtils from 'src/utils/DateUtils'
-import Bots from 'src/utils/bots'
+
+const ZERO_PUBKEY = '0000000000000000000000000000000000000000000000000000000000000000'
const Feeds = {
global: {
@@ -75,24 +47,33 @@ const Feeds = {
kinds: [EventKind.NOTE], // TODO Deletions
limit: 20,
},
+ hideBots: true,
+ },
+ following: {
+ name: 'following',
+ filters: () => {
+ const app = useAppStore()
+ const nostr = useNostrStore()
+ const contacts = nostr.getContacts(app.myPubkey)
+ let authors = contacts?.map(contact => contact.pubkey)
+ if (!authors || !authors.length) authors = [ZERO_PUBKEY]
+ return {
+ authors,
+ kinds: [EventKind.NOTE],
+ limit: 50,
+ }
+ },
+ hideBots: false,
},
}
-const feedOrder = (a, b) => b[0].createdAt - a[0].createdAt
-
-const MAX_ITEMS_VISIBLE = 50
-
export default defineComponent({
- name: 'Feed',
+ name: 'FeedPage',
components: {
PageHeader,
PostEditor,
- Thread,
- BaseIcon,
- AsyncLoadButton,
- ListPlaceholder,
+ Feed,
},
- mixins: [Defer(2000)],
setup() {
return {
app: useAppStore(),
@@ -102,120 +83,31 @@ export default defineComponent({
data() {
return {
feeds: {},
- availableFeeds: ['global'],
- selectedFeed: 'global',
- loading: true,
- recentlyLoaded: true,
+ activeFeed: 'global',
}
},
computed: {
- activeFeed() {
- return this.feeds[this.selectedFeed]
+ availableFeeds() {
+ const feeds = ['global']
+ if (this.app.isSignedIn) feeds.push('following')
+ return feeds
},
- feedItems() {
- return this.activeFeed?.visible
- },
- numUnreads() {
- if (this.recentlyLoaded) return 0
- return this.activeFeed?.newer.length
+ contacts() {
+ if (!this.app.isSignedIn) return
+ return this.nostr.getContacts(this.app.myPubkey)
},
},
methods: {
- initFeed(feedId) {
- if (this.feeds[feedId]) return
-
- const filters = Feeds[feedId].filters
- const stream = this.nostr.stream(
- filters,
- {subId: `feed:${feedId}`}
- )
- stream.on('init', notes => {
- const items = notes
- .filter(this.filterNote.bind(this))
- .map(note => [note]) // TODO Single element thread
- items.sort(feedOrder)
- this.feeds[feedId].visible = items.slice(0, filters.limit)
- this.loading = false
-
- // Wait a bit before showing the first unreads
- setTimeout(() => this.recentlyLoaded = false, 5000)
- })
- stream.on('update', note => {
- if (this.filterNote(note)) {
- this.feeds[feedId].newer.push([note]) // TODO Single element thread
- }
- })
-
- this.feeds[feedId] = {
- visible: [],
- newer: [],
- older: [],
- stream,
- }
+ feedDef(feed) {
+ return Feeds[feed]
},
- switchFeed(feedId) {
- this.initFeed(feedId)
- this.selectedFeed = feedId
- },
- loadNewer() {
- // TODO Deduplicate feed items
- this.activeFeed.newer.sort(feedOrder)
- const items = this.activeFeed.newer.concat(this.feedItems)
- if (items.length > MAX_ITEMS_VISIBLE) {
- const older = items.splice(MAX_ITEMS_VISIBLE)
- this.activeFeed.older = older.concat(this.activeFeed.older)
- }
- //items.sort(feedOrder)
-
- this.activeFeed.visible = items
- this.activeFeed.newer = []
-
- // Wait a bit before showing unreads again
- this.recentlyLoaded = true
- setTimeout(() => this.recentlyLoaded = false, 5000)
-
- return true
- },
- async loadOlder() {
- const until = this.feedItems[this.feedItems.length - 1]?.[0]?.createdAt || DateUtils.now()
- const filters = Object.assign({}, Feeds[this.selectedFeed].filters, {until})
-
- if (this.activeFeed.older.length >= filters.limit) {
- const chunk = this.activeFeed.older.splice(0, filters.limit)
- this.activeFeed.visible = this.feedItems.concat(chunk)
- return chunk
- }
-
- // Remove any residual older items
- this.activeFeed.older = []
-
- const older = await this.nostr.fetch(filters, {subId: `feed:${this.selectedFeed}-older`})
- const items = older
- .filter(note => note.createdAt <= until)
- .filter(this.filterNote.bind(this))
- .map(note => [note]) // TODO Single element thread
- .sort(feedOrder)
-
- // TODO Deduplicate feed items
- this.activeFeed.visible = this.feedItems.concat(items)
-
- return older
- },
- filterNote(note) {
- if (note.isReaction()) return false
- if (note.isRepostOrTag()) return false
- if (note.relatedPubkeys().some(Bots.isBot)) return false
- return true
- }
},
- mounted() {
- this.initFeed(this.selectedFeed)
+ watch: {
+ contacts() {
+ console.log('following', this.$refs.following)
+ this.$refs.following?.[0]?.reload()
+ },
},
- unmounted() {
- for (const feed of Object.values(this.feeds)) {
- feed.stream.close()
- }
- }
})
@@ -223,23 +115,9 @@ export default defineComponent({
@import "assets/theme/colors.scss";
@import "assets/variables.scss";
-.feed {
- .load-more-container {
- border-top: $border-dark;
- border-bottom: $border-dark;
- min-height: 6px;
- }
- > .async-load-button:last-child {
- border-bottom: 0;
- }
-}
-
-.addon-menu {
- display: flex;
- flex-direction: row-reverse;
- &-icon {
- cursor: pointer;
- }
+.feed-selector {
+ background-color: rgba($color: $color-dark-gray, $alpha: 0.2);
+ //box-shadow: none;
}
@media screen and (max-width: $phone) {
@@ -249,50 +127,27 @@ export default defineComponent({
.post-editor {
display: none;
}
- .feed {
- .load-more-container {
- border: 0;
- min-height: 0;
- &.more-available {
- border-bottom: $border-dark;
- }
- }
- }
}