Update dependencies and fix code for the changes.
This commit is contained in:
parent
2f2372efc2
commit
dd53bf2de5
9 changed files with 3754 additions and 17827 deletions
21451
package-lock.json
generated
21451
package-lock.json
generated
File diff suppressed because it is too large
Load diff
18
package.json
18
package.json
|
|
@ -17,12 +17,12 @@
|
||||||
"fix-webm-duration": "^1.0.0",
|
"fix-webm-duration": "^1.0.0",
|
||||||
"image-resize": "^1.1.5",
|
"image-resize": "^1.1.5",
|
||||||
"image-size": "^1.0.0",
|
"image-size": "^1.0.0",
|
||||||
"intersection-observer": "^0.11.0",
|
"intersection-observer": "^0.12",
|
||||||
"js-sha256": "^0.9.0",
|
"js-sha256": "^0.9.0",
|
||||||
"json-web-key": "^0.4.0",
|
"json-web-key": "^0.4.0",
|
||||||
"linkifyjs": "^2.1.9",
|
"linkifyjs": "3.0.0-beta.3",
|
||||||
"material-design-icons-iconfont": "^5.0.1",
|
"material-design-icons-iconfont": "^6.1",
|
||||||
"matrix-js-sdk": "^9.4.1",
|
"matrix-js-sdk": "^12.4",
|
||||||
"md-gum-polyfill": "^1.0.0",
|
"md-gum-polyfill": "^1.0.0",
|
||||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||||
"pretty-bytes": "^5.6.0",
|
"pretty-bytes": "^5.6.0",
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-clipboard2": "^0.3.1",
|
"vue-clipboard2": "^0.3.1",
|
||||||
"vue-i18n": "^8.24.4",
|
"vue-i18n": "^8.24.4",
|
||||||
"vue-resize": "^0.5.0",
|
"vue-resize": "^1.0",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^3.2.0",
|
||||||
"vue-sanitize": "^0.2.1",
|
"vue-sanitize": "^0.2.1",
|
||||||
"vue-swipeable-bottom-sheet": "^0.0.5",
|
"vue-swipeable-bottom-sheet": "^0.0.5",
|
||||||
|
|
@ -47,11 +47,11 @@
|
||||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
"@vue/cli-service": "~4.5.0",
|
"@vue/cli-service": "~4.5.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^7.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^7.0",
|
||||||
"sass": "^1.19.0",
|
"sass": "^1.19.0",
|
||||||
"sass-loader": "^8.0.0",
|
"sass-loader": "^10",
|
||||||
"vue-cli-plugin-vuetify": "~2.0.7",
|
"vue-cli-plugin-vuetify": "^2.4",
|
||||||
"vue-template-compiler": "^2.6.11",
|
"vue-template-compiler": "^2.6.11",
|
||||||
"vuetify-loader": "^1.3.0"
|
"vuetify-loader": "^1.3.0"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
indeterminate
|
indeterminate
|
||||||
color="primary"
|
color="primary"
|
||||||
></v-progress-circular>
|
></v-progress-circular>
|
||||||
<div>{{$t('menu.loading', {appName: appName})}}</div>
|
<div>{{ $t("menu.loading", { appName: appName }) }}</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
@ -90,8 +90,7 @@ export default {
|
||||||
if (this.$matrix.currentRoom) {
|
if (this.$matrix.currentRoom) {
|
||||||
title +=
|
title +=
|
||||||
" - " +
|
" - " +
|
||||||
(this.$matrix.currentRoom.summary.info.title ||
|
(this.$matrix.currentRoom.name || this.$matrix.currentRoom.roomId);
|
||||||
this.$matrix.currentRoom.roomId);
|
|
||||||
} else if (this.$matrix.currentRoomId) {
|
} else if (this.$matrix.currentRoomId) {
|
||||||
title += " - " + this.$matrix.currentRoomId;
|
title += " - " + this.$matrix.currentRoomId;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,13 +93,13 @@
|
||||||
:event="event"
|
:event="event"
|
||||||
:nextEvent="events[index + 1]"
|
:nextEvent="events[index + 1]"
|
||||||
:reactions="
|
:reactions="
|
||||||
timelineWindow._timelineSet.getRelationsForEvent(
|
timelineSet.getRelationsForEvent(
|
||||||
event.getId(),
|
event.getId(),
|
||||||
'm.annotation',
|
'm.annotation',
|
||||||
'm.reaction'
|
'm.reaction'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
:timelineSet="timelineWindow._timelineSet"
|
:timelineSet="timelineSet"
|
||||||
v-on:send-quick-reaction="sendQuickReaction"
|
v-on:send-quick-reaction="sendQuickReaction"
|
||||||
v-on:context-menu="showContextMenuForEvent($event)"
|
v-on:context-menu="showContextMenuForEvent($event)"
|
||||||
v-on:own-avatar-clicked="viewProfile"
|
v-on:own-avatar-clicked="viewProfile"
|
||||||
|
|
@ -559,6 +559,7 @@ export default {
|
||||||
events: [],
|
events: [],
|
||||||
currentInput: "",
|
currentInput: "",
|
||||||
typingMembers: [],
|
typingMembers: [],
|
||||||
|
timelineSet: null,
|
||||||
timelineWindow: null,
|
timelineWindow: null,
|
||||||
|
|
||||||
/** true if we are currently paginating */
|
/** true if we are currently paginating */
|
||||||
|
|
@ -824,10 +825,10 @@ export default {
|
||||||
console.log("Read up to " + initialEventId);
|
console.log("Read up to " + initialEventId);
|
||||||
|
|
||||||
//initialEventId = null;
|
//initialEventId = null;
|
||||||
|
this.timelineSet = this.room.getUnfilteredTimelineSet();
|
||||||
this.timelineWindow = new TimelineWindow(
|
this.timelineWindow = new TimelineWindow(
|
||||||
this.$matrix.matrixClient,
|
this.$matrix.matrixClient,
|
||||||
this.room.getUnfilteredTimelineSet(),
|
this.timelineSet,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
@ -903,15 +904,17 @@ export default {
|
||||||
) {
|
) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
// Instead of paging though ALL history, just reload a timeline at the live marker...
|
// Instead of paging though ALL history, just reload a timeline at the live marker...
|
||||||
|
var timelineSet = this.room.getUnfilteredTimelineSet();
|
||||||
var timelineWindow = new TimelineWindow(
|
var timelineWindow = new TimelineWindow(
|
||||||
this.$matrix.matrixClient,
|
this.$matrix.matrixClient,
|
||||||
this.room.getUnfilteredTimelineSet(),
|
timelineSet,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const self = this;
|
const self = this;
|
||||||
timelineWindow
|
timelineWindow
|
||||||
.load(null, 20)
|
.load(null, 20)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
self.timelineSet = timelineSet;
|
||||||
self.timelineWindow = timelineWindow;
|
self.timelineWindow = timelineWindow;
|
||||||
self.events = self.timelineWindow.getEvents();
|
self.events = self.timelineWindow.getEvents();
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
@click.stop="onHeaderClicked"
|
@click.stop="onHeaderClicked"
|
||||||
>
|
>
|
||||||
<div class="d-flex flex-nowrap room-name-inline">
|
<div class="d-flex flex-nowrap room-name-inline">
|
||||||
{{ room.summary.info.title }}
|
{{ room.name }}
|
||||||
<!--<v-icon>expand_more</v-icon>-->
|
<!--<v-icon>expand_more</v-icon>-->
|
||||||
</div>
|
</div>
|
||||||
<div class="num-members">{{ $tc("room.members", memberCount) }}</div>
|
<div class="num-members">{{ $tc("room.members", memberCount) }}</div>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<v-dialog v-model="showDialog" class="ma-0 pa-0" width="80%">
|
<v-dialog v-model="showDialog" class="ma-0 pa-0" width="80%">
|
||||||
<div class="dialog-content text-center" ref="qrContainer">
|
<div class="dialog-content text-center" ref="qrContainer">
|
||||||
<div class="d-flex flex-column text-center" style="align-items:center">
|
<div class="d-flex flex-column text-center" style="align-items: center">
|
||||||
<canvas
|
<canvas ref="qr" class="qr" id="qr" :style="qrStyle"></canvas>
|
||||||
ref="qr"
|
|
||||||
class="qr"
|
|
||||||
id="qr"
|
|
||||||
:style="qrStyle"
|
|
||||||
></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
<div>{{ $t("room_info.scan_code") }}</div>
|
<div>{{ $t("room_info.scan_code") }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -30,7 +25,7 @@ export default {
|
||||||
message: {
|
message: {
|
||||||
type: String,
|
type: String,
|
||||||
default: function () {
|
default: function () {
|
||||||
return false;
|
return null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -46,9 +41,9 @@ export default {
|
||||||
qrStyle() {
|
qrStyle() {
|
||||||
const w = document.documentElement.clientWidth;
|
const w = document.documentElement.clientWidth;
|
||||||
const h = document.documentElement.clientHeight;
|
const h = document.documentElement.clientHeight;
|
||||||
const s = 0.6 * Math.min(w,h);
|
const s = 0.6 * Math.min(w, h);
|
||||||
return "width: " + s + "px;height:" + s + "px;";
|
return "width: " + s + "px;height:" + s + "px;";
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
show: {
|
show: {
|
||||||
|
|
@ -61,7 +56,7 @@ export default {
|
||||||
this.$emit("close");
|
this.$emit("close");
|
||||||
} else {
|
} else {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.updateQR(this.message);
|
this.updateQR(this.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -76,23 +71,26 @@ export default {
|
||||||
updateQR(message) {
|
updateQR(message) {
|
||||||
var canvas = this.$refs.qr;
|
var canvas = this.$refs.qr;
|
||||||
var canvasContainer = this.$refs.qrContainer;
|
var canvasContainer = this.$refs.qrContainer;
|
||||||
if (message && canvas && canvasContainer) {
|
if (message && canvas && canvasContainer) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
QRCode.toCanvas(
|
QRCode.toCanvas(
|
||||||
canvas,
|
canvas,
|
||||||
message,
|
message,
|
||||||
{
|
{
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
margin: 1,
|
margin: 1,
|
||||||
width: Math.min(0.7 * canvasContainer.clientWidth, 0.7 * canvasContainer.clientHeight),
|
width: Math.min(
|
||||||
|
0.7 * canvasContainer.clientWidth,
|
||||||
|
0.7 * canvasContainer.clientHeight
|
||||||
|
),
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
if (error) console.error(error);
|
if (error) console.error(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,23 @@
|
||||||
<v-img :src="room.avatar" />
|
<v-img :src="room.avatar" />
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title>{{
|
<v-list-item-title>{{ room.name }}</v-list-item-title>
|
||||||
room.name || room.summary.info.title
|
|
||||||
}}</v-list-item-title>
|
|
||||||
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
|
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
<v-list-item-action>
|
<v-list-item-action>
|
||||||
<v-btn class="filled-button" depressed color="black" @click.stop="acceptInvitation(room)"
|
<v-btn
|
||||||
>{{$t('menu.join')}}</v-btn
|
class="filled-button"
|
||||||
|
depressed
|
||||||
|
color="black"
|
||||||
|
@click.stop="acceptInvitation(room)"
|
||||||
|
>{{ $t("menu.join") }}</v-btn
|
||||||
>
|
>
|
||||||
<v-btn class="filled-button" color="black" @click.stop="rejectInvitation(room)" text
|
<v-btn
|
||||||
>{{$t('menu.ignore')}}</v-btn
|
class="filled-button"
|
||||||
|
color="black"
|
||||||
|
@click.stop="rejectInvitation(room)"
|
||||||
|
text
|
||||||
|
>{{ $t("menu.ignore") }}</v-btn
|
||||||
>
|
>
|
||||||
</v-list-item-action>
|
</v-list-item-action>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
@ -49,7 +55,7 @@
|
||||||
{{ notificationCount(room) }}
|
{{ notificationCount(room) }}
|
||||||
</div>
|
</div>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title>{{ room.summary.info.title }}</v-list-item-title>
|
<v-list-item-title>{{ room.name }}</v-list-item-title>
|
||||||
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
|
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
@ -99,9 +105,9 @@ export default {
|
||||||
if (items == null) {
|
if (items == null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return items.sort(function(a, b) {
|
return items.sort(function (a, b) {
|
||||||
const titleA = a.name || a.summary.info.title;
|
const titleA = a.name || a.summary.info.title;
|
||||||
const titleB = b.name || b.summary.info.title
|
const titleB = b.name || b.summary.info.title;
|
||||||
if (titleA == null) {
|
if (titleA == null) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (titleB == null) {
|
} else if (titleB == null) {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,9 @@
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="6" v-if="ptt">
|
<v-col cols="6" v-if="ptt">
|
||||||
<div class="swipe-info"><< {{$t('voice_recorder.swipe_to_cancel')}}</div>
|
<div class="swipe-info">
|
||||||
|
<< {{ $t("voice_recorder.swipe_to_cancel") }}
|
||||||
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
@ -72,7 +74,9 @@
|
||||||
<v-icon color="white">delete_outline</v-icon>
|
<v-icon color="white">delete_outline</v-icon>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="6">
|
<v-col cols="6">
|
||||||
<div class="swipe-info">{{$t('voice_recorder.release_to_cancel')}}</div>
|
<div class="swipe-info">
|
||||||
|
{{ $t("voice_recorder.release_to_cancel") }}
|
||||||
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
@ -93,9 +97,9 @@
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="3">
|
<v-col cols="3">
|
||||||
<v-btn @click.stop="cancelRecording" text class="swipe-info"
|
<v-btn @click.stop="cancelRecording" text class="swipe-info">{{
|
||||||
>{{$t('menu.cancel')}}</v-btn
|
$t("menu.cancel")
|
||||||
>
|
}}</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="3">
|
<v-col cols="3">
|
||||||
<v-btn @click.stop="stopRecording" icon class="swipe-info"
|
<v-btn @click.stop="stopRecording" icon class="swipe-info"
|
||||||
|
|
@ -116,7 +120,7 @@
|
||||||
<v-row align="center">
|
<v-row align="center">
|
||||||
<v-col>
|
<v-col>
|
||||||
<div class="swipe-info">
|
<div class="swipe-info">
|
||||||
{{ errorMessage || $t('voice_recorder.failed_to_record') }}
|
{{ errorMessage || $t("voice_recorder.failed_to_record") }}
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col align="right">
|
<v-col align="right">
|
||||||
|
|
@ -236,6 +240,7 @@ export default {
|
||||||
this.startRecording();
|
this.startRecording();
|
||||||
} else {
|
} else {
|
||||||
console.log("Not PTT");
|
console.log("Not PTT");
|
||||||
|
//eslint-disable-next-line
|
||||||
this.micButtonRef.$el.style.display = "none";
|
this.micButtonRef.$el.style.display = "none";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -250,6 +255,7 @@ export default {
|
||||||
this.startCoordinateX = null;
|
this.startCoordinateX = null;
|
||||||
this.startCoordinateY = null;
|
this.startCoordinateY = null;
|
||||||
this.recordingLocked = false;
|
this.recordingLocked = false;
|
||||||
|
//eslint-disable-next-line
|
||||||
this.micButtonRef.$el.style.display = "block";
|
this.micButtonRef.$el.style.display = "block";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -341,7 +347,7 @@ export default {
|
||||||
mimeType: "audio/webm",
|
mimeType: "audio/webm",
|
||||||
sampleRate: 16000,
|
sampleRate: 16000,
|
||||||
desiredSampRate: 16000,
|
desiredSampRate: 16000,
|
||||||
numberOfAudioChannels: 1
|
numberOfAudioChannels: 1,
|
||||||
});
|
});
|
||||||
this.recorder.startRecording();
|
this.recorder.startRecording();
|
||||||
this.state = State.RECORDING;
|
this.state = State.RECORDING;
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,13 @@ export default {
|
||||||
|
|
||||||
joinedRooms() {
|
joinedRooms() {
|
||||||
return this.rooms.filter(room => {
|
return this.rooms.filter(room => {
|
||||||
return room._selfMembership === 'join'
|
return room.selfMembership === 'join'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
invites() {
|
invites() {
|
||||||
return this.rooms.filter(room => {
|
return this.rooms.filter(room => {
|
||||||
return room._selfMembership === 'invite'
|
return room.selfMembership === 'invite'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -348,13 +348,13 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoom(ignoredroom) {
|
onRoom(ignoredroom) {
|
||||||
console.log("Got room: " + ignoredroom);
|
console.log("Got room", ignoredroom);
|
||||||
this.reloadRooms();
|
this.reloadRooms();
|
||||||
this.updateNotificationCount();
|
this.updateNotificationCount();
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoomMyMembership(room) {
|
onRoomMyMembership(room) {
|
||||||
if (room._selfMembership === "invite") {
|
if (room.selfMembership === "invite") {
|
||||||
// Invitation. Need to call "recalculate" to pick
|
// Invitation. Need to call "recalculate" to pick
|
||||||
// up room name, not sure why exactly.
|
// up room name, not sure why exactly.
|
||||||
room.recalculate();
|
room.recalculate();
|
||||||
|
|
@ -394,7 +394,7 @@ export default {
|
||||||
// each time!
|
// each time!
|
||||||
var updatedRooms = this.matrixClient.getVisibleRooms();
|
var updatedRooms = this.matrixClient.getVisibleRooms();
|
||||||
updatedRooms = updatedRooms.filter(room => {
|
updatedRooms = updatedRooms.filter(room => {
|
||||||
return room._selfMembership && (room._selfMembership == "invite" || room._selfMembership == "join");
|
return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join");
|
||||||
});
|
});
|
||||||
updatedRooms.forEach(room => {
|
updatedRooms.forEach(room => {
|
||||||
if (!room.avatar) {
|
if (!room.avatar) {
|
||||||
|
|
@ -438,7 +438,7 @@ export default {
|
||||||
var ids = {};
|
var ids = {};
|
||||||
const ret = [];
|
const ret = [];
|
||||||
for (const room of this.rooms) {
|
for (const room of this.rooms) {
|
||||||
if (room._selfMembership == 'join' && this.getRoomJoinRule(room) == 'invite') {
|
if (room.selfMembership == 'join' && this.getRoomJoinRule(room) == 'invite') {
|
||||||
for (const member of room.getJoinedMembers()) {
|
for (const member of room.getJoinedMembers()) {
|
||||||
if (member.userId != this.currentUserId && !ids[member.userId]) {
|
if (member.userId != this.currentUserId && !ids[member.userId]) {
|
||||||
ids[member.userId] = member;
|
ids[member.userId] = member;
|
||||||
|
|
@ -661,7 +661,7 @@ export default {
|
||||||
* @param {*} userId
|
* @param {*} userId
|
||||||
*/
|
*/
|
||||||
isDirectRoomWith(room, userId) {
|
isDirectRoomWith(room, userId) {
|
||||||
if (room._selfMembership == "join" && room.getInvitedAndJoinedMemberCount() == 2) {
|
if (room.selfMembership == "join" && room.getInvitedAndJoinedMemberCount() == 2) {
|
||||||
// Is the other member the one we are looking for?
|
// Is the other member the one we are looking for?
|
||||||
if (room.getMembersWithMembership("join").some(item => item.userId == userId)) {
|
if (room.getMembersWithMembership("join").some(item => item.userId == userId)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue