Merge branch 'master' into matrix-chat-native

matrix-chat-native
adb 4 years ago
commit 550d79af28

@ -1,36 +1,30 @@
<template> <template>
<img v-if="mxcURL" :src="thumbnailUrl()" class="userThumbnail image"/> <img v-if="mxcURL" :src="getAvatarUrl(mxcURL)" class="userThumbnail image"/>
<div v-else v-html="getJdenticon()" class="userThumbnail identicon"/> <div v-else v-html="getJdenticon()" class="userThumbnail identicon"/>
</template> </template>
<script> <script>
import parseMXC from '@modular-matrix/parse-mxc';
import {matrix} from "@/main";
import {toSvg} from 'jdenticon'; import {toSvg} from 'jdenticon';
import {getAvatarUrl} from '@/lib/getMxc';
export default { export default {
name: "userThumbnail.vue", name: 'userThumbnail.vue',
components: { components: {
}, },
props: { props: {
mxcURL: String, mxcURL: String,
username: String, username: String,
fallback: String, fallback: String,
homeserver: String,
size: Number size: Number
}, },
methods: { methods: {
thumbnailUrl(){
let mxc = parseMXC.parse(this.mxcURL);
return `${this.homeserver||matrix.baseUrl}/_matrix/media/v1/thumbnail/${
mxc.homeserver}/${mxc.id}?width=${this.imageSize}&height=${this.imageSize}&method=${this.resizeMethod}`;
},
getFontSize(){ getFontSize(){
return window.getComputedStyle(document.body,null).fontSize.split("px", 1)||16; return window.getComputedStyle(document.body,null).fontSize.split('px', 1)||16;
}, },
getJdenticon(){ getJdenticon(){
return toSvg(this.fallback, this.getFontSize()*this.size); return toSvg(this.fallback, this.getFontSize()*this.size);
} },
getAvatarUrl
}, },
data(){ data(){
return { return {

@ -7,7 +7,7 @@
<form v-on:submit.prevent="sendMessage()"> <form v-on:submit.prevent="sendMessage()">
<textarea <textarea
@keyup.enter.exact="sendMessage()" @keyup.enter.exact="sendMessage()"
@input="resizeMessageBanner()" @input="resizeMessageBanner(); sendTyping(2000);"
v-model="event.content.body" v-model="event.content.body"
ref="newMessageInput" class="newMessageInput" ref="newMessageInput" class="newMessageInput"
rows="1" placeholder="type a message ..." rows="1" placeholder="type a message ..."
@ -50,6 +50,11 @@ export default {
id.style.height = '1.25rem'; id.style.height = '1.25rem';
this.onResize(id.parentElement.clientHeight); this.onResize(id.parentElement.clientHeight);
}, },
sendTyping(timeout){
if (this.waitForSendTyping) return;
matrix.client.sendTyping(this.roomId, true, timeout+100);
setTimeout(()=>this.waitForSendTyping=false, timeout);
},
resizeMessageBanner(){ resizeMessageBanner(){
let id = this.$refs.newMessageInput; let id = this.$refs.newMessageInput;
id.style.height = '1.25rem'; id.style.height = '1.25rem';
@ -82,7 +87,8 @@ export default {
} }
} }
}, },
showEmojiPicker: false showEmojiPicker: false,
waitForSendTyping: false
} }
} }
} }

@ -0,0 +1,28 @@
import {getMxcFromUserId, getAvatarUrl} from '@/lib/getMxc';
import {calcUserName} from '@/lib/matrixUtils';
import {getRoom} from '@/lib/matrixUtils';
import {router} from '@/router';
export class NotificationHandler{
constructor() {
this.activateNotification();
}
async activateNotification(){
if (!window.Notification){
console.log('notifications are unsupported')
return false;
}
if (Notification.permission === 'granted') return true;
return await Notification.requestPermission()
.then(permission => {return permission === 'granted'});
}
showNotification(event){
if (Notification.permission !== 'granted') return false;
console.log(event);
let mxc = getMxcFromUserId(event.sender);
new Notification(`${calcUserName(event.sender)} in ${getRoom(event.room_id).name}`, {
body: event.content.body,
icon: mxc?getAvatarUrl(mxc):undefined
}).onclick = ()=>router.push(`/rooms/${event.room_id}`);
}
}

