Compare commits
10 commits
develop
...
feature/gr
Author | SHA1 | Date | |
---|---|---|---|
|
c52173c64b | ||
|
cd87a6712d | ||
|
a284a52306 | ||
|
caf4c7fddf | ||
|
55925383b7 | ||
|
7a3e608455 | ||
|
d4de464ab5 | ||
|
54d3b0cd7e | ||
|
9c90b019d1 | ||
|
b25b993309 |
16 changed files with 548 additions and 16 deletions
225
src/components/group_card_content/group_card_content.vue
Normal file
225
src/components/group_card_content/group_card_content.vue
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
<template>
|
||||||
|
<div id="heading" class="group-panel-background" :style="headingStyle">
|
||||||
|
<div class="panel-heading text-center">
|
||||||
|
<div class='group-info'>
|
||||||
|
<div class='container'>
|
||||||
|
<router-link :to="{ name: 'group-page', params: { name: group.nickname } }">
|
||||||
|
<img v-if="!!group.original_logo" :src="group.original_logo">
|
||||||
|
<img v-else src="https://placehold.it/48x48">
|
||||||
|
</router-link>
|
||||||
|
<span class="glyphicon glyphicon-user"></span>
|
||||||
|
<div class="name-and-screen-name">
|
||||||
|
<div class='group-name'>{{group.nickname}}</div>
|
||||||
|
<router-link :to="{ name: 'group-page', params: { name: group.nickname } }">
|
||||||
|
<div class='group-full-name'>{{group.fullname}}</div>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="group-interactions">
|
||||||
|
<div class="member" v-if="loggedIn">
|
||||||
|
<span v-if="isMember">
|
||||||
|
<button @click="leaveGroup" class="base04 base00-background pressed">
|
||||||
|
{{ $t('group_card.leave') }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
<span v-if="!isMember">
|
||||||
|
<button @click="joinGroup" class="base05 base02-background">
|
||||||
|
{{ $t('group_card.join') }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body group-panel-body">
|
||||||
|
<div class="member-counts">
|
||||||
|
<div class="member-count">
|
||||||
|
<a href="#" v-on:click.prevent="setGroupView('statuses')"><h5 class="base05">{{ $t('user_card.statuses') }}</h5></a>
|
||||||
|
</div>
|
||||||
|
<div class="member-count">
|
||||||
|
<a href="#" v-on:click.prevent="setGroupView('members')"><h5 class="base05">{{ $t('group_card.members') }}</h5></a>
|
||||||
|
<span class="base05">{{group.member_count}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>{{group.description}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: [ 'group', 'isMember' ],
|
||||||
|
computed: {
|
||||||
|
groupName () {
|
||||||
|
return this.group.nickname
|
||||||
|
},
|
||||||
|
headingStyle () {
|
||||||
|
let color = this.$store.state.config.colors['base00']
|
||||||
|
if (color) {
|
||||||
|
let rgb = this.$store.state.config.colors['base00'].match(/\d+/g)
|
||||||
|
return {
|
||||||
|
backgroundColor: `rgb(${Math.floor(rgb[0] * 0.53)}, ${Math.floor(rgb[1] * 0.56)}, ${Math.floor(rgb[2] * 0.59)})`,
|
||||||
|
backgroundImage: `url(${this.group.cover_photo})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loggedIn () {
|
||||||
|
return !!this.$store.state.users.currentUser
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setMember (value) {
|
||||||
|
this.$store.state.groups.groupMemberships[this.groupName] = value
|
||||||
|
},
|
||||||
|
joinGroup () {
|
||||||
|
const store = this.$store
|
||||||
|
store.state.api.backendInteractor.joinGroup({'groupName': this.groupName})
|
||||||
|
.then((joinedGroup) => {
|
||||||
|
store.commit('addNewGroup', joinedGroup)
|
||||||
|
this.setMember(true)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
leaveGroup () {
|
||||||
|
const store = this.$store
|
||||||
|
store.state.api.backendInteractor.leaveGroup({'groupName': this.groupName})
|
||||||
|
.then((leftGroup) => {
|
||||||
|
store.commit('addNewGroup', leftGroup)
|
||||||
|
this.setMember(false)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setGroupView (v) {
|
||||||
|
const store = this.$store
|
||||||
|
store.commit('setGroupView', { v })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.group-panel-background {
|
||||||
|
background-size: cover;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
.panel-heading {
|
||||||
|
padding: 0.6em 0em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-panel-body {
|
||||||
|
top: -0em;
|
||||||
|
padding-top: 4em;
|
||||||
|
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-info {
|
||||||
|
color: white;
|
||||||
|
padding: 0 16px 16px 16px;
|
||||||
|
margin-bottom: -4em;
|
||||||
|
|
||||||
|
.container{
|
||||||
|
padding: 16px 10px 4px 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
max-height: 56px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 5px;
|
||||||
|
flex: 1 0 100%;
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
box-shadow: 0px 1px 8px rgba(0,0,0,0.75);
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
text-shadow: 0px 1px 1.5px rgba(0, 0, 0, 1.0);
|
||||||
|
|
||||||
|
.name-and-screen-name {
|
||||||
|
display: block;
|
||||||
|
margin-left: 0.6em;
|
||||||
|
text-align: left;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-name{
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-full-name {
|
||||||
|
color: white;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 15px;
|
||||||
|
padding-right: 0.1em;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-interactions {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
margin-top: 0.7em;
|
||||||
|
margin-bottom: -1.0em;
|
||||||
|
|
||||||
|
.following {
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
flex: 0 0 100%;
|
||||||
|
margin: -0.7em 0.0em 0.3em 0.0em;
|
||||||
|
padding-left: 16px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mute {
|
||||||
|
max-width: 220px;
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.follow {
|
||||||
|
max-width: 220px;
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 92%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.pressed {
|
||||||
|
border-bottom-color: rgba(255, 255, 255, 0.2);
|
||||||
|
border-top-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-counts {
|
||||||
|
display: flex;
|
||||||
|
line-height:16px;
|
||||||
|
padding: 1em 1.5em 0em 1em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-count {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size:1em;
|
||||||
|
font-weight: bolder;
|
||||||
|
margin: 0 0 0.25em;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
36
src/components/group_page/group_page.js
Normal file
36
src/components/group_page/group_page.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import GroupCardContent from '../group_card_content/group_card_content.vue'
|
||||||
|
import Timeline from '../timeline/timeline.vue'
|
||||||
|
|
||||||
|
const GroupPage = {
|
||||||
|
created () {
|
||||||
|
this.$store.dispatch('startFetching', { 'timeline': 'group', 'identifier': this.groupName })
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
this.$store.commit('clearTimeline', { timeline: 'group' })
|
||||||
|
this.$store.dispatch('stopFetching', 'group')
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
timeline () { return this.$store.state.statuses.timelines.group },
|
||||||
|
groupName () {
|
||||||
|
return this.$route.params.name
|
||||||
|
},
|
||||||
|
group () {
|
||||||
|
return this.$store.state.groups.groupsObject[this.groupName]
|
||||||
|
},
|
||||||
|
isMember () {
|
||||||
|
return this.$store.state.groups.groupMemberships[this.groupName]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
groupName () {
|
||||||
|
this.$store.commit('clearTimeline', { timeline: 'group' })
|
||||||
|
this.$store.dispatch('startFetching', { 'timeline': 'group', 'identifier': this.groupName })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
GroupCardContent,
|
||||||
|
Timeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupPage
|
21
src/components/group_page/group_page.vue
Normal file
21
src/components/group_page/group_page.vue
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="group" class="group-page panel panel-default base00-background">
|
||||||
|
<group-card-content :group="group" :isMember="isMember"></group-card-content>
|
||||||
|
</div>
|
||||||
|
<Timeline :title="'Group Timeline'" :group="group" v-bind:timeline="timeline" v-bind:timeline-name="'group'" :groupName="groupName" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./group_page.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
.group-page {
|
||||||
|
flex: 2;
|
||||||
|
flex-basis: 500px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -74,6 +74,18 @@ const PostStatusForm = {
|
||||||
name: '',
|
name: '',
|
||||||
img: image_url
|
img: image_url
|
||||||
}))
|
}))
|
||||||
|
} else if (firstchar === '!') {
|
||||||
|
const matchedGroups = filter(this.groups, (group) => group.nickname.match(this.textAtCaret.slice(1)))
|
||||||
|
if (matchedGroups.length <= 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/* eslint-disable */
|
||||||
|
return map(take(matchedGroups, 5), ({nickname, original_logo}) => ({
|
||||||
|
screen_name: `!${nickname}`,
|
||||||
|
name: '',
|
||||||
|
img: original_logo || 'https://placehold.it/48x48'
|
||||||
|
}))
|
||||||
|
/* eslint-enable */
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -90,6 +102,9 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
emoji () {
|
emoji () {
|
||||||
return this.$store.state.config.emoji || []
|
return this.$store.state.config.emoji || []
|
||||||
|
},
|
||||||
|
groups () {
|
||||||
|
return this.$store.state.groups.groups
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -54,6 +54,11 @@
|
||||||
{{status.in_reply_to_screen_name}}
|
{{status.in_reply_to_screen_name}}
|
||||||
</router-link>
|
</router-link>
|
||||||
</small>
|
</small>
|
||||||
|
<small v-for="group in status.statusnet_in_groups"> |
|
||||||
|
<router-link :to="{ name: 'group-page', params: { name: group.nickname } }">
|
||||||
|
{{group.nickname}}
|
||||||
|
</router-link>
|
||||||
|
</small>
|
||||||
<template v-if="isReply">
|
<template v-if="isReply">
|
||||||
<small>
|
<small>
|
||||||
<a href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"><i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i></a>
|
<a href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"><i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i></a>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Timeline from '../timeline/timeline.vue'
|
||||||
const TagTimeline = {
|
const TagTimeline = {
|
||||||
created () {
|
created () {
|
||||||
this.$store.commit('clearTimeline', { timeline: 'tag' })
|
this.$store.commit('clearTimeline', { timeline: 'tag' })
|
||||||
this.$store.dispatch('startFetching', { 'tag': this.tag })
|
this.$store.dispatch('startFetching', ['identifier', this.tag])
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Timeline
|
Timeline
|
||||||
|
@ -15,7 +15,7 @@ const TagTimeline = {
|
||||||
watch: {
|
watch: {
|
||||||
tag () {
|
tag () {
|
||||||
this.$store.commit('clearTimeline', { timeline: 'tag' })
|
this.$store.commit('clearTimeline', { timeline: 'tag' })
|
||||||
this.$store.dispatch('startFetching', { 'tag': this.tag })
|
this.$store.dispatch('startFetching', ['identifier', this.tag])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
|
|
|
@ -9,6 +9,7 @@ const Timeline = {
|
||||||
'timelineName',
|
'timelineName',
|
||||||
'title',
|
'title',
|
||||||
'userId',
|
'userId',
|
||||||
|
'groupName',
|
||||||
'tag'
|
'tag'
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
|
@ -24,6 +25,9 @@ const Timeline = {
|
||||||
friends () {
|
friends () {
|
||||||
return this.timeline.friends
|
return this.timeline.friends
|
||||||
},
|
},
|
||||||
|
members () {
|
||||||
|
return this.timeline.members
|
||||||
|
},
|
||||||
viewing () {
|
viewing () {
|
||||||
return this.timeline.viewing
|
return this.timeline.viewing
|
||||||
},
|
},
|
||||||
|
@ -49,7 +53,7 @@ const Timeline = {
|
||||||
timeline: this.timelineName,
|
timeline: this.timelineName,
|
||||||
showImmediately,
|
showImmediately,
|
||||||
userId: this.userId,
|
userId: this.userId,
|
||||||
tag: this.tag
|
identifier: this.tag || this.groupName
|
||||||
})
|
})
|
||||||
|
|
||||||
// don't fetch followers for public, friend, twkn
|
// don't fetch followers for public, friend, twkn
|
||||||
|
@ -57,6 +61,9 @@ const Timeline = {
|
||||||
this.fetchFriends()
|
this.fetchFriends()
|
||||||
this.fetchFollowers()
|
this.fetchFollowers()
|
||||||
}
|
}
|
||||||
|
if (this.timelineName === 'group') {
|
||||||
|
this.fetchGroup()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('scroll', this.scrollLoad)
|
window.removeEventListener('scroll', this.scrollLoad)
|
||||||
|
@ -77,7 +84,7 @@ const Timeline = {
|
||||||
older: true,
|
older: true,
|
||||||
showImmediately: true,
|
showImmediately: true,
|
||||||
userId: this.userId,
|
userId: this.userId,
|
||||||
tag: this.tag
|
identifier: this.tag || this.groupName
|
||||||
}).then(() => store.commit('setLoading', { timeline: this.timelineName, value: false }))
|
}).then(() => store.commit('setLoading', { timeline: this.timelineName, value: false }))
|
||||||
},
|
},
|
||||||
fetchFollowers () {
|
fetchFollowers () {
|
||||||
|
@ -90,6 +97,17 @@ const Timeline = {
|
||||||
this.$store.state.api.backendInteractor.fetchFriends({ id })
|
this.$store.state.api.backendInteractor.fetchFriends({ id })
|
||||||
.then((friends) => this.$store.dispatch('addFriends', { friends }))
|
.then((friends) => this.$store.dispatch('addFriends', { friends }))
|
||||||
},
|
},
|
||||||
|
fetchGroup () {
|
||||||
|
const ident = this.groupName
|
||||||
|
this.$store.dispatch('fetchGroup', { 'groupName': ident })
|
||||||
|
this.$store.dispatch('fetchIsMember', { 'groupName': ident, 'id': this.$store.state.users.currentUser.id })
|
||||||
|
this.$store.state.api.backendInteractor.fetchMembers({ 'groupName': ident })
|
||||||
|
.then((members) => {
|
||||||
|
this.$store.dispatch('addMembers', { members })
|
||||||
|
this.$router.push('/groups/temp') // TODO FIX THIS
|
||||||
|
this.$router.push(`/groups/${ident}`) // ;_;
|
||||||
|
})
|
||||||
|
},
|
||||||
scrollLoad (e) {
|
scrollLoad (e) {
|
||||||
let height = Math.max(document.body.offsetHeight, document.body.scrollHeight)
|
let height = Math.max(document.body.offsetHeight, document.body.scrollHeight)
|
||||||
if (this.timeline.loading === false &&
|
if (this.timeline.loading === false &&
|
||||||
|
|
|
@ -48,6 +48,18 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="timeline panel panel-default" v-else-if="viewing == 'members'">
|
||||||
|
<div class="panel-heading timeline-heading base01-background base04">
|
||||||
|
<div class="title">
|
||||||
|
{{$t('group_card.members')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body base02-background">
|
||||||
|
<div class="timeline">
|
||||||
|
<user-card v-for="member in members" :user="member" :showFollows="false"></user-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script src="./timeline.js"></script>
|
<script src="./timeline.js"></script>
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,11 @@ const en = {
|
||||||
followees: 'Following',
|
followees: 'Following',
|
||||||
per_day: 'per day'
|
per_day: 'per day'
|
||||||
},
|
},
|
||||||
|
group_card: {
|
||||||
|
members: 'Members',
|
||||||
|
join: 'Join group',
|
||||||
|
leave: 'Leave group'
|
||||||
|
},
|
||||||
timeline: {
|
timeline: {
|
||||||
show_new: 'Show new',
|
show_new: 'Show new',
|
||||||
error_fetching: 'Error fetching updates',
|
error_fetching: 'Error fetching updates',
|
||||||
|
|
|
@ -9,12 +9,14 @@ import TagTimeline from './components/tag_timeline/tag_timeline.vue'
|
||||||
import ConversationPage from './components/conversation-page/conversation-page.vue'
|
import ConversationPage from './components/conversation-page/conversation-page.vue'
|
||||||
import Mentions from './components/mentions/mentions.vue'
|
import Mentions from './components/mentions/mentions.vue'
|
||||||
import UserProfile from './components/user_profile/user_profile.vue'
|
import UserProfile from './components/user_profile/user_profile.vue'
|
||||||
|
import GroupPage from './components/group_page/group_page.vue'
|
||||||
import Settings from './components/settings/settings.vue'
|
import Settings from './components/settings/settings.vue'
|
||||||
import Registration from './components/registration/registration.vue'
|
import Registration from './components/registration/registration.vue'
|
||||||
import UserSettings from './components/user_settings/user_settings.vue'
|
import UserSettings from './components/user_settings/user_settings.vue'
|
||||||
|
|
||||||
import statusesModule from './modules/statuses.js'
|
import statusesModule from './modules/statuses.js'
|
||||||
import usersModule from './modules/users.js'
|
import usersModule from './modules/users.js'
|
||||||
|
import groupsModule from './modules/groups.js'
|
||||||
import apiModule from './modules/api.js'
|
import apiModule from './modules/api.js'
|
||||||
import configModule from './modules/config.js'
|
import configModule from './modules/config.js'
|
||||||
|
|
||||||
|
@ -55,6 +57,7 @@ const store = new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
statuses: statusesModule,
|
statuses: statusesModule,
|
||||||
users: usersModule,
|
users: usersModule,
|
||||||
|
groups: groupsModule,
|
||||||
api: apiModule,
|
api: apiModule,
|
||||||
config: configModule
|
config: configModule
|
||||||
},
|
},
|
||||||
|
@ -70,6 +73,7 @@ const routes = [
|
||||||
{ path: '/tag/:tag', component: TagTimeline },
|
{ path: '/tag/:tag', component: TagTimeline },
|
||||||
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
|
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
|
||||||
{ name: 'user-profile', path: '/users/:id', component: UserProfile },
|
{ name: 'user-profile', path: '/users/:id', component: UserProfile },
|
||||||
|
{ name: 'group-page', path: '/groups/:name', component: GroupPage },
|
||||||
{ name: 'mentions', path: '/:username/mentions', component: Mentions },
|
{ name: 'mentions', path: '/:username/mentions', component: Mentions },
|
||||||
{ name: 'settings', path: '/settings', component: Settings },
|
{ name: 'settings', path: '/settings', component: Settings },
|
||||||
{ name: 'registration', path: '/registration', component: Registration },
|
{ name: 'registration', path: '/registration', component: Registration },
|
||||||
|
|
59
src/modules/groups.js
Normal file
59
src/modules/groups.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { compact, map, each, merge } from 'lodash'
|
||||||
|
|
||||||
|
export const mergeOrAdd = (arr, obj, item) => {
|
||||||
|
if (!item) { return false }
|
||||||
|
const oldItem = obj[item.nickname]
|
||||||
|
if (oldItem) {
|
||||||
|
// We already have this, so only merge the new info.
|
||||||
|
merge(oldItem, item)
|
||||||
|
return {item: oldItem, new: false}
|
||||||
|
} else {
|
||||||
|
// This is a new item, prepare it
|
||||||
|
arr.push(item)
|
||||||
|
obj[item.nickname] = item
|
||||||
|
return {item, new: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultState = {
|
||||||
|
groups: [],
|
||||||
|
groupsObject: {},
|
||||||
|
groupMemberships: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups = {
|
||||||
|
state: defaultState,
|
||||||
|
mutations: {
|
||||||
|
addNewGroups (state, statuses) {
|
||||||
|
each(statuses, (groups) => {
|
||||||
|
each(groups, (group) => mergeOrAdd(state.groups, state.groupsObject, group))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
addNewGroup (state, group) {
|
||||||
|
mergeOrAdd(state.groups, state.groupsObject, group)
|
||||||
|
if (!state.groupMemberships[group.nickname]) {
|
||||||
|
// insert some fake placeholder data
|
||||||
|
state.groupMemberships[group.nickname] = {'is_member': false}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addMembership (state, {groupName, membership}) {
|
||||||
|
state.groupMemberships[groupName] = membership
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchGroup (store, { groupName }) {
|
||||||
|
store.rootState.api.backendInteractor.fetchGroup({ groupName })
|
||||||
|
.then((group) => store.commit('addNewGroup', group))
|
||||||
|
},
|
||||||
|
fetchIsMember (store, { groupName, id }) {
|
||||||
|
store.rootState.api.backendInteractor.fetchIsMember({id, groupName})
|
||||||
|
.then((membership) => store.commit('addMembership', {groupName, 'membership': membership.is_member}))
|
||||||
|
},
|
||||||
|
addNewStatuses (store, { statuses }) {
|
||||||
|
const groups = compact(map(statuses, 'statusnet_in_groups'))
|
||||||
|
store.commit('addNewGroups', groups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default groups
|
|
@ -93,6 +93,21 @@ export const defaultState = {
|
||||||
followers: [],
|
followers: [],
|
||||||
friends: [],
|
friends: [],
|
||||||
viewing: 'statuses'
|
viewing: 'statuses'
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
statuses: [],
|
||||||
|
statusesObject: {},
|
||||||
|
faves: [],
|
||||||
|
visibleStatuses: [],
|
||||||
|
visibleStatusesObject: {},
|
||||||
|
newStatusCount: 0,
|
||||||
|
maxId: 0,
|
||||||
|
minVisibleId: 0,
|
||||||
|
loading: false,
|
||||||
|
members: [],
|
||||||
|
followers: [],
|
||||||
|
friends: [],
|
||||||
|
viewing: 'statuses'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,12 +427,18 @@ export const mutations = {
|
||||||
// load followers / friends only when needed
|
// load followers / friends only when needed
|
||||||
state.timelines['user'].viewing = v
|
state.timelines['user'].viewing = v
|
||||||
},
|
},
|
||||||
|
setGroupView (state, { v }) {
|
||||||
|
state.timelines['group'].viewing = v
|
||||||
|
},
|
||||||
addFriends (state, { friends }) {
|
addFriends (state, { friends }) {
|
||||||
state.timelines['user'].friends = friends
|
state.timelines['user'].friends = friends
|
||||||
},
|
},
|
||||||
addFollowers (state, { followers }) {
|
addFollowers (state, { followers }) {
|
||||||
state.timelines['user'].followers = followers
|
state.timelines['user'].followers = followers
|
||||||
},
|
},
|
||||||
|
addMembers (state, { members }) {
|
||||||
|
state.timelines['group'].members = members
|
||||||
|
},
|
||||||
markNotificationsAsSeen (state, notifications) {
|
markNotificationsAsSeen (state, notifications) {
|
||||||
each(notifications, (notification) => {
|
each(notifications, (notification) => {
|
||||||
notification.seen = true
|
notification.seen = true
|
||||||
|
@ -440,6 +461,9 @@ const statuses = {
|
||||||
addFollowers ({ rootState, commit }, { followers }) {
|
addFollowers ({ rootState, commit }, { followers }) {
|
||||||
commit('addFollowers', { followers })
|
commit('addFollowers', { followers })
|
||||||
},
|
},
|
||||||
|
addMembers ({ rootState, commit }, { members }) {
|
||||||
|
commit('addMembers', { members })
|
||||||
|
},
|
||||||
deleteStatus ({ rootState, commit }, status) {
|
deleteStatus ({ rootState, commit }, status) {
|
||||||
commit('setDeleted', { status })
|
commit('setDeleted', { status })
|
||||||
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
||||||
|
|
|
@ -29,6 +29,14 @@ const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json'
|
||||||
const BLOCKING_URL = '/api/blocks/create.json'
|
const BLOCKING_URL = '/api/blocks/create.json'
|
||||||
const UNBLOCKING_URL = '/api/blocks/destroy.json'
|
const UNBLOCKING_URL = '/api/blocks/destroy.json'
|
||||||
const USER_URL = '/api/users/show.json'
|
const USER_URL = '/api/users/show.json'
|
||||||
|
const GROUP_URL = '/api/statusnet/groups/show'
|
||||||
|
const GROUP_TIMELINE_URL = '/api/statusnet/groups/timeline'
|
||||||
|
const GROUP_JOINING_URL = '/api/statusnet/groups/join'
|
||||||
|
const GROUP_LEAVING_URL = '/api/statusnet/groups/leave'
|
||||||
|
const GROUP_CREATE_URL = '/api/statusnet/groups/create.json'
|
||||||
|
const USER_MEMBERSHIPS_URL = '/api/statusnet/groups/list.json'
|
||||||
|
const GROUP_MEMBERS_URL = '/api/statusnet/groups/membership'
|
||||||
|
const GROUP_IS_MEMBER_URL = '/api/statusnet/groups/is_member.json'
|
||||||
|
|
||||||
import { each, map } from 'lodash'
|
import { each, map } from 'lodash'
|
||||||
import 'whatwg-fetch'
|
import 'whatwg-fetch'
|
||||||
|
@ -254,14 +262,15 @@ const setUserMute = ({id, credentials, muted = true}) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => {
|
const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, identifier = false}) => {
|
||||||
const timelineUrls = {
|
const timelineUrls = {
|
||||||
public: PUBLIC_TIMELINE_URL,
|
public: PUBLIC_TIMELINE_URL,
|
||||||
friends: FRIENDS_TIMELINE_URL,
|
friends: FRIENDS_TIMELINE_URL,
|
||||||
mentions: MENTIONS_URL,
|
mentions: MENTIONS_URL,
|
||||||
'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL,
|
'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL,
|
||||||
user: QVITTER_USER_TIMELINE_URL,
|
user: QVITTER_USER_TIMELINE_URL,
|
||||||
tag: TAG_TIMELINE_URL
|
tag: TAG_TIMELINE_URL,
|
||||||
|
group: GROUP_TIMELINE_URL
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = timelineUrls[timeline]
|
let url = timelineUrls[timeline]
|
||||||
|
@ -277,8 +286,8 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use
|
||||||
if (userId) {
|
if (userId) {
|
||||||
params.push(['user_id', userId])
|
params.push(['user_id', userId])
|
||||||
}
|
}
|
||||||
if (tag) {
|
if (identifier) {
|
||||||
url += `/${tag}.json`
|
url += `/${identifier}.json`
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
||||||
|
@ -358,6 +367,65 @@ const fetchMutes = ({credentials}) => {
|
||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchGroup = ({ groupName }) => {
|
||||||
|
const url = `${GROUP_URL}/${groupName}.json`
|
||||||
|
|
||||||
|
return fetch(url).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinGroup = ({groupName, credentials}) => {
|
||||||
|
const url = `${GROUP_JOINING_URL}/${groupName}.json`
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
headers: authHeaders(credentials),
|
||||||
|
method: 'POST'
|
||||||
|
}).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const leaveGroup = ({groupName, credentials}) => {
|
||||||
|
const url = `${GROUP_LEAVING_URL}/${groupName}.json`
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
headers: authHeaders(credentials),
|
||||||
|
method: 'POST'
|
||||||
|
}).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const createGroup = ({params, credentials}) => {
|
||||||
|
const form = new FormData()
|
||||||
|
|
||||||
|
each(params, (value, key) => {
|
||||||
|
if (value) {
|
||||||
|
form.append(key, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return fetch(GROUP_CREATE_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
body: form
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchMemberships = ({id, credentials}) => {
|
||||||
|
const url = `${USER_MEMBERSHIPS_URL}?user_id=${id}`
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
headers: authHeaders(credentials)
|
||||||
|
}).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchMembers = ({ groupName }) => {
|
||||||
|
const url = `${GROUP_MEMBERS_URL}/${groupName}.json`
|
||||||
|
|
||||||
|
return fetch(url).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchIsMember = ({id, groupName}) => {
|
||||||
|
const url = `${GROUP_IS_MEMBER_URL}?user_id=${id}&group_name=${groupName}`
|
||||||
|
|
||||||
|
return fetch(url).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
const apiService = {
|
const apiService = {
|
||||||
verifyCredentials,
|
verifyCredentials,
|
||||||
fetchTimeline,
|
fetchTimeline,
|
||||||
|
@ -384,7 +452,14 @@ const apiService = {
|
||||||
updateBg,
|
updateBg,
|
||||||
updateProfile,
|
updateProfile,
|
||||||
updateBanner,
|
updateBanner,
|
||||||
externalProfile
|
externalProfile,
|
||||||
|
fetchGroup,
|
||||||
|
joinGroup,
|
||||||
|
leaveGroup,
|
||||||
|
createGroup,
|
||||||
|
fetchMemberships,
|
||||||
|
fetchMembers,
|
||||||
|
fetchIsMember
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiService
|
export default apiService
|
||||||
|
|
|
@ -50,6 +50,32 @@ const backendInteractorService = (credentials) => {
|
||||||
return apiService.setUserMute({id, muted, credentials})
|
return apiService.setUserMute({id, muted, credentials})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchGroup = ({ groupName }) => {
|
||||||
|
return apiService.fetchGroup({ groupName })
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinGroup = ({ groupName }) => {
|
||||||
|
return apiService.joinGroup({groupName, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const leaveGroup = ({ groupName }) => {
|
||||||
|
return apiService.leaveGroup({groupName, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchMemberships = ({ id }) => {
|
||||||
|
return apiService.fetchMemberships({id, credentials})
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchMembers = ({ groupName }) => {
|
||||||
|
return apiService.fetchMembers({ groupName })
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchIsMember = ({id, groupName}) => {
|
||||||
|
return apiService.fetchIsMember({id, groupName})
|
||||||
|
}
|
||||||
|
|
||||||
|
const createGroup = (params) => apiService.createGroup({params, credentials})
|
||||||
|
|
||||||
const fetchMutes = () => apiService.fetchMutes({credentials})
|
const fetchMutes = () => apiService.fetchMutes({credentials})
|
||||||
|
|
||||||
const register = (params) => apiService.register(params)
|
const register = (params) => apiService.register(params)
|
||||||
|
@ -80,7 +106,14 @@ const backendInteractorService = (credentials) => {
|
||||||
updateBg,
|
updateBg,
|
||||||
updateBanner,
|
updateBanner,
|
||||||
updateProfile,
|
updateProfile,
|
||||||
externalProfile
|
externalProfile,
|
||||||
|
fetchGroup,
|
||||||
|
joinGroup,
|
||||||
|
leaveGroup,
|
||||||
|
createGroup,
|
||||||
|
fetchMemberships,
|
||||||
|
fetchMembers,
|
||||||
|
fetchIsMember
|
||||||
}
|
}
|
||||||
|
|
||||||
return backendInteractorServiceInstance
|
return backendInteractorServiceInstance
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const addPositionToWords = (words) => {
|
||||||
export const splitIntoWords = (str) => {
|
export const splitIntoWords = (str) => {
|
||||||
// Split at word boundaries
|
// Split at word boundaries
|
||||||
const regex = /\b/
|
const regex = /\b/
|
||||||
const triggers = /[@#:]+$/
|
const triggers = /[@#:!]+$/
|
||||||
|
|
||||||
let split = str.split(regex)
|
let split = str.split(regex)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ const update = ({store, statuses, timeline, showImmediately}) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false, showImmediately = false, userId = false, tag = false}) => {
|
const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false, showImmediately = false, userId = false, identifier = false}) => {
|
||||||
const args = { timeline, credentials }
|
const args = { timeline, credentials }
|
||||||
const rootState = store.rootState || store.state
|
const rootState = store.rootState || store.state
|
||||||
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
|
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
|
||||||
|
@ -26,16 +26,16 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false
|
||||||
}
|
}
|
||||||
|
|
||||||
args['userId'] = userId
|
args['userId'] = userId
|
||||||
args['tag'] = tag
|
args['identifier'] = identifier
|
||||||
|
|
||||||
return apiService.fetchTimeline(args)
|
return apiService.fetchTimeline(args)
|
||||||
.then((statuses) => update({store, statuses, timeline, showImmediately}),
|
.then((statuses) => update({store, statuses, timeline, showImmediately}),
|
||||||
() => store.dispatch('setError', { value: true }))
|
() => store.dispatch('setError', { value: true }))
|
||||||
}
|
}
|
||||||
|
|
||||||
const startFetching = ({timeline = 'friends', credentials, store, userId = false, tag = false}) => {
|
const startFetching = ({timeline = 'friends', credentials, store, userId = false, identifier = false}) => {
|
||||||
fetchAndUpdate({timeline, credentials, store, showImmediately: true, userId, tag})
|
fetchAndUpdate({timeline, credentials, store, showImmediately: true, userId, identifier})
|
||||||
const boundFetchAndUpdate = () => fetchAndUpdate({ timeline, credentials, store, userId, tag })
|
const boundFetchAndUpdate = () => fetchAndUpdate({ timeline, credentials, store, showImmediately: false, userId, identifier })
|
||||||
return setInterval(boundFetchAndUpdate, 10000)
|
return setInterval(boundFetchAndUpdate, 10000)
|
||||||
}
|
}
|
||||||
const timelineFetcher = {
|
const timelineFetcher = {
|
||||||
|
|
Loading…
Reference in a new issue