<template>
<div class="feed-container">
<div class="navigation">
<b-button
class="btn-add-tweet"
rounded
size="is-medium"
type="is-danger"
icon-left="twitter"
icon-pack="fab"
@click="onAddTweetClick"
>
Tweet :)
</b-button>
<div class="buttons">
<button
class="btn-left"
:class="{ 'btn-active': cardsViewSeen }"
@click="changeViewToCards"
>
<img src="https://img.icons8.com/ios-filled/24/000000/health-data.png" />
</button>
<button
class="btn-right"
:class="{ 'btn-active': mediaObjViewSeen }"
@click="changeViewToMedia"
>
<img src="https://img.icons8.com/ios-filled/24/000000/menu.png" />
</button>
</div>
</div>
<TweetPreviewList
:tweets="tweets"
@infinite="infiniteHandler"
:cards-view-seen="cardsViewSeen"
:media-obj-view-seen="mediaObjViewSeen"
/>
<b-modal :active.sync="isNewTweetModalActive" has-modal-card>
<NewTweetForm />
</b-modal>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import TweetPreviewList from '@/components/common/TweetPreviewList.vue';
import { pusher } from '@/services/Pusher';
import { SET_TWEET } from '@/store/modules/tweet/mutationTypes';
import showStatusToast from '@/components/mixin/showStatusToast';
import NewTweetForm from './NewTweetForm.vue';
export default {
name: 'FeedContainer',
mixins: [showStatusToast],
components: {
TweetPreviewList,
NewTweetForm,
},
data: () => ({
isNewTweetModalActive: false,
page: 1,
mediaObjViewSeen: true,
cardsViewSeen: false
}),
async created() {
try {
await this.fetchTweets({
page: 1
});
} catch (error) {
this.showErrorMessage(error.message);
}
const channel = pusher.subscribe('private-tweets');
channel.bind('tweet.added', (data) => {
this.$store.commit(`tweet/${SET_TWEET}`, data.tweet);
});
},
beforeDestroy() {
pusher.unsubscribe('private-tweets');
},
computed: {
...mapGetters('tweet', {
tweets: 'tweetsSortedByCreatedDate'
}),
},
methods: {
...mapActions('tweet', [
'fetchTweets',
]),
changeViewToMedia() {
this.mediaObjViewSeen = true;
this.cardsViewSeen = false;
},
changeViewToCards() {
this.mediaObjViewSeen = false;
this.cardsViewSeen = true;
},
onAddTweetClick() {
this.showAddTweetModal();
},
showAddTweetModal() {
this.isNewTweetModalActive = true;
},
async infiniteHandler($state) {
try {
const tweets = await this.fetchTweets({ page: this.page + 1 });
if (tweets.length) {
this.page += 1;
$state.loaded();
} else {
$state.complete();
}
} catch (error) {
this.showErrorMessage(error.message);
$state.complete();
}
},
},
mounted() {
if (localStorage.getItem('mediaObjViewSeen') === true) {
this.mediaObjViewSeen = true;
this.cardsViewSeen = false;
}
if (localStorage.getItem('cardsViewSeen') === true) {
this.mediaObjViewSeen = false;
this.cardsViewSeen = true;
}
},
watch: {
mediaObjViewSeen(newMediaObjViewSeen) {
localStorage.setItem('mediaObjViewSeen', newMediaObjViewSeen);
this.mediaObjViewSeen = newMediaObjViewSeen;
},
cardsViewSeen(newCardsViewSeen) {
localStorage.setItem('cardsViewSeen', newCardsViewSeen);
this.cardsViewSeen = newCardsViewSeen;
},
}
};
</script>
<style lang="scss" scoped>
@import '~bulma/sass/utilities/initial-variables';
.buttons {
float: right;
}
.btn-left, .btn-right {
border: none;
padding: 10px;
outline: none;
}
.btn-left {
border-radius: 5px 0 0 5px;
}
.btn-right{
border-radius: 0 5px 5px 0;
}
.btn-active{
background: #ff3860;
}
.navigation {
padding: 10px 0;
margin-bottom: 20px;
}
.modal-card {
border-radius: 6px;
}
.btn-add-tweet {
transition: 0.2s ease-out all;
box-shadow: 1px 5px 5px 0 #00000020;
&:hover {
box-shadow: 1px 1px 0 0 #00000020;
}
@media screen and (max-width: $tablet) {
font-size: 1rem;
}
}
</style>
<template>
<div class="tweets-container">
<transition-group name="slide-prev" tag="div">
<template v-for="tweet in tweets">
<TweetPreview
:key="tweet.id"
:tweet="tweet"
@click="onTweetClick"
:cards-view-seen="cardsViewSeen"
:media-obj-view-seen="mediaObjViewSeen"
/>
</template>
</transition-group>
<infinite-loading @infinite="infiniteHandler">
<div slot="no-more" />
<div slot="no-results" />
<div slot="spinner" />
</infinite-loading>
</div>
</template>
<script>
import InfiniteLoading from 'vue-infinite-loading';
import TweetPreview from './TweetPreview.vue';
export default {
name: 'TweetPreviewList',
props: {
tweets: {
type: Array,
required: true
},
mediaObjViewSeen: {
type: Boolean,
},
cardsViewSeen: {
type: Boolean,
}
},
components: {
TweetPreview,
InfiniteLoading,
},
methods: {
onTweetClick(tweet) {
this.$router.push({ name: 'tweet-page', params: { id: tweet.id } }).catch(() => {});
},
infiniteHandler($state) {
this.$emit('infinite', $state);
},
},
};
</script>
<style lang="scss" scoped>
.tweets-container {
padding-bottom: 20px;
}
</style>
<template>
<div
class="box tweet"
:class="{ 'col-f': cardsViewSeen }"
@click="$emit('click', tweet)"
>
<CardsView v-if="cardsViewSeen" :tweet="tweet" />
<MediaObjView v-if="mediaObjViewSeen" :tweet="tweet" />
</div>
</template>
<script>
import CardsView from '../view/feed/CardsView.vue';
import MediaObjView from '../view/feed/MediaObjView.vue';
export default {
name: 'TweetPreview',
components: {
CardsView,
MediaObjView
},
props: {
tweet: {
type: Object,
required: true,
},
mediaObjViewSeen: {
type: Boolean,
},
cardsViewSeen: {
type: Boolean,
}
},
};
</script>
<style lang="scss" scoped>
@import '../../styles/common';
.col-f {
width: 32%;
display: inline-block;
margin-left: 1%;
vertical-align: top;
}
.card-image {
min-height: 160px;
}
.card-image figure {
min-height: 160px;
}
.tweet {
cursor: pointer;
padding: 15px;
border-radius: 5px;
box-shadow: 5px 5px 5px 0 #00000020;
transition: 0.2s ease-out all;
&:hover {
box-shadow: 1px 1px 0 0 #00000020;
}
&-image {
margin: 12px 0 0 0;
img {
width: auto;
}
}
.nickname {
margin-left: 5px;
}
.created {
margin-left: 5px;
}
}
@media (min-width: 500px) and (max-width: 768px) {
.col-f {
width: 49%;
display: inline-block;
margin-left: 1%;
}
}
@media (max-width: 499px) {
.col-f {
width: 100%;
}
}
</style>
if (localStorage.getItem('mediaObjViewSeen') === true) {
Ключи и значения всегда строки
data: () => ({
view: localStorage.getItem('view') || 'CardsView',
// ...
}),
watch: {
view: val => localStorage.setItem('view', val),
},
data: () => ({
views: [
{
name: 'CardsView',
btnClass: 'btn-left',
img: '...',
},
{
name: 'MediaObjView',
btnClass: 'btn-right',
img: '...',
},
],
// ...
}),
<button
v-for="n in views"
:class="[ n.btnClass, view === n.name ? 'btn-active' : '' ]"
@click="view = n.name"
>
<img :src="n.img">
</button>
view
в TweetPreview
, используете его значение как имя компонента:<component
:is="view"
:tweet="tweet"
/>