2019-12-02 23:30:16 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="boards">
|
|
|
|
|
<section class="section">
|
|
|
|
|
<div id="boards-container" class="container" v-if="blocks">
|
|
|
|
|
<div
|
|
|
|
|
v-masonry="" transition-duration="0.3s"
|
|
|
|
|
item-selector=".grid-item"
|
|
|
|
|
column-width=".grid-sizer"
|
|
|
|
|
gutter=".gutter-sizer"
|
|
|
|
|
>
|
|
|
|
|
<template v-for="item in blocks">
|
|
|
|
|
<div v-bind:key="item.id"
|
|
|
|
|
v-masonry-tile
|
|
|
|
|
:class="item.class"
|
|
|
|
|
class="grid">
|
|
|
|
|
<div class="grid-sizer"></div>
|
|
|
|
|
<div class="gutter-sizer"></div>
|
|
|
|
|
<div class="board-card grid-item">
|
2019-12-07 14:15:05 +08:00
|
|
|
<div @mouseenter="currentEditBoard = item.id"
|
|
|
|
|
@mouseleave="currentEditBoard = null"
|
|
|
|
|
>
|
2019-12-02 23:30:16 +08:00
|
|
|
<div class="card-image">
|
2019-12-07 14:15:05 +08:00
|
|
|
<BoardEditorUI
|
|
|
|
|
v-show="shouldShowEdit(item.id)"
|
|
|
|
|
:board="item"
|
|
|
|
|
v-on:board-delete-succeed="reset"
|
2019-12-07 15:10:35 +08:00
|
|
|
v-on:board-save-succeed="reset"
|
2019-12-07 14:15:05 +08:00
|
|
|
></BoardEditorUI>
|
|
|
|
|
<router-link :to="{ name: 'board', params: { boardId: item.id } }">
|
|
|
|
|
<img :src="item.preview_image_url"
|
|
|
|
|
@load="onPinImageLoaded(item.id)"
|
|
|
|
|
:style="item.style"
|
|
|
|
|
v-show="item.preview_image_url"
|
|
|
|
|
class="preview-image">
|
|
|
|
|
</router-link>
|
2019-12-02 23:30:16 +08:00
|
|
|
</div>
|
|
|
|
|
<div class="board-footer">
|
|
|
|
|
<p class="sub-title board-info">{{ item.name }}</p>
|
|
|
|
|
<p class="description">
|
|
|
|
|
<small>
|
|
|
|
|
Pins in board: <span class="num-pins">{{ item.total_pins }}</span>
|
|
|
|
|
</small>
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2019-12-07 14:15:05 +08:00
|
|
|
</div>
|
2019-12-02 23:30:16 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<loadingSpinner v-bind:show="status.loading"></loadingSpinner>
|
|
|
|
|
<noMore v-bind:show="!status.hasNext"></noMore>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import API from './api';
|
|
|
|
|
import pinHandler from './utils/PinHandler';
|
|
|
|
|
import loadingSpinner from './loadingSpinner.vue';
|
|
|
|
|
import noMore from './noMore.vue';
|
|
|
|
|
import scroll from './utils/scroll';
|
|
|
|
|
import placeholder from '../assets/pinry-placeholder.jpg';
|
2019-12-07 14:15:05 +08:00
|
|
|
import BoardEditorUI from './editors/BoardEditUI.vue';
|
2019-12-07 17:24:41 +08:00
|
|
|
import bus from './utils/bus';
|
2019-12-02 23:30:16 +08:00
|
|
|
|
|
|
|
|
function createBoardItem(board) {
|
|
|
|
|
const defaultPreviewImage = placeholder;
|
|
|
|
|
const boardItem = {};
|
|
|
|
|
const pins4Board = board.pins_detail;
|
|
|
|
|
let previewImage = {
|
|
|
|
|
image: { thumbnail: { image: null, width: 240, height: 240 } },
|
|
|
|
|
};
|
|
|
|
|
if (pins4Board.length > 0) {
|
|
|
|
|
[previewImage] = pins4Board;
|
|
|
|
|
}
|
|
|
|
|
boardItem.id = board.id;
|
|
|
|
|
boardItem.name = board.name;
|
|
|
|
|
boardItem.total_pins = pins4Board.length;
|
|
|
|
|
if (previewImage.image.thumbnail.image !== null) {
|
|
|
|
|
boardItem.preview_image_url = pinHandler.escapeUrl(
|
|
|
|
|
previewImage.image.thumbnail.image,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
boardItem.preview_image_url = defaultPreviewImage;
|
|
|
|
|
}
|
|
|
|
|
boardItem.style = {
|
|
|
|
|
width: `${previewImage.image.thumbnail.width}px`,
|
|
|
|
|
height: `${previewImage.image.thumbnail.height}px`,
|
|
|
|
|
};
|
|
|
|
|
boardItem.class = {};
|
|
|
|
|
return boardItem;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-07 14:15:05 +08:00
|
|
|
function initialData() {
|
|
|
|
|
return {
|
|
|
|
|
currentEditBoard: null,
|
|
|
|
|
blocks: [],
|
|
|
|
|
blocksMap: {},
|
|
|
|
|
status: {
|
|
|
|
|
loading: false,
|
|
|
|
|
hasNext: true,
|
|
|
|
|
offset: 0,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-02 23:30:16 +08:00
|
|
|
export default {
|
|
|
|
|
name: 'boards',
|
|
|
|
|
components: {
|
|
|
|
|
loadingSpinner,
|
|
|
|
|
noMore,
|
2019-12-07 14:15:05 +08:00
|
|
|
BoardEditorUI,
|
2019-12-02 23:30:16 +08:00
|
|
|
},
|
2019-12-07 14:15:05 +08:00
|
|
|
data: initialData,
|
2019-12-02 23:30:16 +08:00
|
|
|
props: ['boardUsername'],
|
|
|
|
|
methods: {
|
2019-12-07 14:15:05 +08:00
|
|
|
initialize() {
|
|
|
|
|
this.fetchMore(true);
|
|
|
|
|
},
|
|
|
|
|
reset() {
|
|
|
|
|
const data = initialData();
|
|
|
|
|
Object.entries(data).forEach(
|
|
|
|
|
(kv) => {
|
|
|
|
|
const [key, value] = kv;
|
|
|
|
|
this[key] = value;
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
this.initialize();
|
|
|
|
|
},
|
|
|
|
|
shouldShowEdit(boardId) {
|
|
|
|
|
return this.currentEditBoard === boardId;
|
|
|
|
|
},
|
2019-12-02 23:30:16 +08:00
|
|
|
onPinImageLoaded(itemId) {
|
|
|
|
|
this.blocksMap[itemId].class = {
|
|
|
|
|
'image-loaded': true,
|
|
|
|
|
};
|
2019-12-06 14:15:39 +08:00
|
|
|
this.blocksMap[itemId].style.height = 'auto';
|
2019-12-02 23:30:16 +08:00
|
|
|
},
|
|
|
|
|
registerScrollEvent() {
|
|
|
|
|
const self = this;
|
|
|
|
|
scroll.bindScroll2Bottom(
|
|
|
|
|
() => {
|
|
|
|
|
if (self.status.loading || !self.status.hasNext) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
self.fetchMore();
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
buildBlocks(results) {
|
|
|
|
|
const blocks = [];
|
|
|
|
|
results.forEach(
|
|
|
|
|
(pin) => {
|
|
|
|
|
const item = createBoardItem(pin);
|
|
|
|
|
blocks.push(
|
|
|
|
|
item,
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
return blocks;
|
|
|
|
|
},
|
|
|
|
|
shouldFetchMore(created) {
|
|
|
|
|
if (!created) {
|
|
|
|
|
if (this.status.loading) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!this.status.hasNext) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
fetchMore(created) {
|
|
|
|
|
if (!this.shouldFetchMore(created)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.status.loading = true;
|
|
|
|
|
const promise = API.fetchBoardForUser(this.boardUsername);
|
|
|
|
|
promise.then(
|
|
|
|
|
(resp) => {
|
|
|
|
|
const { results, next } = resp.data;
|
|
|
|
|
let newBlocks = this.buildBlocks(results);
|
|
|
|
|
newBlocks.forEach(
|
|
|
|
|
(item) => { this.blocksMap[item.id] = item; },
|
|
|
|
|
);
|
|
|
|
|
newBlocks = this.blocks.concat(newBlocks);
|
|
|
|
|
this.blocks = newBlocks;
|
|
|
|
|
this.status.offset = newBlocks.length;
|
|
|
|
|
this.status.hasNext = !(next === null);
|
|
|
|
|
this.status.loading = false;
|
|
|
|
|
},
|
|
|
|
|
() => { this.status.loading = false; },
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
created() {
|
2019-12-07 17:24:41 +08:00
|
|
|
bus.bus.$on(bus.events.refreshBoards, this.reset);
|
2019-12-02 23:30:16 +08:00
|
|
|
this.registerScrollEvent();
|
2019-12-07 14:15:05 +08:00
|
|
|
this.initialize();
|
2019-12-02 23:30:16 +08:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
/* grid */
|
2019-12-04 20:18:30 +08:00
|
|
|
@import 'utils/pin';
|
|
|
|
|
|
2019-12-02 23:30:16 +08:00
|
|
|
.grid-sizer,
|
2019-12-04 20:18:30 +08:00
|
|
|
.grid-item { width: $pin-preview-width; }
|
2019-12-02 23:30:16 +08:00
|
|
|
.grid-item {
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
}
|
|
|
|
|
.gutter-sizer {
|
|
|
|
|
width: 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* card */
|
|
|
|
|
$pin-footer-position-fix: -6px;
|
|
|
|
|
$avatar-width: 30px;
|
|
|
|
|
$avatar-height: 30px;
|
|
|
|
|
@import './utils/fonts';
|
|
|
|
|
@import './utils/loader.scss';
|
|
|
|
|
|
|
|
|
|
.board-card{
|
|
|
|
|
.card-image > img {
|
2019-12-06 14:15:39 +08:00
|
|
|
min-width: $pin-preview-width;
|
2019-12-02 23:30:16 +08:00
|
|
|
background-color: white;
|
|
|
|
|
border-radius: 3px 3px 0 0;
|
|
|
|
|
@include loader('../assets/loader.gif');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.board-footer {
|
|
|
|
|
position: relative;
|
|
|
|
|
top: $pin-footer-position-fix;
|
|
|
|
|
background-color: white;
|
|
|
|
|
border-radius: 0 0 3px 3px ;
|
|
|
|
|
box-shadow: 0 1px 0 #bbb;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
.description {
|
|
|
|
|
@include secondary-font;
|
|
|
|
|
padding-left: 10px;
|
|
|
|
|
padding-bottom: 5px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
.board-info {
|
|
|
|
|
padding: 10px;
|
|
|
|
|
color: $main-title-font-color;
|
|
|
|
|
}
|
|
|
|
|
.num-pins {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: $main-title-font-color;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@import 'utils/grid-layout';
|
|
|
|
|
@include screen-grid-layout("#boards-container")
|
|
|
|
|
|
|
|
|
|
</style>
|