Mastodon: websocket token and card

master
May B. 2019-05-27 14:31:43 +02:00
parent 74935ced6c
commit a97ffb491b
5 changed files with 93 additions and 39 deletions

View File

@ -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",

View File

@ -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<Options>>(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)
}
}
})
}
}
</script>
@ -191,6 +195,12 @@ export default class Client extends Mixins<ServiceClient<Options>>(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

View File

@ -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)
})
}

View File

@ -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 {

View File

@ -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 {