Add group page and card components.

This commit is contained in:
eal 2017-11-19 17:44:35 +02:00
parent 7a3e608455
commit 55925383b7
8 changed files with 327 additions and 1 deletions

View file

@ -0,0 +1,228 @@
<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: [ 'groupName' ],
computed: {
group () {
return this.$store.state.groups.groupsObject[this.groupName]
},
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
},
isMember () {
return this.$store.state.groups.groupMemberships[this.groupName]
}
},
methods: {
setMember (value) {
this.$store.state.groups.groupMemberships[this.groupName] = value
},
joinGroup () {
const store = this.$store
store.state.api.backendInteractor.joinGroup({'groupName': this.group.nickname})
.then((joinedGroup) => {
store.commit('addNewGroup', joinedGroup)
this.setMember(true)
})
},
leaveGroup () {
const store = this.$store
store.state.api.backendInteractor.leaveGroup({'groupName': this.group.nickname})
.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>

View file

@ -0,0 +1,38 @@
import GroupCardContent from '../group_card_content/group_card_content.vue'
import Timeline from '../timeline/timeline.vue'
const GroupPage = {
created () {
const name = this.$route.params.name
this.$store.commit('clearTimeline', { timeline: 'group' })
this.$store.dispatch('startFetching', { 'identifier': name })
this.$store.dispatch('fetchGroup', { 'groupName': name })
this.$store.dispatch('fetchIsMember', { 'groupName': name, 'id': this.$store.state.users.currentUser.id })
},
destroyed () {
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] || false
}
},
watch: {
groupName () {
this.$store.dispatch('fetchGroup', { 'groupName': this.groupName })
this.$store.dispatch('fetchIsMember', { 'groupName': this.groupName, 'id': this.$store.state.users.currentUser.id })
this.$store.commit('clearTimeline', { timeline: 'group' })
// this.$store.dispatch('startFetching', { 'identifier': this.groupName })
}
},
components: {
GroupCardContent,
Timeline
}
}
export default GroupPage

View file

@ -0,0 +1,21 @@
<template>
<div>
<div v-if="group" class="group-page panel panel-default base00-background">
<group-card-content :groupName="groupName"></group-card-content>
</div>
<Timeline :title="'Group Timeline'" 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>

View file

@ -158,6 +158,11 @@ const en = {
followees: 'Following',
per_day: 'per day'
},
group_card: {
members: 'Members',
join: 'Join group',
leave: 'Leave group'
},
timeline: {
show_new: 'Show new',
error_fetching: 'Error fetching updates',

View file

@ -9,6 +9,7 @@ import TagTimeline from './components/tag_timeline/tag_timeline.vue'
import ConversationPage from './components/conversation-page/conversation-page.vue'
import Mentions from './components/mentions/mentions.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 Registration from './components/registration/registration.vue'
import UserSettings from './components/user_settings/user_settings.vue'
@ -72,6 +73,7 @@ const routes = [
{ path: '/tag/:tag', component: TagTimeline },
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
{ name: 'user-profile', path: '/users/:id', component: UserProfile },
{ name: 'group-page', path: '/groups/:name', component: GroupPage },
{ name: 'mentions', path: '/:username/mentions', component: Mentions },
{ name: 'settings', path: '/settings', component: Settings },
{ name: 'registration', path: '/registration', component: Registration },

View file

@ -17,7 +17,8 @@ export const mergeOrAdd = (arr, obj, item) => {
export const defaultState = {
groups: [],
groupsObject: {}
groupsObject: {},
groupMemberships: {}
}
const groups = {
@ -27,9 +28,23 @@ const groups = {
each(statuses, (groups) => {
each(groups, (group) => mergeOrAdd(state.groups, state.groupsObject, group))
})
},
addNewGroup (state, group) {
mergeOrAdd(state.groups, state.groupsObject, group)
},
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)

View file

@ -93,6 +93,21 @@ export const defaultState = {
followers: [],
friends: [],
viewing: 'statuses'
},
group: {
statuses: [],
statusesObject: {},
faves: [],
visibleStatuses: [],
visibleStatusesObject: {},
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
loading: false,
members: [],
followers: [],
friends: [],
viewing: 'statuses'
}
}
}

View file

@ -29,12 +29,14 @@ const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json'
const BLOCKING_URL = '/api/blocks/create.json'
const UNBLOCKING_URL = '/api/blocks/destroy.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 'whatwg-fetch'