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