Compare commits

...

6 Commits

Author SHA1 Message Date
333c91b4c6 Problem: after deleting channel, only that channel is shown
Solution: show remaining channels
2022-04-18 14:19:37 +02:00
906bcd5493 Problem: event source code show lots of debugging messages
Solution: remove logging in event source
2022-04-18 14:19:01 +02:00
79b88d7a59 Problem: impossible to use global search after selecting channel
Solution: make global search a specific option in searchpopup
2022-04-18 14:18:15 +02:00
f559c237ca Problem: UI uses too much space
Solution: use smaller buttons and simplify language
2022-04-18 14:17:37 +02:00
52bc6febaa Problem: menu does not work on mobile
Solution: fix menu
2022-04-18 14:16:50 +02:00
3e1695e6f3 Problem: hidden content does not always work right
Solution: clean up hidden content
2022-04-18 14:15:56 +02:00
7 changed files with 79 additions and 45 deletions

View File

@ -5,14 +5,13 @@
<div class="navbar-brand"> <div class="navbar-brand">
<router-link to="/" class="navbar-item">Ekster</router-link> <router-link to="/" class="navbar-item">Ekster</router-link>
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false"> <a role="button" :class="{'navbar-burger':true,'is-active':menuActive}" aria-label="menu" aria-expanded="false" @click="toggleMenu">
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
</a> </a>
</div> </div>
<div class="navbar-menu"> <div :class="{'navbar-menu':true,'is-active':menuActive}">
<div class="navbar-end"> <div class="navbar-end">
<div class="buttons"> <div class="buttons">
<button class="button is-light" @click="openSearch">Search</button> <button class="button is-light" @click="openSearch">Search</button>
@ -42,10 +41,18 @@
this.$store.dispatch('startEventListening', '?action=events') this.$store.dispatch('startEventListening', '?action=events')
} }
}, },
computed: {
menuActive() {
return this.$store.state.menuActive
}
},
methods: { methods: {
openSearch () { openSearch () {
this.$store.dispatch('openSearch') this.$store.dispatch('openSearch')
} },
toggleMenu () {
this.$store.dispatch('toggleMenu' )
},
} }
} }
</script> </script>

View File

@ -3,7 +3,7 @@
<div class="channels--channel" v-for="channel in channels" :key="channel.uid"> <div class="channels--channel" v-for="channel in channels" :key="channel.uid">
<slot :channel="channel"></slot> <slot :channel="channel"></slot>
</div> </div>
<button class="button is-primary" @click="open">+ New Channel</button> <button class="button is-primary is-small" @click="open">New Channel</button>
</div> </div>
</template> </template>

View File

@ -216,15 +216,18 @@ export default {
}, },
toggleHiddenContent() { toggleHiddenContent() {
this.hiddenContentVisible = !this.hiddenContentVisible this.hiddenContentVisible = !this.hiddenContentVisible
const el = this.$refs['content-container'] // const el = this.$refs['content-container']
el.scrollIntoView(true) // el.scrollIntoView(true)
} }
}, },
mounted() { mounted() {
this.showFooterButtons = true this.showFooterButtons = true
const el = this.$refs['content-container'] const el = this.$refs['content-container']
this.hasHiddenContent = el.scrollHeight > el.clientHeight this.hiddenContentVisible = false
this.$nextTick(() => {
this.hasHiddenContent = el.scrollHeight > el.clientHeight
})
}, },
computed: { computed: {
@ -377,8 +380,10 @@ export default {
console.log('ref content-container not found') console.log('ref content-container not found')
return return
} }
this.hasHiddenContent = el.scrollHeight > el.clientHeight
this.hiddenContentVisible = false this.hiddenContentVisible = false
this.$nextTick(() => {
this.hasHiddenContent = el.scrollHeight > el.clientHeight
})
}) })
} }
} }

View File

@ -16,7 +16,14 @@
<button :class="searchClasses" @click="search">Search</button> <button :class="searchClasses" @click="search">Search</button>
</div> </div>
</div> </div>
<div class="field">
<div class="control">
<label for="global" class="radio"><input type="radio" name="channel" id="global" value="global" v-model="searchChannel"> Global</label>
<label for="channel" class="radio"><input type="radio" name="channel" id="channel" value="channel" v-model="searchChannel"> Channel</label>
</div>
</div>
<div class="timeline--item" v-for="item in searchItems" :key="item.id"> <div class="timeline--item" v-for="item in searchItems" :key="item.id">
<TimelineEntry :item="item" :is-main-entry="true" @debug="debug" /> <TimelineEntry :item="item" :is-main-entry="true" @debug="debug" />
</div> </div>
@ -38,7 +45,8 @@ export default {
return { return {
query: '', query: '',
loading: false, loading: false,
error: '' error: '',
searchChannel: 'global'
} }
}, },
mounted () { mounted () {
@ -65,7 +73,7 @@ export default {
search () { search () {
this.loading = true this.loading = true
this.error = '' this.error = ''
this.$store.dispatch('startQuery', this.query) this.$store.dispatch('startQuery', {query: this.query, channel: this.searchChannel})
.then(result => { .then(result => {
this.loading = false this.loading = false
if (result.error) { if (result.error) {

View File

@ -4,6 +4,9 @@
<TimelineEntry :item="item" @debug="debug" @markRead="markRead(channel.uid, ...arguments)" @followFeed="$emit('followFeed', arguments[0])" <TimelineEntry :item="item" @debug="debug" @markRead="markRead(channel.uid, ...arguments)" @followFeed="$emit('followFeed', arguments[0])"
:is-main-entry="true"/> :is-main-entry="true"/>
</div> </div>
<div v-if="items.length === 0">
<p class="text-center">No items in this timeline</p>
</div>
<div class="level"> <div class="level">
<div class="level-item"> <div class="level-item">
<button class="button" @click="prevPage" v-if="timeline.paging.before">Prev Page</button> <button class="button" @click="prevPage" v-if="timeline.paging.before">Prev Page</button>

View File

@ -22,7 +22,8 @@ export default new Vuex.Store({
globalTimeline: {items: []}, globalTimeline: {items: []},
debug: false, debug: false,
debugItem: {}, debugItem: {},
debugVisible: false debugVisible: false,
menuActive: false,
}; };
let loginData = JSON.parse(window.localStorage.getItem('login_data')) let loginData = JSON.parse(window.localStorage.getItem('login_data'))
if (loginData) { if (loginData) {
@ -79,21 +80,19 @@ export default new Vuex.Store({
'Authorization': 'Bearer ' + this.state.access_token 'Authorization': 'Bearer ' + this.state.access_token
} }
}) })
state.eventSource.addEventListener('open', evt => { // state.eventSource.addEventListener('open', evt => {
// eslint-disable-next-line // // eslint-disable-next-line
console.log(evt) // console.log(evt)
}) // })
state.eventSource.addEventListener('ping', evt => { // state.eventSource.addEventListener('ping', evt => {
// eslint-disable-next-line // // eslint-disable-next-line
console.log(evt) // console.log(evt)
}) // })
state.eventSource.addEventListener('message', evt => { // state.eventSource.addEventListener('message', evt => {
// eslint-disable-next-line // // eslint-disable-next-line
console.log(evt) // console.log(evt)
}) // })
state.eventSource.addEventListener('error', evt => { state.eventSource.addEventListener('error', evt => {
// eslint-disable-next-line
console.log(evt)
if (evt.message === "network error") { if (evt.message === "network error") {
state.eventSource.close() state.eventSource.close()
} }
@ -111,8 +110,6 @@ export default new Vuex.Store({
} }
}) })
state.eventSource.addEventListener('new item in channel', evt => { state.eventSource.addEventListener('new item in channel', evt => {
// eslint-disable-next-line
console.log(evt)
let msg = JSON.parse(evt.data) let msg = JSON.parse(evt.data)
let channel = _.find(state.channels, item => item.uid === msg.uid) let channel = _.find(state.channels, item => item.uid === msg.uid)
if (channel) { if (channel) {
@ -121,8 +118,6 @@ export default new Vuex.Store({
}) })
state.eventSource.addEventListener('new channel', evt => { state.eventSource.addEventListener('new channel', evt => {
// eslint-disable-next-line
console.log(evt)
let msg = JSON.parse(evt.data) let msg = JSON.parse(evt.data)
let channel = _.find(state.channels, it => it.uid === msg.channel.uid) let channel = _.find(state.channels, it => it.uid === msg.channel.uid)
if (!channel) { if (!channel) {
@ -131,8 +126,6 @@ export default new Vuex.Store({
}) })
state.eventSource.addEventListener('update channel', evt => { state.eventSource.addEventListener('update channel', evt => {
// eslint-disable-next-line
console.log(evt)
let msg = JSON.parse(evt.data) let msg = JSON.parse(evt.data)
let channel = _.find(state.channels, it => it.uid === msg.channel.uid) let channel = _.find(state.channels, it => it.uid === msg.channel.uid)
if (channel) { if (channel) {
@ -141,10 +134,8 @@ export default new Vuex.Store({
}) })
state.eventSource.addEventListener('delete channel', evt => { state.eventSource.addEventListener('delete channel', evt => {
// eslint-disable-next-line
console.log(evt)
let msg = JSON.parse(evt.data) let msg = JSON.parse(evt.data)
state.channels = _.remove(state.channels, it => it.uid === msg.uid) state.channels = _.filter(state.channels, it => it.uid !== msg.uid)
}) })
}, },
newSearchResults(state, items) { newSearchResults(state, items) {
@ -156,6 +147,9 @@ export default new Vuex.Store({
}, },
closeDebugPopup(state) { closeDebugPopup(state) {
state.debugVisible = false state.debugVisible = false
},
activateMenu(state) {
state.menuActive = !state.menuActive
} }
}, },
@ -175,6 +169,9 @@ export default new Vuex.Store({
commit('clearTimeline', {channel: channel}) commit('clearTimeline', {channel: channel})
}, },
fetchTimeline({commit}, channel) { fetchTimeline({commit}, channel) {
if (!channel.uid) {
channel.uid = 'home';
}
let url = this.state.microsubEndpoint + '?action=timeline&channel=' + channel.uid let url = this.state.microsubEndpoint + '?action=timeline&channel=' + channel.uid
if (channel.after) { if (channel.after) {
url += '&after=' + channel.after; url += '&after=' + channel.after;
@ -261,14 +258,22 @@ export default new Vuex.Store({
commit('setSearchPopupState', false) commit('setSearchPopupState', false)
}, },
startQuery({state, commit}, query) { startQuery({state, commit}, query) {
let channel = 'global' let channel
if (state.channel !== null && state.channel.uid !== null && state.channel.uid !== 'home') { if (query.channel) {
channel = state.channel.uid if (query.channel === 'global') {
channel = 'global'
} else if (query.channel === 'channel') {
if (state.channel !== null && state.channel.uid !== null) {
channel = state.channel.uid
}
} else {
channel = 'global'
}
} }
const url = new URL(this.state.microsubEndpoint) const url = new URL(this.state.microsubEndpoint)
url.searchParams.set('action', 'search') url.searchParams.set('action', 'search')
url.searchParams.set('channel', channel) url.searchParams.set('channel', channel)
url.searchParams.set('query', query) url.searchParams.set('query', query.query)
return fetch(url.toString(), { return fetch(url.toString(), {
headers: { headers: {
'Authorization': 'Bearer ' + this.state.access_token 'Authorization': 'Bearer ' + this.state.access_token
@ -318,7 +323,7 @@ export default new Vuex.Store({
} }
}, },
startEventListening({commit}, url) { startEventListening({commit}, url) {
commit('createEventSource', url) return commit('createEventSource', url)
}, },
openDebug({commit}, item) { openDebug({commit}, item) {
return commit('openDebugPopup', item) return commit('openDebugPopup', item)
@ -326,5 +331,8 @@ export default new Vuex.Store({
closeDebug({commit}) { closeDebug({commit}) {
return commit('closeDebugPopup') return commit('closeDebugPopup')
}, },
toggleMenu({commit}) {
return commit('activateMenu')
}
} }
}) })

View File

@ -3,7 +3,7 @@
<div class="column" style="padding-top: 20px"> <div class="column" style="padding-top: 20px">
<button class="channels-toggle button" type="button" @click="showChannels">Channels</button> <button class="channels-toggle button" type="button" @click="showChannels">Channels</button>
<Channels :class="channelsClass" :channels="this.$store.state.channels"> <Channels :class="channelsClass" :channels="this.channels">
<div slot-scope="{ channel }"> <div slot-scope="{ channel }">
<Channel :channel="channel" @channel-selected="selectChannel"></Channel> <Channel :channel="channel" @channel-selected="selectChannel"></Channel>
</div> </div>
@ -11,14 +11,14 @@
</div> </div>
<div class="timeline column is-three-fifths"> <div class="timeline column is-three-fifths">
<div class="level"> <div class="level is-mobile">
<div class="level-left"> <div class="level-left">
<h1 class="title is-5">{{ channel.name }}</h1> <h1 class="title is-5">{{ channel.name }}</h1>
</div> </div>
<div class="level-right timeline-buttons"> <div class="level-right timeline-buttons">
<div class="level-item"> <div class="level-item">
<button class="button" @click.prevent="openPost">New Post</button> <button class="button is-small" @click.prevent="openPost">New Post</button>
<button class="button" @click.prevent="openFeedFollower('')">Add feed</button> <button class="button is-small" @click.prevent="openFeedFollower('')">Add feed</button>
</div> </div>
</div> </div>
</div> </div>
@ -80,6 +80,9 @@
channel() { channel() {
return this.$store.state.channel return this.$store.state.channel
}, },
channels() {
return this.$store.state.channels
},
channelsClass () { channelsClass () {
return { return {
'channels-open': this.channelsVisible, 'channels-open': this.channelsVisible,