RefreshToken support and matrix fixes

Also, some initial TypeScript.
This commit is contained in:
N-Pex 2025-05-22 10:22:19 +02:00
parent f8f6c2ed94
commit a6d932ddb5
8 changed files with 169 additions and 120 deletions

View file

@ -90,17 +90,17 @@
<interactive-auth ref="interactiveAuth" />
<v-btn id="btn-join" class="btn-dark" :disabled="!acceptUA || (room && room.selfMembership == 'ban')" size="large"
<v-btn id="btn-join" class="btn-dark" :disabled="!acceptUA || (room && room.getMyMembership() == 'ban')" size="large"
@click.stop="handleJoin" :loading="loading" v-if="!currentUser">{{
roomId && roomId.startsWith("@") ? $t("join.enter_room_user") : $t("join.enter_room")
}}</v-btn>
<v-btn id="btn-join" class="btn-dark" :disabled="!acceptUA || (room && room.selfMembership == 'ban')" size="large"
<v-btn id="btn-join" class="btn-dark" :disabled="!acceptUA || (room && room.getMyMembership() == 'ban')" size="large"
block @click.stop="handleJoin" :loading="loading" v-else>{{
roomId && roomId.startsWith("@") ? $t("join.join_user") : $t("join.join")
}}</v-btn>
<div v-if="loadingMessage" class="text-center">{{ loadingMessage }}</div>
<div v-if="room && room.selfMembership == 'ban'" class="text-center">{{ $t("join.you_have_been_banned") }}</div>
<div v-if="room && room.getMyMembership() == 'ban'" class="text-center">{{ $t("join.you_have_been_banned") }}</div>
</div>
</div>

View file

@ -1,23 +0,0 @@
export default class User {
constructor(user_id, password, is_guest) {
this.user_id = user_id;
this.password = password;
this.is_guest = is_guest || false
}
static localPart(user_id) {
if (user_id && user_id.startsWith('@') && user_id.includes(':')) {
const parts = user_id.split(":");
return parts[0].substring(1);
}
return user_id;
}
static domainPart(user_id) {
if (user_id && user_id.startsWith('@') && user_id.includes(':')) {
const parts = user_id.split(":");
return parts[1];
}
return undefined;
}
}

27
src/models/user.ts Normal file
View file

@ -0,0 +1,27 @@
export default class User {
user_id: string;
password: string | null;
is_guest: boolean;
constructor(user_id: string, password: string | null, is_guest?: boolean) {
this.user_id = user_id;
this.password = password;
this.is_guest = is_guest || false;
}
static localPart(user_id: string | null) {
if (user_id && user_id.startsWith("@") && user_id.includes(":")) {
const parts = user_id.split(":");
return parts[0].substring(1);
}
return user_id;
}
static domainPart(user_id: string | null) {
if (user_id && user_id.startsWith("@") && user_id.includes(":")) {
const parts = user_id.split(":");
return parts[1];
}
return undefined;
}
}

View file

