|
@@ -1,301 +1,311 @@
|
|
<template>
|
|
<template>
|
|
- <div class="dms-page-component">
|
|
|
|
- <div v-if="isLoaded" class="container-fluid mt-3">
|
|
|
|
- <div class="row">
|
|
|
|
- <div class="col-md-3 d-md-block">
|
|
|
|
- <sidebar :user="profile" />
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div class="col-md-5 offset-md-1 mb-5 order-2 order-md-1">
|
|
|
|
- <h1 class="font-weight-bold mb-4">Direct Messages</h1>
|
|
|
|
- <div v-if="threadsLoaded">
|
|
|
|
- <div v-for="(thread, idx) in threads" class="card shadow-sm mb-1" style="border-radius:15px;">
|
|
|
|
- <div class="card-body p-3">
|
|
|
|
- <div class="media">
|
|
|
|
- <img :src="thread.accounts[0].avatar" width="45" height="45" class="shadow-sm mr-3" style="border-radius: 15px;" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
|
|
|
|
-
|
|
|
|
- <div class="media-body">
|
|
|
|
- <!-- <p class="lead mb-n2">{{ thread.accounts[0].display_name }}</p> -->
|
|
|
|
- <div class="d-flex justify-content-between align-items-start mb-1">
|
|
|
|
- <p class="dm-display-name font-weight-bold mb-0">@{{ thread.accounts[0].acct }}</p>
|
|
|
|
- <p class="font-weight-bold small text-muted mb-0">{{ timeago(thread.last_status.created_at) }} ago</p>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <p class="dm-thread-summary text-muted mr-4" v-html="threadSummary(thread.last_status)"></p>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <router-link class="btn btn-link stretched-link align-self-center mr-n3" :to="`/i/web/direct/thread/${thread.accounts[0].id}`">
|
|
|
|
- <i class="fal fa-chevron-right fa-lg text-lighter"></i>
|
|
|
|
- </router-link>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div v-if="!threads || !threads.length" class="row justify-content-center">
|
|
|
|
- <div class="col-12 text-center">
|
|
|
|
- <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
|
- <p class="lead text-muted font-weight-bold">Your inbox is empty</p>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div v-if="canLoadMore">
|
|
|
|
- <intersect @enter="enterIntersect">
|
|
|
|
- <dm-placeholder />
|
|
|
|
- </intersect>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div v-else>
|
|
|
|
- <dm-placeholder />
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div class="col-md-3 d-md-block order-1 order-md-2 mb-4">
|
|
|
|
- <button class="btn btn-dark shadow-sm font-weight-bold btn-block" @click="openCompose"><i class="far fa-envelope mr-1"></i> Compose</button>
|
|
|
|
- <hr>
|
|
|
|
- <div class="d-flex d-md-block">
|
|
|
|
- <button
|
|
|
|
- v-for="(tab, index) in tabs"
|
|
|
|
- class="btn shadow-sm font-weight-bold btn-block text-capitalize mt-0 mt-md-2 mx-1 mx-md-0"
|
|
|
|
- :class="[ index === tabIndex ? 'btn-primary' : 'btn-light' ]"
|
|
|
|
- @click="toggleTab(index)"
|
|
|
|
- >
|
|
|
|
- {{ $t('directMessages.' + tab) }}
|
|
|
|
- </button>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <drawer />
|
|
|
|
- </div>
|
|
|
|
- <div v-else class="d-flex justify-content-center align-items-center" style="height:calc(100vh - 58px);">
|
|
|
|
- <b-spinner />
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <b-modal
|
|
|
|
- ref="compose"
|
|
|
|
- hide-header
|
|
|
|
- hide-footer
|
|
|
|
- centered
|
|
|
|
- rounded
|
|
|
|
- size="md"
|
|
|
|
- >
|
|
|
|
- <div class="card shadow-none mt-4">
|
|
|
|
- <div class="card-body d-flex align-items-center justify-content-between flex-column" style="min-height: 50vh;">
|
|
|
|
- <h3 class="font-weight-bold">New Direct Message</h3>
|
|
|
|
- <div>
|
|
|
|
- <p class="mb-0 font-weight-bold">Select Recipient</p>
|
|
|
|
- <autocomplete
|
|
|
|
- :search="composeSearch"
|
|
|
|
- :disabled="composeLoading"
|
|
|
|
- placeholder="@dansup"
|
|
|
|
- aria-label="Search usernames"
|
|
|
|
- :get-result-value="getTagResultValue"
|
|
|
|
- @submit="onTagSubmitLocation"
|
|
|
|
- :debounce-time="500"
|
|
|
|
- ref="autocomplete"
|
|
|
|
- >
|
|
|
|
- </autocomplete>
|
|
|
|
- <p class="small text-muted">Search by username, or webfinger (@dansup@pixelfed.social)</p>
|
|
|
|
- <div style="width:300px;"></div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <button class="btn btn-outline-dark rounded-pill font-weight-bold px-5 py-1" @click="closeCompose">Cancel</button>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </b-modal>
|
|
|
|
- </div>
|
|
|
|
|
|
+ <div class="dms-page-component">
|
|
|
|
+ <div v-if="isLoaded" class="container-fluid mt-3">
|
|
|
|
+ <div class="row">
|
|
|
|
+ <div class="col-md-3 d-md-block">
|
|
|
|
+ <sidebar :user="profile" />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="col-md-5 offset-md-1 mb-5 order-2 order-md-1">
|
|
|
|
+ <h1 class="font-weight-bold mb-4">Direct Messages</h1>
|
|
|
|
+ <div v-if="threadsLoaded">
|
|
|
|
+ <div v-for="(thread, idx) in threads" class="card shadow-sm mb-1" style="border-radius:15px;">
|
|
|
|
+ <div class="card-body p-3">
|
|
|
|
+ <div class="media">
|
|
|
|
+ <img :src="thread.accounts[0].avatar" width="45" height="45" class="shadow-sm mr-3" style="border-radius: 15px;" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
|
|
|
|
+
|
|
|
|
+ <div class="media-body">
|
|
|
|
+ <!-- <p class="lead mb-n2">{{ thread.accounts[0].display_name }}</p> -->
|
|
|
|
+ <div class="d-flex justify-content-between align-items-start mb-1">
|
|
|
|
+ <p class="dm-display-name font-weight-bold mb-0">@{{ thread.accounts[0].acct }}</p>
|
|
|
|
+ <p class="font-weight-bold small text-muted mb-0">{{ timeago(thread.last_status.created_at) }} ago</p>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <p class="dm-thread-summary text-muted mr-4" v-html="threadSummary(thread.last_status)"></p>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <router-link class="btn btn-link stretched-link align-self-center mr-n3" :to="`/i/web/direct/thread/${thread.accounts[0].id}`">
|
|
|
|
+ <i class="fal fa-chevron-right fa-lg text-lighter"></i>
|
|
|
|
+ </router-link>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div v-if="!threads || !threads.length" class="row justify-content-center">
|
|
|
|
+ <div class="col-12 text-center">
|
|
|
|
+ <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
|
+ <p class="lead text-muted font-weight-bold">Your inbox is empty</p>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div v-if="canLoadMore">
|
|
|
|
+ <intersect @enter="enterIntersect">
|
|
|
|
+ <dm-placeholder />
|
|
|
|
+ </intersect>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div v-else>
|
|
|
|
+ <dm-placeholder />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="col-md-3 d-md-block order-1 order-md-2 mb-4">
|
|
|
|
+ <button class="btn btn-dark shadow-sm font-weight-bold btn-block" @click="openCompose"><i class="far fa-envelope mr-1"></i> Compose</button>
|
|
|
|
+ <hr>
|
|
|
|
+ <div class="d-flex d-md-block">
|
|
|
|
+ <button
|
|
|
|
+ v-for="(tab, index) in tabs"
|
|
|
|
+ class="btn shadow-sm font-weight-bold btn-block text-capitalize mt-0 mt-md-2 mx-1 mx-md-0"
|
|
|
|
+ :class="[ index === tabIndex ? 'btn-primary' : 'btn-light' ]"
|
|
|
|
+ @click="toggleTab(index)"
|
|
|
|
+ >
|
|
|
|
+ {{ $t('directMessages.' + tab) }}
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <drawer />
|
|
|
|
+ </div>
|
|
|
|
+ <div v-else class="d-flex justify-content-center align-items-center" style="height:calc(100vh - 58px);">
|
|
|
|
+ <b-spinner />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <b-modal
|
|
|
|
+ ref="compose"
|
|
|
|
+ hide-header
|
|
|
|
+ hide-footer
|
|
|
|
+ centered
|
|
|
|
+ rounded
|
|
|
|
+ size="md"
|
|
|
|
+ >
|
|
|
|
+ <div class="card shadow-none mt-4">
|
|
|
|
+ <div class="card-body d-flex align-items-center justify-content-between flex-column" style="min-height: 50vh;">
|
|
|
|
+ <h3 class="font-weight-bold">New Direct Message</h3>
|
|
|
|
+ <div>
|
|
|
|
+ <p class="mb-0 font-weight-bold">Select Recipient</p>
|
|
|
|
+ <autocomplete
|
|
|
|
+ :search="composeSearch"
|
|
|
|
+ :disabled="composeLoading"
|
|
|
|
+ placeholder="@dansup"
|
|
|
|
+ aria-label="Search usernames"
|
|
|
|
+ :get-result-value="getTagResultValue"
|
|
|
|
+ @submit="onTagSubmitLocation"
|
|
|
|
+ :debounce-time="500"
|
|
|
|
+ ref="autocomplete"
|
|
|
|
+ >
|
|
|
|
+ </autocomplete>
|
|
|
|
+ <p class="small text-muted">Search by username, or webfinger (@dansup@pixelfed.social)</p>
|
|
|
|
+ <div style="width:300px;"></div>
|
|
|
|
+ </div>
|
|
|
|
+ <div>
|
|
|
|
+ <button class="btn btn-outline-dark rounded-pill font-weight-bold px-5 py-1" @click="closeCompose">Cancel</button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </b-modal>
|
|
|
|
+ </div>
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script type="text/javascript">
|
|
<script type="text/javascript">
|
|
- import Drawer from './partials/drawer.vue';
|
|
|
|
- import Sidebar from './partials/sidebar.vue';
|
|
|
|
- import Placeholder from './partials/placeholders/DirectMessagePlaceholder.vue';
|
|
|
|
- import Intersect from 'vue-intersect'
|
|
|
|
- import Autocomplete from '@trevoreyre/autocomplete-vue'
|
|
|
|
- import '@trevoreyre/autocomplete-vue/dist/style.css';
|
|
|
|
-
|
|
|
|
- export default {
|
|
|
|
- components: {
|
|
|
|
- "drawer": Drawer,
|
|
|
|
- "sidebar": Sidebar,
|
|
|
|
- "intersect": Intersect,
|
|
|
|
- "dm-placeholder": Placeholder,
|
|
|
|
- "autocomplete": Autocomplete
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- data() {
|
|
|
|
- return {
|
|
|
|
- isLoaded: false,
|
|
|
|
- profile: undefined,
|
|
|
|
- canLoadMore: true,
|
|
|
|
- threadsLoaded: false,
|
|
|
|
- composeLoading: false,
|
|
|
|
- threads: [],
|
|
|
|
- tabIndex: 0,
|
|
|
|
- tabs: [
|
|
|
|
- 'inbox',
|
|
|
|
- 'sent',
|
|
|
|
- 'requests'
|
|
|
|
- ],
|
|
|
|
- page: 1,
|
|
|
|
- ids: [],
|
|
|
|
- isIntersecting: false
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- mounted() {
|
|
|
|
- this.profile = window._sharedData.user;
|
|
|
|
- this.isLoaded = true;
|
|
|
|
- this.fetchThreads();
|
|
|
|
|
|
+ import Drawer from './partials/drawer.vue';
|
|
|
|
+ import Sidebar from './partials/sidebar.vue';
|
|
|
|
+ import Placeholder from './partials/placeholders/DirectMessagePlaceholder.vue';
|
|
|
|
+ import Intersect from 'vue-intersect'
|
|
|
|
+ import Autocomplete from '@trevoreyre/autocomplete-vue'
|
|
|
|
+ import '@trevoreyre/autocomplete-vue/dist/style.css';
|
|
|
|
+ import { parseLinkHeader } from '@web3-storage/parse-link-header';
|
|
|
|
+
|
|
|
|
+ export default {
|
|
|
|
+ components: {
|
|
|
|
+ "drawer": Drawer,
|
|
|
|
+ "sidebar": Sidebar,
|
|
|
|
+ "intersect": Intersect,
|
|
|
|
+ "dm-placeholder": Placeholder,
|
|
|
|
+ "autocomplete": Autocomplete
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ isLoaded: false,
|
|
|
|
+ profile: undefined,
|
|
|
|
+ canLoadMore: true,
|
|
|
|
+ threadsLoaded: false,
|
|
|
|
+ composeLoading: false,
|
|
|
|
+ threads: [],
|
|
|
|
+ tabIndex: 0,
|
|
|
|
+ tabs: [
|
|
|
|
+ 'inbox',
|
|
|
|
+ 'sent',
|
|
|
|
+ 'requests'
|
|
|
|
+ ],
|
|
|
|
+ nextUrl: false,
|
|
|
|
+ ids: [],
|
|
|
|
+ isIntersecting: false
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ mounted() {
|
|
|
|
+ this.profile = window._sharedData.user;
|
|
|
|
+ this.isLoaded = true;
|
|
|
|
+ this.fetchThreads();
|
|
},
|
|
},
|
|
|
|
|
|
methods: {
|
|
methods: {
|
|
- fetchThreads() {
|
|
|
|
- axios.get('/api/v1/conversations', {
|
|
|
|
- params: {
|
|
|
|
- scope: this.tabs[this.tabIndex]
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- .then(res => {
|
|
|
|
- let data = res.data.filter(m => {
|
|
|
|
- return m && m.hasOwnProperty('last_status') && m.last_status;
|
|
|
|
- })
|
|
|
|
- let ids = data.map(dm => dm.accounts[0].id);
|
|
|
|
- this.ids = ids;
|
|
|
|
- this.threads = data;
|
|
|
|
- this.threadsLoaded = true;
|
|
|
|
- this.page++;
|
|
|
|
- });
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- timeago(ts) {
|
|
|
|
- return App.util.format.timeAgo(ts);
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- enterIntersect() {
|
|
|
|
- if(this.isIntersecting) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- this.isIntersecting = true;
|
|
|
|
-
|
|
|
|
- axios.get('/api/v1/conversations', {
|
|
|
|
- params: {
|
|
|
|
- scope: this.tabs[this.tabIndex],
|
|
|
|
- page: this.page
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- .then(res => {
|
|
|
|
- let data = res.data.filter(m => {
|
|
|
|
- return m && m.hasOwnProperty('last_status') && m.last_status;
|
|
|
|
- })
|
|
|
|
- data.forEach(dm => {
|
|
|
|
- if(this.ids.indexOf(dm.accounts[0].id) == -1) {
|
|
|
|
- this.ids.push(dm.accounts[0].id);
|
|
|
|
- this.threads.push(dm);
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- // this.threads.push(...res.data);
|
|
|
|
- if(!res.data.length || res.data.length < 5) {
|
|
|
|
- this.canLoadMore = false;
|
|
|
|
- this.isIntersecting = false;
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- this.page++;
|
|
|
|
- this.isIntersecting = false;
|
|
|
|
- });
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- toggleTab(index) {
|
|
|
|
- event.currentTarget.blur();
|
|
|
|
- this.threadsLoaded = false;
|
|
|
|
- this.page = 1;
|
|
|
|
- this.tabIndex = index;
|
|
|
|
- this.fetchThreads();
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- threadSummary(status, len = 50) {
|
|
|
|
- if(status.pf_type == 'photo') {
|
|
|
|
- let sender = this.profile.id == status.account.id;
|
|
|
|
- let icon = '<div class="' + (sender ? 'text-muted' : 'text-primary') + ' border px-2 py-1 mt-1 rounded" style="font-size:11px;width: fit-content"><i class="far fa-image mr-1"></i> <span>';
|
|
|
|
- icon += sender ? 'Sent a photo' : 'Received a photo';
|
|
|
|
- return icon + '</span></div>';
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(status.pf_type == 'video') {
|
|
|
|
- let sender = this.profile.id == status.account.id;
|
|
|
|
- let icon = '<div class="' + (sender ? 'text-muted' : 'text-primary') + ' border px-2 py-1 mt-1 rounded" style="font-size:11px;width: fit-content"><i class="far fa-video mr-1"></i> <span>';
|
|
|
|
- icon += sender ? 'Sent a video' : 'Received a video';
|
|
|
|
- return icon + '</span></div>';
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let res = '';
|
|
|
|
-
|
|
|
|
- if(this.profile.id == status.account.id) {
|
|
|
|
- res += '<i class="far fa-reply-all fa-flip-both"></i> ';
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let content = status.content;
|
|
|
|
- let text = content.replace(/(<([^>]+)>)/gi, "");
|
|
|
|
-
|
|
|
|
- if(text.length > len) {
|
|
|
|
- return res + text.slice(0, len) + '...';
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return res + text;
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- openCompose() {
|
|
|
|
- this.$refs.compose.show();
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- composeSearch(input) {
|
|
|
|
- if (input.length < 1) { return []; };
|
|
|
|
- let self = this;
|
|
|
|
- let results = [];
|
|
|
|
- return axios.post('/api/direct/lookup', {
|
|
|
|
- q: input
|
|
|
|
- }).then(res => {
|
|
|
|
- return res.data;
|
|
|
|
- });
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- getTagResultValue(result) {
|
|
|
|
- // return '@' + result.name;
|
|
|
|
- return result.local ? '@' + result.name : result.name;
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- onTagSubmitLocation(result) {
|
|
|
|
- //this.$refs.autocomplete.value = '';
|
|
|
|
- this.composeLoading = true;
|
|
|
|
- window.location.href = '/i/web/direct/thread/' + result.id;
|
|
|
|
- return;
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- closeCompose() {
|
|
|
|
- this.$refs.compose.hide();
|
|
|
|
- }
|
|
|
|
|
|
+ fetchThreads() {
|
|
|
|
+ axios.get('/api/v1/conversations', {
|
|
|
|
+ params: {
|
|
|
|
+ scope: this.tabs[this.tabIndex]
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ .then(res => {
|
|
|
|
+ let data = res.data.filter(m => {
|
|
|
|
+ return m && m.hasOwnProperty('last_status') && m.last_status;
|
|
|
|
+ })
|
|
|
|
+ if(res.headers && res.headers.link) {
|
|
|
|
+ const links = parseLinkHeader(res.headers.link);
|
|
|
|
+ if(links.next && links.next.url) {
|
|
|
|
+ this.nextUrl = links.next.url
|
|
|
|
+ } else {
|
|
|
|
+ this.nextUrl = false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ let ids = data.map(dm => dm.accounts[0].id);
|
|
|
|
+ this.ids = ids;
|
|
|
|
+ this.threads = data;
|
|
|
|
+ this.threadsLoaded = true;
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ timeago(ts) {
|
|
|
|
+ return App.util.format.timeAgo(ts);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ enterIntersect() {
|
|
|
|
+ if(this.isIntersecting || !this.nextUrl) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.isIntersecting = true;
|
|
|
|
+
|
|
|
|
+ axios.get(this.nextUrl)
|
|
|
|
+ .then(res => {
|
|
|
|
+ if(res.headers && res.headers.link) {
|
|
|
|
+ const links = parseLinkHeader(res.headers.link);
|
|
|
|
+ if(links.next && links.next.url) {
|
|
|
|
+ this.nextUrl = links.next.url
|
|
|
|
+ } else {
|
|
|
|
+ this.nextUrl = false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ let data = res.data.filter(m => {
|
|
|
|
+ return m && m.hasOwnProperty('last_status') && m.last_status;
|
|
|
|
+ })
|
|
|
|
+ data.forEach(dm => {
|
|
|
|
+ if(this.ids.indexOf(dm.accounts[0].id) == -1) {
|
|
|
|
+ this.ids.push(dm.accounts[0].id);
|
|
|
|
+ this.threads.push(dm);
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ // this.threads.push(...res.data);
|
|
|
|
+ if(!res.data.length || res.data.length < 5) {
|
|
|
|
+ this.canLoadMore = false;
|
|
|
|
+ this.isIntersecting = false;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ this.isIntersecting = false;
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ toggleTab(index) {
|
|
|
|
+ event.currentTarget.blur();
|
|
|
|
+ this.threadsLoaded = false;
|
|
|
|
+ this.nextUrl = false;
|
|
|
|
+ this.tabIndex = index;
|
|
|
|
+ this.fetchThreads();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ threadSummary(status, len = 50) {
|
|
|
|
+ if(status.pf_type == 'photo') {
|
|
|
|
+ let sender = this.profile.id == status.account.id;
|
|
|
|
+ let icon = '<div class="' + (sender ? 'text-muted' : 'text-primary') + ' border px-2 py-1 mt-1 rounded" style="font-size:11px;width: fit-content"><i class="far fa-image mr-1"></i> <span>';
|
|
|
|
+ icon += sender ? 'Sent a photo' : 'Received a photo';
|
|
|
|
+ return icon + '</span></div>';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(status.pf_type == 'video') {
|
|
|
|
+ let sender = this.profile.id == status.account.id;
|
|
|
|
+ let icon = '<div class="' + (sender ? 'text-muted' : 'text-primary') + ' border px-2 py-1 mt-1 rounded" style="font-size:11px;width: fit-content"><i class="far fa-video mr-1"></i> <span>';
|
|
|
|
+ icon += sender ? 'Sent a video' : 'Received a video';
|
|
|
|
+ return icon + '</span></div>';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let res = '';
|
|
|
|
+
|
|
|
|
+ if(this.profile.id == status.account.id) {
|
|
|
|
+ res += '<i class="far fa-reply-all fa-flip-both"></i> ';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let content = status.content;
|
|
|
|
+ let text = content.replace(/(<([^>]+)>)/gi, "");
|
|
|
|
+
|
|
|
|
+ if(text.length > len) {
|
|
|
|
+ return res + text.slice(0, len) + '...';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return res + text;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ openCompose() {
|
|
|
|
+ this.$refs.compose.show();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ composeSearch(input) {
|
|
|
|
+ if (input.length < 1) { return []; };
|
|
|
|
+ let self = this;
|
|
|
|
+ let results = [];
|
|
|
|
+ return axios.post('/api/direct/lookup', {
|
|
|
|
+ q: input
|
|
|
|
+ }).then(res => {
|
|
|
|
+ return res.data;
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ getTagResultValue(result) {
|
|
|
|
+ // return '@' + result.name;
|
|
|
|
+ return result.local ? '@' + result.name : result.name;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ onTagSubmitLocation(result) {
|
|
|
|
+ //this.$refs.autocomplete.value = '';
|
|
|
|
+ this.composeLoading = true;
|
|
|
|
+ window.location.href = '/i/web/direct/thread/' + result.id;
|
|
|
|
+ return;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ closeCompose() {
|
|
|
|
+ this.$refs.compose.hide();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
+ }
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
- .dms-page-component {
|
|
|
|
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
|
|
-
|
|
|
|
- .dm {
|
|
|
|
- &-thread-summary {
|
|
|
|
- margin-bottom: 0;
|
|
|
|
- font-size: 12px;
|
|
|
|
- line-height: 12px;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- &-display-name {
|
|
|
|
- font-size: 16px;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ .dms-page-component {
|
|
|
|
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
|
|
+
|
|
|
|
+ .dm {
|
|
|
|
+ &-thread-summary {
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
+ font-size: 12px;
|
|
|
|
+ line-height: 12px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &-display-name {
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
</style>
|
|
</style>
|