@ -1,5 +1,6 @@
import sdk from 'matrix-js-sdk' import sdk from 'matrix-js-sdk'
import {matrix} from "@/main"; import {matrix} from '@/main';
import parseMXC from '@modular-matrix/parse-mxc';
export function getMxcFromUser(user){ export function getMxcFromUser(user){
return user.avatarUrl; return user.avatarUrl;
@ -10,10 +11,16 @@ export function getMxcFromUserId(userId){
} }
export function getMxcFromRoom(room){ export function getMxcFromRoom(room){
let avatarState = room.getLiveTimeline().getState(sdk.EventTimeline.FORWARDS).getStateEvents("m.room.avatar"); let avatarState = room.getLiveTimeline().getState(sdk.EventTimeline.FORWARDS).getStateEvents('m.room.avatar');
return avatarState.length>0?avatarState[avatarState.length-1].getContent().url:undefined; return avatarState.length>0?avatarState[avatarState.length-1].getContent().url:undefined;
} }
export function getMxcFromRoomId(roomId){ export function getMxcFromRoomId(roomId){
return getMxcFromRoom(matrix.client.getRoom(roomId)); return getMxcFromRoom(matrix.client.getRoom(roomId));
} }
export function getAvatarUrl(mxcUrl, size = 64, resizeMethod = 'crop'){
let mxc = parseMXC.parse(mxcUrl);
return `${matrix.baseUrl}/_matrix/media/v1/thumbnail/${
mxc.homeserver}/${mxc.id}?width=${size}&height=${size}&method=${resizeMethod}`;
}

@ -1,4 +1,5 @@
import matrix from 'matrix-js-sdk'; import matrix from 'matrix-js-sdk';
import {NotificationHandler} from "@/lib/NotificationHandler";
export class MatrixHandler { export class MatrixHandler {
constructor(clientDisplayName = 'matrix-chat') { constructor(clientDisplayName = 'matrix-chat') {
@ -9,6 +10,7 @@ export class MatrixHandler {
this.loading = undefined; this.loading = undefined;
this.user = undefined; this.user = undefined;
this.baseUrl = undefined; this.baseUrl = undefined;
this.notify = new NotificationHandler();
} }
login(user, password, baseUrl, onError, callback = ()=>{}){ login(user, password, baseUrl, onError, callback = ()=>{}){
if (this.client){ console.log('there is already an active session'); return; } if (this.client){ console.log('there is already an active session'); return; }
@ -57,15 +59,22 @@ export class MatrixHandler {
await this.client.stopClient(); await this.client.stopClient();
this.client = undefined; this.client = undefined;
} }
startSync(callback = ()=>{}){ async startSync(callback = ()=>{}){
this.loading = true; this.loading = true;
this.client.startClient(); await this.client.startClient();
this.client.once('sync', (state) => { this.client.once('sync', (state) => {
console.log(state); console.log(state);
this.rooms = this.client.getRooms(); this.rooms = this.client.getRooms();
console.log(this.rooms)
this.loading = false; this.loading = false;
callback(); callback();
this.listenToPushEvents()
});
}
listenToPushEvents(){
this.client.on('event', event => {
if (this.client.getPushActionsForEvent(event).notify){
this.notify.showNotification(event.event);
}
}); });
} }
async sendEvent({content, type}, roomId, replyTo = undefined){ async sendEvent({content, type}, roomId, replyTo = undefined){

@ -1,7 +1,7 @@
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import App from './App.vue' import App from './App.vue'
import {router} from './router.js' import {router} from '@/router'
import {MatrixHandler} from './lib/matrixHandler.js' import {MatrixHandler} from './lib/matrixHandler.js'
import {cookieHandler} from './lib/cookieHandler.js'; import {cookieHandler} from './lib/cookieHandler.js';

@ -1,40 +0,0 @@
import VueRouter from 'vue-router';
import login from '@/views/login';
import chat from '@/views/chat';
import rooms from '@/views/rooms';
import admin from '@/views/admin';
export const router = new VueRouter({
routes: [
{
path: '/',
name: 'home',
component: login
},
{
path: '/login',
name: 'login',
component: login
},
{
path: '/chat/*',
name: 'chat',
component: chat
},
{
path: '/rooms/*',
name: 'room',
component: rooms
},
{
path: '/rooms',
name: 'rooms',
component: rooms
},
{
path: '/admin',
name: 'admin',
component: admin
}
]
})

@ -0,0 +1,40 @@
import VueRouter from 'vue-router';
import login from '@/views/login';
import chat from '@/views/chat';
import rooms from '@/views/rooms';
import admin from '@/views/admin';
export const router = new VueRouter({
routes: [
{
path: '/',
name: 'home',
component: login
},
{
path: '/login',
name: 'login',
component: login
},
{
path: '/chat/*',
name: 'chat',
component: chat
},
{
path: '/rooms/*',
name: 'room',
component: rooms
},
{
path: '/rooms',
name: 'rooms',
component: rooms
},
{
path: '/admin',
name: 'admin',
component: admin
}
]
})

@ -45,8 +45,8 @@ export default {
}, },
methods:{ methods:{
onScroll(){ onScroll(){
if (this.$refs.timelineContainer.scrollTop === 0) this.loadEvents(); if (this.$refs.timelineContainer.scrollTop < 400 && this.loadingStatus !== 'loading') this.loadEvents();
this.showScrollBtn = this.scroll.getScrollBottom() > 400; this.showScrollBtn = this.scroll.getScrollBottom() > 500;
}, },
resize(height = this.$refs.newMessage.clientHeight){ resize(height = this.$refs.newMessage.clientHeight){
this.$refs.chatContainer.style.height = `calc(100% - ${height}px - 3.5rem)`; this.$refs.chatContainer.style.height = `calc(100% - ${height}px - 3.5rem)`;
@ -56,10 +56,10 @@ export default {
}, },
async loadEvents(){ async loadEvents(){
let scrollBottom = this.scroll.getScrollBottom(); let scrollBottom = this.scroll.getScrollBottom();
this.loadingStatus = 'loading ...'; this.loadingStatus = 'loading';
await matrix.client.paginateEventTimeline(this.room.getLiveTimeline(), {backwards: true}) await matrix.client.scrollback(this.room, 30);
.then(state => this.loadingStatus = state?'load more':false); this.loadingStatus = 'load more';
if (this.loadingStatus) this.scroll.setScrollBottom(scrollBottom); this.scroll.setScrollBottom(scrollBottom)
}, },
getUser(userId){ getUser(userId){
return matrix.client.getUser(userId); return matrix.client.getUser(userId);
@ -76,11 +76,12 @@ export default {
} }
}, },
updated(){ updated(){
if(this.scroll.getScrollBottom() < 350) this.scroll.scrollToBottom(); if(this.scroll.getScrollBottom() < 400 && this.loadingStatus !== 'loading') this.scroll.scrollToBottom();
}, },
mounted(){ mounted(){
this.scroll = new scrollHandler(this.$refs.timelineContainer); this.scroll = new scrollHandler(this.$refs.timelineContainer);
this.scroll.scrollToBottom(); this.scroll.scrollToBottom();
this.onScroll();
}, },
watch: { watch: {
'$route'(){ '$route'(){

Loading…
Cancel
Save