From a97ffb491b81d2a04d429f8e0a2bed1b29ecaada Mon Sep 17 00:00:00 2001 From: shu Date: Mon, 27 May 2019 14:31:43 +0200 Subject: [PATCH] Mastodon: websocket token and card --- package.json | 12 +++--- src/services/mastodon/Client.vue | 68 +++++++++++++++++------------- src/services/mastodon/Mastodon.vue | 5 ++- src/services/mastodon/Status.vue | 16 ++++++- src/services/mastodon/Types.ts | 31 +++++++++++++- 5 files changed, 93 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index 03c3aed..ca92211 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,6 @@ "@vue/cli-plugin-typescript": "^3.0.3", "@vue/cli-service": "^3.0.3", "babel-plugin-transform-decorators": "^6.24.1", - "pug": "^2.0.3", - "pug-plain-loader": "^1.0.0", - "sass-loader": "^7.0.1", - "tslint-config-prettier": "^1.15.0", - "tslint-plugin-prettier": "^1.3.0", - "typescript": "^3.0.0", "core-js": "^2.6.5", "css-loader": "^0.28.11", "eslint": "^5.16.0", @@ -37,6 +31,12 @@ "less-loader": "^4.1.0", "node-sass": "^4.11.0", "optimize-css-assets-webpack-plugin": "^4.0.0", + "pug": "^2.0.3", + "pug-plain-loader": "^1.0.0", + "sass-loader": "^7.0.1", + "tslint-config-prettier": "^1.15.0", + "tslint-plugin-prettier": "^1.3.0", + "typescript": "^3.0.0", "url-loader": "^1.0.1", "vue-loader": "^15.4.2", "vue-style-loader": "^4.1.2", diff --git a/src/services/mastodon/Client.vue b/src/services/mastodon/Client.vue index 9bbac32..8a51ef8 100644 --- a/src/services/mastodon/Client.vue +++ b/src/services/mastodon/Client.vue @@ -24,7 +24,7 @@ import ServiceClient from '@/components/ServiceClient' import Lists from '@/helpers/lists/Lists' import AxiosLodable from '@/helpers/loadable/AxiosLoadable' import AxiosLodableMore from '@/helpers/loadable/AxiosLoadableMore' -import { AUTH, getRest } from './Mastodon.vue' +import { AUTH, getHeaders, getRest } from './Mastodon.vue' import Notification from './Notification.vue' import Status from './Status.vue' import { MarkMessage, Notification as INotification, Options, Status as IStatus } from './Types' @@ -98,36 +98,40 @@ export default class Client extends Mixins>(ServiceClient } setupStream() { - const ws = new WebSocket( - `wss://${this.auth.get(AUTH.SERVER)}/api/v1/streaming?access_token=${this.auth.get(AUTH.TOKEN)}&stream=user` - ) - ws.onmessage = event => { - const data = JSON.parse(event.data) - const payload = JSON.parse(data.payload) - switch (data.event) { - case 'update': - this.statues.with(s => s.unshift(payload)) - break - - case 'notification': - this.notifications.with(n => n.unshift(payload)) - break - - case 'delete': - this.statues.with(st => Lists.removeFirstBy(st, s => s.id, payload.id)) - break - } - } - ws.onerror = ev => this.emitError(ev.type) - ws.onclose = () => { - this.emitError( - 'Mastodon stream disconnected !' + - (this.options.reconnect ? ' Reconnecting...' : '') + this.get('/instance').then(res => { + const oldAuth = res.data.version < '2.8.4' ? `access_token=${this.auth.get(AUTH.TOKEN)}&` : '' + const ws = new WebSocket( + `wss://${this.auth.get(AUTH.SERVER)}/api/v1/streaming?${oldAuth}stream=user`, + this.auth.get(AUTH.TOKEN) ) - if (this.options.reconnect) { - setTimeout(() => this.setupStream(), this.options.timeout) + ws.onmessage = event => { + const data = JSON.parse(event.data) + const payload = JSON.parse(data.payload) + switch (data.event) { + case 'update': + this.statues.with(s => s.unshift(payload)) + break + + case 'notification': + this.notifications.with(n => n.unshift(payload)) + break + + case 'delete': + this.statues.with(st => Lists.removeFirstBy(st, s => s.id, payload.id)) + break + } } - } + ws.onerror = ev => this.emitError(ev.type) + ws.onclose = () => { + this.emitError( + 'Mastodon stream disconnected !' + + (this.options.reconnect ? ' Reconnecting...' : '') + ) + if (this.options.reconnect) { + setTimeout(() => this.setupStream(), this.options.timeout) + } + } + }) } } @@ -191,6 +195,12 @@ export default class Client extends Mixins>(ServiceClient background-color: #00000044 color: white padding: .5em + .card + @include tile + padding: .2em + display: block + .provider + float: right .meta margin-left: 1em + $avatarSize font-size: .8em diff --git a/src/services/mastodon/Mastodon.vue b/src/services/mastodon/Mastodon.vue index ff40822..374a9bc 100644 --- a/src/services/mastodon/Mastodon.vue +++ b/src/services/mastodon/Mastodon.vue @@ -38,10 +38,13 @@ import { ParseEmojisMixin } from './ParseEmojisMixin' import { Account, Options } from './Types' export const AUTH = { SERVER: 'server', TOKEN: 'token' } +export function getHeaders(auth: Auth) { + return { headers: { Authorization: 'Bearer ' + auth.get(AUTH.TOKEN) } } +} export function getRest(auth: Auth, timeout: number) { return axios.create({ baseURL: `https://${auth.get(AUTH.SERVER)}/api/v1/`, timeout, - headers: { Authorization: 'Bearer ' + auth.get(AUTH.TOKEN) }, + ...getHeaders(auth) }) } diff --git a/src/services/mastodon/Status.vue b/src/services/mastodon/Status.vue index dbb1e93..d991986 100644 --- a/src/services/mastodon/Status.vue +++ b/src/services/mastodon/Status.vue @@ -17,7 +17,15 @@ img(v-if="media.type == 'image' || media.type == 'gifv'" :src="media.preview_url" :alt="media.description" :title="media.description") template(v-else) Wrong type .gif(v-if="media.type == 'gifv'") GIF - template(v-else) Hidden media + template(v-else) Hidden media {{ media.description }} + .poll(v-if="status.poll") {{ renderPoll(status.poll) }} + a.card(v-if="status.card" :href="status.card.url" target="_blank") + a.provider(v-if="status.card.provider_name" :src="status.card.provider_url" target="_blank") {{ status.card.provider_name }} + .title {{ status.card.title }} + .descr {{ status.card.description }} + template(v-if="status.card.image") + img(v-if="showMedia" :src="status.card.image") + a(v-else-if="status.card.type == 'photo'" :src="status.card.image" target="_blank") Hidden media status.reblog(v-else :status="status.reblog" :showMedia="showMedia") .meta(v-if="!status.reblog") @@ -41,7 +49,7 @@ import FromNowMixin from '@/components/FromNowMixin' import ShowMediaMixin from '@/components/ShowMediaMixin' import Account from './Account.vue' import { ParseEmojisMixin } from './ParseEmojisMixin' -import { MarkMessage, Status as IStatus } from './Types' +import { Card, MarkMessage, Poll, Status as IStatus } from './Types' @Component({ components: { Account } }) export default class Status extends Mixins(ParseEmojisMixin, ShowMediaMixin, FromNowMixin) { @@ -60,6 +68,10 @@ export default class Status extends Mixins(ParseEmojisMixin, ShowMediaMixin, Fro throw status.id // TODO: } + renderPoll(poll: Poll) { + throw poll // TODO: + } + @Emit('mark') emitMark(status: IStatus, action: 'reblog' | 'favourite', callback: CallableFunction, undo = false): MarkMessage { return { diff --git a/src/services/mastodon/Types.ts b/src/services/mastodon/Types.ts index 67dec37..00bdbda 100644 --- a/src/services/mastodon/Types.ts +++ b/src/services/mastodon/Types.ts @@ -34,7 +34,36 @@ export interface Status { replies_count: number in_reply_to_id?: number reblog?: Status - spoiler_text?: string + spoiler_text?: string, + card?: Card, + poll?: Poll +} + +export type CardType = 'link' | 'photo' | 'video' | 'rich' +export interface Card { + url: string + title: string + description: string + image?: string + type: CardType + author_name?: string + author_url?: string + provider_name?: string + provider_url?: string +} + +export interface PollOption { + title: string + votes_count?: number +} +export interface Poll { + id: string + expires_at?: string + expired: boolean + multiple: boolean + votes_count: number + options: PollOption[] + voted?: boolean } export interface Media {