@ -1,9 +1,23 @@
import cleaninsights from "./cleaninsights.service";
import matomo from "./matomo.service";
export interface AnalyticsEngine {
event(category: string, action: string);
}
type AnalyticsEvent = {
category: string;
action: string;
}
export default {
install(app) {
class AnalyticsServiceClass {
engines: AnalyticsEngine[];
cachedEvents: AnalyticsEvent[];
initialized: boolean;
constructor() {
this.engines = [];
this.cachedEvents = [];
@ -37,7 +51,7 @@ export default {
this.initialized = true;
// Handle cached events
this.cachedEvents.forEach(([category, action]) => {
this.cachedEvents.forEach(({category, action}) => {
this.event(category, action);
});
this.cachedEvents = [];
@ -46,7 +60,7 @@ export default {
event(category, action) {
if (!this.initialized) {
this.cachedEvents.push([category, action]);
this.cachedEvents.push({category, action});
return;
}

View file

@ -85,19 +85,19 @@ export default {
joinedRooms() {
return this.rooms.filter((room) => {
return room.selfMembership === "join";
return room.getMyMembership() === "join";
});
},
invites() {
return this.rooms.filter((room) => {
return room.selfMembership === "invite";
return room.getMyMembership() === "invite";
});
},
joinedAndInvitedRooms() {
return this.rooms.filter((room) => {
return room.selfMembership === "join" || room.selfMembership === "invite";
return room.getMyMembership() === "join" || room.getMyMembership() === "invite";
});
},
},
@ -313,6 +313,8 @@ export default {
store: matrixStore,
deviceId: user.device_id,
accessToken: user.access_token,
refreshToken: user.refresh_token,
tokenRefreshFunction: this.onSessionRefresh,
timelineSupport: true,
unstableClientRelationAggregation: true,
cryptoStore: cryptoStore
@ -413,7 +415,7 @@ export default {
case "m.room.join_rules":
{
const room = this.matrixClient.getRoom(event.getRoomId());
if (room && room.getJoinRule() == "private" && room.selfMembership == "invite") {
if (room && room.getJoinRule() == "private" && room.getMyMembership() == "invite") {
// We have an invite to a room that's now "private"? This is most probably a deleted DM room.
// Reject the invite, i.e. call "leave" on it.
this.matrixClient.leave(room.roomId);
@ -451,7 +453,7 @@ export default {
},
onRoom(room) {
if (room.selfMembership === "invite") {
if (room.getMyMembership() === "invite") {
this.matrixClient
.getRoomTags(room.roomId)
.then((reply) => {
@ -468,7 +470,7 @@ export default {
},
onRoomMyMembership(room) {
if (room.selfMembership === "invite") {
if (room.getMyMembership() === "invite") {
// Invitation. Need to call "recalculate" to pick
// up room name, not sure why exactly.
room.recalculate();
@ -476,6 +478,28 @@ export default {
this.reloadRooms();
},
onSessionRefresh(refreshToken) {
const now = Date.now();
return this.matrixClient.refreshToken(refreshToken).then((result) => {
// Store new one!
var user = this.$store.state.auth.user;
user.access_token = result.access_token;
user.refresh_token = result.refresh_token;
user.expires_in_ms = result.expires_in_ms;
this.$store.commit("setUser", user);
// Return AccessTokens struct
let accesssTokens = {
accessToken: result.access_token,
refreshToken: result.refresh_token
};
if (result.expires_in_ms) {
accesssTokens.expiry = new Date(now + result.expires_in_ms);
};
return accesssTokens;
});
},
onSessionLoggedOut() {
console.log("Logged out!");
if (this.matrixClient) {
@ -517,7 +541,7 @@ export default {
// each time!
var updatedRooms = this.matrixClient.getVisibleRooms();
updatedRooms = updatedRooms.filter((room) => {
return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join") && room.currentState.getStateEvents(STATE_EVENT_ROOM_DELETED).length == 0;
return room.getMyMembership() && (room.getMyMembership() == "invite" || room.getMyMembership() == "join") && room.currentState.getStateEvents(STATE_EVENT_ROOM_DELETED).length == 0;
});
updatedRooms.forEach((room) => {
room["avatar"] = room.getAvatarUrl(this.matrixClient.getHomeserverUrl(), 80, 80, "scale", true, this.useAuthedMedia);
@ -567,7 +591,7 @@ export default {
var ids = {};
const ret = [];
for (const room of this.rooms) {
if (room.selfMembership == "join" && this.getRoomJoinRule(room) == "invite") {
if (room.getMyMembership() == "join" && this.getRoomJoinRule(room) == "invite") {
for (const member of room.getJoinedMembers()) {
if (member.userId != this.currentUserId && !ids[member.userId]) {
ids[member.userId] = member;
@ -842,7 +866,7 @@ export default {
})
.then(() => {
//console.log("Purge: create timeline");
return timelineWindow.load(null, 100);
return timelineWindow.load(undefined, 100);
})
.then(() => {
const getMoreIfAvailable = function _getMoreIfAvailable() {
@ -1234,7 +1258,7 @@ export default {
since: response.next_batch,
})
.then((response) => {
return _findOrGetMore(client, response);
return _findOrGetMore(client, useAuthedMedia, response);
})
.catch((err) => {
return Promise.reject("Failed to find room: " + err);

View file

@ -1,82 +0,0 @@
export default {
install(app, router) {
var routes = [];
var nextRoutes = null;
var zeroIndex = undefined;
router.beforeResolve((to, ignoredfrom, next) => {
if (!zeroIndex) {
routes = [to];
zeroIndex = window.history.length;
}
next();
})
router.beforeEach((to, from, next) => {
if (nextRoutes) {
console.log("Nav: next routes set, going:", routes, nextRoutes);
routes = nextRoutes;
nextRoutes = null;
if (routes.length > 0) {
console.log("Redirecting to", routes[routes.length - 1]);
next(routes[routes.length - 1]);
return;
}
}
next();
})
const navigationService = {
/***
* @param mode Mode of operation. -1 = push as root, 0 = replace, 1 = normal push
*/
push(route, mode) {
if (mode === undefined) {
mode = 1;
}
if (mode == -1) {
nextRoutes = [route];
} else if (mode == 0) {
// Replace
nextRoutes = [...routes];
nextRoutes.pop();
nextRoutes.push(route);
} else {
nextRoutes = [...routes];
nextRoutes.push(route);
}
const index = nextRoutes.length - routes.length;
const targetIndex = nextRoutes.length - 1;
console.log("Nav - index " + index + " Target " + targetIndex);
if (index < 0) {
console.log("Nav - go " + index);
router.go(index);
} else if (index == 0) {
console.log("Nav - replace");
routes = nextRoutes;
nextRoutes = null;
router.replace(route).catch((ignoredErr) => {});
} else {
console.log("Nav - push");
router.push(route).catch((ignoredErr) => {});
}
},
canPop() {
if (nextRoutes) {
return nextRoutes.length > 1;
}
return routes.length > 1;
},
pop() {
routes.pop();
router.go(-1);
}
}
app.$navigation = navigationService;
app.config.globalProperties.$navigation = navigationService;
}
}

View file

@ -0,0 +1,84 @@
import { RouteLocationNormalizedGeneric, Router } from "vue-router";
export default {
install(app, router: Router) {
var routes: RouteLocationNormalizedGeneric[] = [];
var nextRoutes: RouteLocationNormalizedGeneric[] | null = null;
var zeroIndex: number | undefined = undefined;
router.beforeResolve((to, ignoredfrom, next) => {
if (!zeroIndex) {
routes = [to];
zeroIndex = window.history.length;
}
next();
});
router.beforeEach((to, from, next) => {
if (nextRoutes) {
console.log("Nav: next routes set, going:", routes, nextRoutes);
routes = nextRoutes;
nextRoutes = null;
if (routes.length > 0) {
console.log("Redirecting to", routes[routes.length - 1]);
next(routes[routes.length - 1]);
return;
}
}
next();
});
const navigationService = {
/***
* @param mode Mode of operation. -1 = push as root, 0 = replace, 1 = normal push
*/
push(route, mode) {
if (mode === undefined) {
mode = 1;
}
if (mode == -1) {
nextRoutes = [route];
} else if (mode == 0) {
// Replace
nextRoutes = [...routes];
nextRoutes.pop();
nextRoutes.push(route);
} else {
nextRoutes = [...routes];
nextRoutes.push(route);
}
const index = nextRoutes.length - routes.length;
const targetIndex = nextRoutes.length - 1;
console.log("Nav - index " + index + " Target " + targetIndex);
if (index < 0) {
console.log("Nav - go " + index);
router.go(index);
} else if (index == 0) {
console.log("Nav - replace");
routes = nextRoutes;
nextRoutes = null;
router.replace(route).catch((ignoredErr) => {});
} else {
console.log("Nav - push");
router.push(route).catch((ignoredErr) => {});
}
},
canPop() {
if (nextRoutes) {
return nextRoutes.length > 1;
}
return routes.length > 1;
},
pop() {
routes.pop();
router.go(-1);
},
};
app.$navigation = navigationService;
app.config.globalProperties.$navigation = navigationService;
},
};

5
tsconfig.json Normal file
View file

@ -0,0 +1,5 @@
{
"compilerOptions": {
"strict": true
},
}