Merge branch 'master' into matrix-chat-native
This commit is contained in:
		
						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
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										28
									
								
								src/lib/NotificationHandler.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/lib/NotificationHandler.js
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
		Reference in New Issue
	
	Block a user