mirror of
https://github.com/Troplo/Colubrina.git
synced 2024-11-23 03:36:42 +11:00
update
This commit is contained in:
parent
87302afa1d
commit
be90a91e52
22 changed files with 2768 additions and 1119 deletions
|
@ -7,3 +7,4 @@ VUE_APP_MATOMO_TRACKER=flow
|
||||||
VUE_APP_MATOMO_DOMAINS=*.troplo.com
|
VUE_APP_MATOMO_DOMAINS=*.troplo.com
|
||||||
VUE_APP_MATOMO_ENABLED=true
|
VUE_APP_MATOMO_ENABLED=true
|
||||||
VUE_APP_SENTRY_ENABLED=true
|
VUE_APP_SENTRY_ENABLED=true
|
||||||
|
VUE_APP_BASE_URL=
|
|
@ -34,12 +34,36 @@ module.exports = async function (socket, next) {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//
|
socket.user = {
|
||||||
|
id: null,
|
||||||
|
username: "Not Authenticated"
|
||||||
|
}
|
||||||
|
socket.compassUser = {
|
||||||
|
id: null,
|
||||||
|
username: "BC-NOAUTH"
|
||||||
|
}
|
||||||
|
next()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//
|
socket.user = {
|
||||||
|
id: null,
|
||||||
|
username: "Not Authenticated"
|
||||||
|
}
|
||||||
|
socket.compassUser = {
|
||||||
|
id: null,
|
||||||
|
username: "BC-NOAUTH"
|
||||||
|
}
|
||||||
|
next()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//
|
socket.user = {
|
||||||
|
id: null,
|
||||||
|
username: "Not Authenticated"
|
||||||
|
}
|
||||||
|
socket.compassUser = {
|
||||||
|
id: null,
|
||||||
|
username: "BC-NOAUTH"
|
||||||
|
}
|
||||||
|
next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,88 +15,115 @@ module.exports = {
|
||||||
id: socket.user.id
|
id: socket.user.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
socket.join(user.id)
|
if (user && socket.user.id) {
|
||||||
socket.emit("siteState", {
|
console.log(socket.user.id)
|
||||||
release: process.env.RELEASE,
|
socket.join(user.id)
|
||||||
notification: process.env.NOTIFICATION,
|
socket.emit("siteState", {
|
||||||
notificationType: process.env.NOTIFICATION_TYPE,
|
release: process.env.RELEASE,
|
||||||
latestVersion: require("../../package.json").version
|
notification: process.env.NOTIFICATION,
|
||||||
})
|
notificationType: process.env.NOTIFICATION_TYPE,
|
||||||
const friends = await Friend.findAll({
|
latestVersion: require("../../package.json").version
|
||||||
where: {
|
})
|
||||||
userId: user.id,
|
const friends = await Friend.findAll({
|
||||||
status: "accepted"
|
where: {
|
||||||
}
|
userId: user.id,
|
||||||
})
|
status: "accepted"
|
||||||
await user.update({
|
}
|
||||||
status:
|
})
|
||||||
user.storedStatus === "invisible" ? "offline" : user.storedStatus
|
await user.update({
|
||||||
})
|
|
||||||
friends.forEach((friend) => {
|
|
||||||
io.to(friend.friendId).emit("userStatus", {
|
|
||||||
userId: user.id,
|
|
||||||
status:
|
status:
|
||||||
user.storedStatus === "invisible" ? "offline" : user.storedStatus
|
user.storedStatus === "invisible" ? "offline" : user.storedStatus
|
||||||
})
|
})
|
||||||
})
|
|
||||||
socket.on("ping", () => {
|
|
||||||
socket.emit("pong")
|
|
||||||
})
|
|
||||||
socket.on("idle", async () => {
|
|
||||||
const user = await User.findOne({
|
|
||||||
where: {
|
|
||||||
id: socket.user.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (user.storedStatus === "online") {
|
|
||||||
friends.forEach((friend) => {
|
|
||||||
io.to(friend.friendId).emit("userStatus", {
|
|
||||||
userId: user.id,
|
|
||||||
status: "away"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
io.to(user.id).emit("userStatus", {
|
|
||||||
userId: user.id,
|
|
||||||
status: "away"
|
|
||||||
})
|
|
||||||
await user.update({
|
|
||||||
status: "away"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
socket.on("online", async () => {
|
|
||||||
const user = await User.findOne({
|
|
||||||
where: {
|
|
||||||
id: socket.user.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (user.storedStatus === "online") {
|
|
||||||
friends.forEach((friend) => {
|
|
||||||
io.to(friend.friendId).emit("userStatus", {
|
|
||||||
userId: user.id,
|
|
||||||
status: "online"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
io.to(user.id).emit("userStatus", {
|
|
||||||
userId: user.id,
|
|
||||||
status: "online"
|
|
||||||
})
|
|
||||||
await user.update({
|
|
||||||
status: "online"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
socket.on("disconnect", async function () {
|
|
||||||
friends.forEach((friend) => {
|
friends.forEach((friend) => {
|
||||||
io.to(friend.friendId).emit("userStatus", {
|
io.to(friend.friendId).emit("userStatus", {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
status:
|
||||||
|
user.storedStatus === "invisible" ? "offline" : user.storedStatus
|
||||||
|
})
|
||||||
|
})
|
||||||
|
socket.on("ping", () => {
|
||||||
|
socket.emit("pong")
|
||||||
|
})
|
||||||
|
socket.on("bcBots/deleteMessage", (e) => {
|
||||||
|
if (socket.user.bot) {
|
||||||
|
socket.to(e.userId).emit("deleteMessage", e)
|
||||||
|
} else {
|
||||||
|
socket.emit("bcBots/deleteMessage", {
|
||||||
|
error: "You cannot perform this action."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
socket.on("idle", async () => {
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: {
|
||||||
|
id: socket.user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (user.storedStatus === "online") {
|
||||||
|
friends.forEach((friend) => {
|
||||||
|
io.to(friend.friendId).emit("userStatus", {
|
||||||
|
userId: user.id,
|
||||||
|
status: "away"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
io.to(user.id).emit("userStatus", {
|
||||||
|
userId: user.id,
|
||||||
|
status: "away"
|
||||||
|
})
|
||||||
|
await user.update({
|
||||||
|
status: "away"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
socket.on("online", async () => {
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: {
|
||||||
|
id: socket.user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (user.storedStatus === "online") {
|
||||||
|
friends.forEach((friend) => {
|
||||||
|
io.to(friend.friendId).emit("userStatus", {
|
||||||
|
userId: user.id,
|
||||||
|
status: "online"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
io.to(user.id).emit("userStatus", {
|
||||||
|
userId: user.id,
|
||||||
|
status: "online"
|
||||||
|
})
|
||||||
|
await user.update({
|
||||||
|
status: "online"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
socket.on("disconnect", async function () {
|
||||||
|
friends.forEach((friend) => {
|
||||||
|
io.to(friend.friendId).emit("userStatus", {
|
||||||
|
userId: user.id,
|
||||||
|
status: "offline"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
await user.update({
|
||||||
status: "offline"
|
status: "offline"
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
await user.update({
|
} else {
|
||||||
status: "offline"
|
socket.join(-1)
|
||||||
|
socket.emit("siteState", {
|
||||||
|
release: process.env.RELEASE,
|
||||||
|
notification: process.env.NOTIFICATION,
|
||||||
|
notificationType: process.env.NOTIFICATION_TYPE,
|
||||||
|
latestVersion: require("../../package.json").version
|
||||||
})
|
})
|
||||||
})
|
socket.emit("unauthorized", {
|
||||||
|
message: "Please reauth."
|
||||||
|
})
|
||||||
|
console.log("Unauthenticated user")
|
||||||
|
socket.on("reAuth", async () => {
|
||||||
|
socket.disconnect()
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
console.log("WS OK")
|
console.log("WS OK")
|
||||||
app.set("io", io)
|
app.set("io", io)
|
||||||
|
|
43
backend/migrations/20220706061900-nicknames.js
Normal file
43
backend/migrations/20220706061900-nicknames.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface, Sequelize) {
|
||||||
|
await queryInterface.createTable("Nicknames", {
|
||||||
|
id: {
|
||||||
|
allowNull: false,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
type: Sequelize.BIGINT
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.BIGINT
|
||||||
|
},
|
||||||
|
nickname: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.STRING
|
||||||
|
},
|
||||||
|
friendId: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.BIGINT
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.DATE
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.DATE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface, Sequelize) {
|
||||||
|
/**
|
||||||
|
* Add reverting commands here.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* await queryInterface.dropTable('users');
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
20
backend/migrations/20220712133850-bot.js
Normal file
20
backend/migrations/20220712133850-bot.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface, Sequelize) {
|
||||||
|
await queryInterface.addColumn("Users", "bot", {
|
||||||
|
type: Sequelize.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async down(queryInterface, Sequelize) {
|
||||||
|
/**
|
||||||
|
* Add reverting commands here.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* await queryInterface.dropTable('users');
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
55
backend/models/nicknames.js
Normal file
55
backend/models/nicknames.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
"use strict"
|
||||||
|
const { Model } = require("sequelize")
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
class Nickname extends Model {
|
||||||
|
/**
|
||||||
|
* Helper method for defining associations.
|
||||||
|
* This method is not a part of Sequelize lifecycle.
|
||||||
|
* The `models/index` file will call this method automatically.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
static associate(models) {
|
||||||
|
Nickname.belongsTo(models.User, {
|
||||||
|
foreignKey: "userId",
|
||||||
|
as: "user"
|
||||||
|
})
|
||||||
|
Nickname.belongsTo(models.User, {
|
||||||
|
foreignKey: "friendId",
|
||||||
|
as: "userNickname"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Nickname.init(
|
||||||
|
{
|
||||||
|
userId: {
|
||||||
|
allowNull: false,
|
||||||
|
type: DataTypes.BIGINT
|
||||||
|
},
|
||||||
|
nickname: {
|
||||||
|
allowNull: false,
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
validate: {
|
||||||
|
notEmpty: true,
|
||||||
|
len: [1, 20]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
friendId: {
|
||||||
|
allowNull: false,
|
||||||
|
type: DataTypes.BIGINT
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: DataTypes.DATE
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: DataTypes.DATE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sequelize,
|
||||||
|
modelName: "Nickname"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return Nickname
|
||||||
|
}
|
|
@ -16,6 +16,10 @@ module.exports = (sequelize, DataTypes) => {
|
||||||
as: "friends",
|
as: "friends",
|
||||||
foreignKey: "userId"
|
foreignKey: "userId"
|
||||||
})
|
})
|
||||||
|
User.hasOne(models.Nickname, {
|
||||||
|
foreignKey: "friendId",
|
||||||
|
as: "nickname"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
User.init(
|
User.init(
|
||||||
|
@ -135,6 +139,11 @@ module.exports = (sequelize, DataTypes) => {
|
||||||
avatar: {
|
avatar: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true
|
allowNull: true
|
||||||
|
},
|
||||||
|
bot: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -342,40 +342,6 @@ router.put("/settings/:type", auth, async (req, res, next) => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function checkPassword(password) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.post(
|
|
||||||
"http://localhost:23994/services/admin.svc/GetApiKey",
|
|
||||||
{
|
|
||||||
password: password,
|
|
||||||
schoolId: req.body.schoolId,
|
|
||||||
sussiId: req.user.sussiId
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
compassInstance:
|
|
||||||
req.header("compassInstance") ||
|
|
||||||
req.query.compassInstance ||
|
|
||||||
"devices"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.data.d) {
|
|
||||||
resolve(true)
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
resolve(false)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
reject(e)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const io = req.app.get("io")
|
const io = req.app.get("io")
|
||||||
if (req.params.type === "full") {
|
if (req.params.type === "full") {
|
||||||
|
@ -452,66 +418,6 @@ router.put("/settings/:type", auth, async (req, res, next) => {
|
||||||
} else {
|
} else {
|
||||||
throw Errors.invalidParameter("Password or Code")
|
throw Errors.invalidParameter("Password or Code")
|
||||||
}
|
}
|
||||||
} else if (req.params.type === "totpConfirm") {
|
|
||||||
const user = await User.findOne({
|
|
||||||
where: {
|
|
||||||
id: req.user.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (user.totp) {
|
|
||||||
const verified = speakeasy.totp.verify({
|
|
||||||
secret: user.totp,
|
|
||||||
encoding: "base32",
|
|
||||||
token: req.body.code
|
|
||||||
})
|
|
||||||
if (verified) {
|
|
||||||
await User.update(
|
|
||||||
{
|
|
||||||
totpEnabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
where: {
|
|
||||||
id: req.user.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
res.sendStatus(204)
|
|
||||||
} else {
|
|
||||||
throw Errors.invalidTotp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw Errors.unknown
|
|
||||||
}
|
|
||||||
} else if (req.params.type === "bcSessionsEnable") {
|
|
||||||
/*
|
|
||||||
const match = await checkPassword(req.body.password)
|
|
||||||
if (match) {
|
|
||||||
await User.update(
|
|
||||||
{
|
|
||||||
bcSessionsEnabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
where: {
|
|
||||||
id: req.user.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const session = await Session.create({
|
|
||||||
session:
|
|
||||||
"BETTERCOMPASS-" +
|
|
||||||
cryptoRandomString({
|
|
||||||
length: 64
|
|
||||||
}),
|
|
||||||
compassUserId: req.user.compassUserId,
|
|
||||||
sussiId: req.user.sussiId,
|
|
||||||
instance: req.user.instance,
|
|
||||||
other: {},
|
|
||||||
userId: req.user.id
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
throw Errors.invalidCredentials
|
|
||||||
}*/
|
|
||||||
throw Errors.experimentsOptIn
|
|
||||||
} else if (req.params.type === "password") {
|
} else if (req.params.type === "password") {
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@mdi/js": "^6.6.95",
|
"@mdi/js": "^6.6.95",
|
||||||
"@sentry/tracing": "^6.19.3",
|
"@sentry/tracing": "^6.19.3",
|
||||||
"@sentry/vue": "^6.19.3",
|
"@sentry/vue": "^6.19.3",
|
||||||
|
"@tauri-apps/api": "^1.0.2",
|
||||||
"apollo-boost": "^0.4.9",
|
"apollo-boost": "^0.4.9",
|
||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
"brace": "^0.11.1",
|
"brace": "^0.11.1",
|
||||||
|
|
160
src/App.vue
160
src/App.vue
|
@ -404,27 +404,6 @@
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
<v-main>
|
<v-main>
|
||||||
<Header></Header>
|
<Header></Header>
|
||||||
<v-container
|
|
||||||
v-if="
|
|
||||||
$store.state.user?.compact === 'nagPending' &&
|
|
||||||
$vuetify.breakpoint.lgAndDown &&
|
|
||||||
!$vuetify.breakpoint.mobile
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<v-alert dense text color="info" dismissible v-model="compactModeNag">
|
|
||||||
Introducing <strong>Compact Mode</strong>, a better experience for
|
|
||||||
lower resolution devices.
|
|
||||||
<v-btn
|
|
||||||
to="/settings/appearance"
|
|
||||||
text
|
|
||||||
color="primary"
|
|
||||||
outlined
|
|
||||||
class="ml-1"
|
|
||||||
>
|
|
||||||
Settings
|
|
||||||
</v-btn>
|
|
||||||
</v-alert>
|
|
||||||
</v-container>
|
|
||||||
<v-container
|
<v-container
|
||||||
v-if="$store.state.site.latestVersion > $store.state.versioning.version"
|
v-if="$store.state.site.latestVersion > $store.state.versioning.version"
|
||||||
id="update-notify-banner"
|
id="update-notify-banner"
|
||||||
|
@ -466,10 +445,9 @@
|
||||||
<style></style>
|
<style></style>
|
||||||
<script>
|
<script>
|
||||||
import AjaxErrorHandler from "@/lib/errorHandler"
|
import AjaxErrorHandler from "@/lib/errorHandler"
|
||||||
import Vue from "vue"
|
|
||||||
import Vuetify from "@/plugins/vuetify"
|
|
||||||
import { VueFinalModal } from "vue-final-modal"
|
import { VueFinalModal } from "vue-final-modal"
|
||||||
import Header from "@/components/Header"
|
import Header from "@/components/Header"
|
||||||
|
import Vue from "vue"
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
components: {
|
components: {
|
||||||
|
@ -580,35 +558,6 @@ export default {
|
||||||
return index.charAt(0).toUpperCase() + index.slice(1)
|
return index.charAt(0).toUpperCase() + index.slice(1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computeColor(event) {
|
|
||||||
if (this.$vuetify?.theme?.themes) {
|
|
||||||
if (event.color === "#003300") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarActivityType8
|
|
||||||
} else if (event.color === "#133897") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarExternalActivity
|
|
||||||
} else if (event.activityType === 7 || event.color === "#f4dcdc") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarActivityType7
|
|
||||||
} else if (event.color === "#dce6f4") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarNormalActivity
|
|
||||||
} else if (event.activityType === 10) {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarActivityType10
|
|
||||||
} else {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarNormalActivity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
editorInit() {
|
editorInit() {
|
||||||
require("brace/ext/language_tools")
|
require("brace/ext/language_tools")
|
||||||
require("brace/mode/css")
|
require("brace/mode/css")
|
||||||
|
@ -617,19 +566,6 @@ export default {
|
||||||
require("brace/theme/chrome")
|
require("brace/theme/chrome")
|
||||||
require("brace/snippets/css")
|
require("brace/snippets/css")
|
||||||
},
|
},
|
||||||
baseRole() {
|
|
||||||
if (this.$store.state.user?.baseRole) {
|
|
||||||
return (
|
|
||||||
this.$store.state.user.baseRole
|
|
||||||
.toLowerCase()
|
|
||||||
.charAt(0)
|
|
||||||
.toUpperCase() +
|
|
||||||
this.$store.state.user.baseRole.toLowerCase().slice(1)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return "Not Authenticated"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveSettings() {
|
saveSettings() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.$vuetify.theme.dark = this.$store.state.user?.theme === "dark"
|
this.$vuetify.theme.dark = this.$store.state.user?.theme === "dark"
|
||||||
|
@ -643,15 +579,6 @@ export default {
|
||||||
AjaxErrorHandler(this.$store)(e)
|
AjaxErrorHandler(this.$store)(e)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
completeGuidedWizard() {
|
|
||||||
this.loadingGuidedWizard = true
|
|
||||||
this.$store.dispatch("saveOnlineSettings", {
|
|
||||||
guidedWizard: false
|
|
||||||
})
|
|
||||||
this.$store.dispatch("getUserInfo").then(() => {
|
|
||||||
this.loadingGuidedWizard = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getThemes() {
|
getThemes() {
|
||||||
this.axios.get("/api/v1/themes").then((res) => {
|
this.axios.get("/api/v1/themes").then((res) => {
|
||||||
this.themes = res.data.map((theme) => {
|
this.themes = res.data.map((theme) => {
|
||||||
|
@ -692,25 +619,27 @@ export default {
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
AjaxErrorHandler(this.$store)(e)
|
AjaxErrorHandler(this.$store)(e)
|
||||||
})
|
})
|
||||||
},
|
|
||||||
retryConnection() {
|
|
||||||
this.connectionLoading = true
|
|
||||||
this.$store.dispatch("getState").finally(() => {
|
|
||||||
this.connectionLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
validate(value, defaultValue) {
|
|
||||||
if (value === undefined || value === null) {
|
|
||||||
return defaultValue
|
|
||||||
} else {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
Vue.axios.defaults.headers.common["Authorization"] =
|
||||||
|
localStorage.getItem("session")
|
||||||
|
if (this.$vuetify.breakpoint.mobile) {
|
||||||
|
this.$store.state.drawer = false
|
||||||
|
}
|
||||||
if (localStorage.getItem("cssTipsDismissed")) {
|
if (localStorage.getItem("cssTipsDismissed")) {
|
||||||
this.cssTips = false
|
this.cssTips = false
|
||||||
}
|
}
|
||||||
|
if (localStorage.getItem("userPanel")) {
|
||||||
|
this.$store.state.userPanel = JSON.parse(
|
||||||
|
localStorage.getItem("userPanel")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.$store.state.userPanel = true
|
||||||
|
}
|
||||||
|
if (this.$vuetify.breakpoint.mobile) {
|
||||||
|
this.$store.state.userPanel = false
|
||||||
|
}
|
||||||
window.addEventListener("offline", () => {
|
window.addEventListener("offline", () => {
|
||||||
this.$store.commit("setOnline", false)
|
this.$store.commit("setOnline", false)
|
||||||
this.$store.dispatch("getState")
|
this.$store.dispatch("getState")
|
||||||
|
@ -721,56 +650,11 @@ export default {
|
||||||
this.$store.dispatch("getUserInfo")
|
this.$store.dispatch("getUserInfo")
|
||||||
})
|
})
|
||||||
this.$socket.connect()
|
this.$socket.connect()
|
||||||
Vue.axios.defaults.headers.common["CompassAPIKey"] =
|
|
||||||
localStorage.getItem("apiKey")
|
|
||||||
Vue.axios.defaults.headers.common["Authorization"] =
|
|
||||||
localStorage.getItem("bcToken")
|
|
||||||
document.title = this.$route.name
|
document.title = this.$route.name
|
||||||
? this.$route.name + " - " + this.$store.state.site.name
|
? this.$route.name + " - " + this.$store.state.site.name
|
||||||
: this.$store.state.site.name
|
: this.$store.state.site.name
|
||||||
this.$store.commit("setLoading", true)
|
this.$store.commit("setLoading", true)
|
||||||
this.$vuetify.theme.dark = this.$store.state.user?.theme === "dark" || true
|
this.$vuetify.theme.dark = this.$store.state.user?.theme === "dark" || true
|
||||||
this.axios.defaults.headers.common["compassInstance"] =
|
|
||||||
localStorage.getItem("schoolInstance")
|
|
||||||
this.axios.defaults.headers.common["compassSchoolId"] =
|
|
||||||
localStorage.getItem("schoolId")
|
|
||||||
if (JSON.parse(localStorage.getItem("userCache"))?.id) {
|
|
||||||
const user = JSON.parse(localStorage.getItem("userCache"))
|
|
||||||
const name = user.themeObject.id
|
|
||||||
const dark = user.themeObject.theme.dark
|
|
||||||
const light = user.themeObject.theme.light
|
|
||||||
if (user.accentColor) {
|
|
||||||
user.themeObject.theme.dark.primary = user.accentColor
|
|
||||||
user.themeObject.theme.light.primary = user.accentColor
|
|
||||||
}
|
|
||||||
if (JSON.parse(localStorage.getItem("subjectsCache"))) {
|
|
||||||
this.$store.commit(
|
|
||||||
"setSubjects",
|
|
||||||
JSON.parse(localStorage.getItem("subjectsCache"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Vuetify.framework.theme.themes.dark = dark
|
|
||||||
Vuetify.framework.theme.themes.light = light
|
|
||||||
Vuetify.framework.theme.themes.name = name
|
|
||||||
Vuetify.framework.theme.themes.primaryType =
|
|
||||||
user.themeObject.theme.primaryType
|
|
||||||
this.$store.commit(
|
|
||||||
"setUser",
|
|
||||||
JSON.parse(localStorage.getItem("userCache"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (localStorage.getItem("calendarCache")?.length) {
|
|
||||||
this.$store.commit(
|
|
||||||
"setCalendar",
|
|
||||||
JSON.parse(localStorage.getItem("calendarCache")).map((event) => {
|
|
||||||
return {
|
|
||||||
...event,
|
|
||||||
start: new Date(event.start),
|
|
||||||
end: new Date(event.finish)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
this.$store.dispatch("getState")
|
this.$store.dispatch("getState")
|
||||||
this.$store.dispatch("checkAuth").catch(() => {
|
this.$store.dispatch("checkAuth").catch(() => {
|
||||||
this.$store.dispatch("logout")
|
this.$store.dispatch("logout")
|
||||||
|
@ -853,9 +737,6 @@ export default {
|
||||||
this.$store.state.site.notification = state.notification
|
this.$store.state.site.notification = state.notification
|
||||||
this.$store.state.site.notificationType = state.notificationType
|
this.$store.state.site.notificationType = state.notificationType
|
||||||
})
|
})
|
||||||
setInterval(() => {
|
|
||||||
this.$socket.emit("ping")
|
|
||||||
}, 10000)
|
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
if (JSON.parse(process.env.VUE_APP_MATOMO_ENABLED)) {
|
if (JSON.parse(process.env.VUE_APP_MATOMO_ENABLED)) {
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
|
@ -870,11 +751,8 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
compactModeNag(val) {
|
"$store.state.userPanel"(val) {
|
||||||
if (!val) {
|
localStorage.setItem("userPanel", val)
|
||||||
this.$store.state.user.compact = "disabled"
|
|
||||||
this.$store.dispatch("saveOnlineSettings")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
cssTips(val) {
|
cssTips(val) {
|
||||||
localStorage.setItem("cssTipsDismissed", !val)
|
localStorage.setItem("cssTipsDismissed", !val)
|
||||||
|
@ -908,7 +786,7 @@ export default {
|
||||||
search() {
|
search() {
|
||||||
if (this.search) {
|
if (this.search) {
|
||||||
if (this.search.id) {
|
if (this.search.id) {
|
||||||
this.$router.push("/activity/activity/" + this.search.id)
|
this.$router.push("/communications/" + this.search.id)
|
||||||
this.$store.commit("setSearch", false)
|
this.$store.commit("setSearch", false)
|
||||||
this.search = null
|
this.search = null
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
|
292
src/components/CommsInput.vue
Normal file
292
src/components/CommsInput.vue
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-toolbar
|
||||||
|
elevation="0"
|
||||||
|
outlined
|
||||||
|
height="40"
|
||||||
|
color="card"
|
||||||
|
v-if="file"
|
||||||
|
style="cursor: pointer; overflow: hidden"
|
||||||
|
class="mb-2"
|
||||||
|
>
|
||||||
|
<v-toolbar-title>
|
||||||
|
<v-icon> mdi-attachment </v-icon>
|
||||||
|
{{ file.name }}
|
||||||
|
</v-toolbar-title>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn icon @click="file = null" small>
|
||||||
|
<v-icon> mdi-close </v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar>
|
||||||
|
<v-textarea
|
||||||
|
style="margin-bottom: 5px"
|
||||||
|
autofocus
|
||||||
|
label="Type a message"
|
||||||
|
placeholder="Keep it civil"
|
||||||
|
type="text"
|
||||||
|
ref="message-input"
|
||||||
|
:id="edit ? 'edit-input' : 'message-input'"
|
||||||
|
outlined
|
||||||
|
append-outer-icon="mdi-send"
|
||||||
|
auto-grow
|
||||||
|
@keyup.up="editLastMessage"
|
||||||
|
@keyup.esc="handleEsc"
|
||||||
|
@click:append-outer="handleMessage()"
|
||||||
|
@keydown.enter.exact.prevent="handleMessage()"
|
||||||
|
v-model="message"
|
||||||
|
@paste="handlePaste"
|
||||||
|
@change="handleChange"
|
||||||
|
rows="1"
|
||||||
|
single-line
|
||||||
|
dense
|
||||||
|
hide-details
|
||||||
|
@keydown.enter.shift.exact.prevent="newLine($event)"
|
||||||
|
>
|
||||||
|
<template v-slot:append-outer>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
small
|
||||||
|
@click="handleMessage()"
|
||||||
|
v-if="!edit"
|
||||||
|
:retain-focus-on-click="false"
|
||||||
|
class="no-focus"
|
||||||
|
>
|
||||||
|
<v-icon> mdi-send </v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-file-input
|
||||||
|
style="margin-top: -18px"
|
||||||
|
single-line
|
||||||
|
hide-input
|
||||||
|
v-model="file"
|
||||||
|
@change="getURLForImage"
|
||||||
|
v-if="!edit"
|
||||||
|
></v-file-input>
|
||||||
|
</template>
|
||||||
|
</v-textarea>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AjaxErrorHandler from "@/lib/errorHandler"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CommsInput",
|
||||||
|
props: {
|
||||||
|
replying: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
autoScroll: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
editLastMessage: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
endEdit: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
endSend: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
chat: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
message: "",
|
||||||
|
file: null,
|
||||||
|
blobURL: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleMessage() {
|
||||||
|
if (this.edit) {
|
||||||
|
this.editMessage()
|
||||||
|
} else {
|
||||||
|
this.sendMessage()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editMessage() {
|
||||||
|
if (this.message.length > 0) {
|
||||||
|
this.axios
|
||||||
|
.put(
|
||||||
|
process.env.VUE_APP_BASE_URL +
|
||||||
|
"/api/v1/communications/" +
|
||||||
|
this.$route.params.id +
|
||||||
|
"/message/edit",
|
||||||
|
{
|
||||||
|
id: this.edit.id,
|
||||||
|
content: this.message
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.edit.editing = false
|
||||||
|
this.edit.id = null
|
||||||
|
this.edit.content = ""
|
||||||
|
this.focusInput()
|
||||||
|
this.endEdit()
|
||||||
|
// response will be handled via WebSocket
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
AjaxErrorHandler(this.$store)(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
newLine(event) {
|
||||||
|
this.message = event.target.value + "\n"
|
||||||
|
},
|
||||||
|
handleEsc() {
|
||||||
|
if (!this.edit) {
|
||||||
|
if (this.replying) {
|
||||||
|
this.endSend()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.endEdit()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getURLForImage() {
|
||||||
|
const file = this.file
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = (e) => {
|
||||||
|
this.blobURL = e.target.result
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
},
|
||||||
|
handleChange() {
|
||||||
|
if (this.$store.state.user.bcUser.storedStatus !== "invisible") {
|
||||||
|
if (this.typingDate) {
|
||||||
|
const now = new Date()
|
||||||
|
if (now - this.typingDate > 5000) {
|
||||||
|
this.typingDate = now
|
||||||
|
this.axios.put(
|
||||||
|
process.env.VUE_APP_BASE_URL +
|
||||||
|
"/api/v1/communications/" +
|
||||||
|
this.chat.id +
|
||||||
|
"/typing"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.typingDate = new Date()
|
||||||
|
this.axios.put(
|
||||||
|
process.env.VUE_APP_BASE_URL +
|
||||||
|
"/api/v1/communications/" +
|
||||||
|
this.chat.id +
|
||||||
|
"/typing"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlePaste(data) {
|
||||||
|
if (data.clipboardData.items.length) {
|
||||||
|
const item = data.clipboardData.items[0]
|
||||||
|
if (item.kind === "file") {
|
||||||
|
this.file = item.getAsFile()
|
||||||
|
this.getURLForImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
focusInput() {
|
||||||
|
const input = document.getElementById("message-input")
|
||||||
|
input.focus()
|
||||||
|
},
|
||||||
|
sendMessage() {
|
||||||
|
this.focusInput()
|
||||||
|
let message = this.message
|
||||||
|
this.message = ""
|
||||||
|
if (this.file || message.length > 0) {
|
||||||
|
const emojis = require("../lib/emojis.json")
|
||||||
|
message = message.replaceAll(
|
||||||
|
/:([a-zA-Z0-9_\-+]+):/g,
|
||||||
|
(match, group1) => {
|
||||||
|
const emoji = emojis.find((emoji) => {
|
||||||
|
return emoji.aliases.includes(group1)
|
||||||
|
})
|
||||||
|
if (emoji) {
|
||||||
|
return emoji.emoji
|
||||||
|
} else {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (!this.file) {
|
||||||
|
this.axios
|
||||||
|
.post(
|
||||||
|
process.env.VUE_APP_BASE_URL +
|
||||||
|
"/api/v1/communications/" +
|
||||||
|
this.$route.params.id +
|
||||||
|
"/message",
|
||||||
|
{
|
||||||
|
message: message,
|
||||||
|
replyId: this.replying?.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.focusInput()
|
||||||
|
this.message = ""
|
||||||
|
this.autoScroll()
|
||||||
|
this.endSend()
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
AjaxErrorHandler(this.$store)(e)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append("message", message)
|
||||||
|
if (this.replying) {
|
||||||
|
formData.append("replyId", this.replying.id)
|
||||||
|
}
|
||||||
|
formData.append("file", this.file)
|
||||||
|
this.axios
|
||||||
|
.post(
|
||||||
|
process.env.VUE_APP_BASE_URL +
|
||||||
|
"/api/v1/communications/" +
|
||||||
|
this.$route.params.id +
|
||||||
|
"/formData/message",
|
||||||
|
formData,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.focusInput()
|
||||||
|
this.message = ""
|
||||||
|
this.autoScroll()
|
||||||
|
this.endSend()
|
||||||
|
this.file = null
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
AjaxErrorHandler(this.$store)(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.edit) {
|
||||||
|
this.message = this.edit.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.no-focus:focus::before {
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,6 +3,39 @@
|
||||||
<v-overlay :value="!$store.state.wsConnected" absolute>
|
<v-overlay :value="!$store.state.wsConnected" absolute>
|
||||||
<v-progress-circular indeterminate size="64"></v-progress-circular>
|
<v-progress-circular indeterminate size="64"></v-progress-circular>
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
|
<NicknameDialog :nickname="nickname" />
|
||||||
|
<v-menu
|
||||||
|
v-model="context.user.value"
|
||||||
|
:position-x="context.user.x"
|
||||||
|
:position-y="context.user.y"
|
||||||
|
absolute
|
||||||
|
offset-y
|
||||||
|
class="rounded-l"
|
||||||
|
>
|
||||||
|
<v-list class="rounded-l" v-if="context.user.item">
|
||||||
|
<v-list-item
|
||||||
|
v-if="context.user.item.type === 'direct'"
|
||||||
|
@click="
|
||||||
|
nickname.user = context.user.item
|
||||||
|
nickname.dialog = true
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<v-list-item-title>Change Friend Nickname</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item @click="groupSettings(context.user.id)">
|
||||||
|
<v-list-item-title>Group Settings</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item
|
||||||
|
@click="
|
||||||
|
leave.item = context.user.item
|
||||||
|
leave.dialog = true
|
||||||
|
"
|
||||||
|
color="error"
|
||||||
|
>
|
||||||
|
<v-list-item-title>Leave Group</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
<v-dialog
|
<v-dialog
|
||||||
v-model="settings.addMembers.dialog"
|
v-model="settings.addMembers.dialog"
|
||||||
max-width="400px"
|
max-width="400px"
|
||||||
|
@ -263,6 +296,38 @@
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
<v-app-bar app color="bg">
|
<v-app-bar app color="bg">
|
||||||
|
<v-app-bar-nav-icon
|
||||||
|
@click.stop="$store.state.drawer = !$store.state.drawer"
|
||||||
|
v-if="$vuetify.breakpoint.mobile"
|
||||||
|
></v-app-bar-nav-icon>
|
||||||
|
<button
|
||||||
|
style="display: none"
|
||||||
|
v-shortkey="['ctrl', 'k']"
|
||||||
|
@shortkey="$store.commit('setSearch', true)"
|
||||||
|
>
|
||||||
|
Debug
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
style="display: none"
|
||||||
|
v-shortkey="['meta', 'k']"
|
||||||
|
@shortkey="$store.commit('setSearch', true)"
|
||||||
|
>
|
||||||
|
Debug
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
style="display: none"
|
||||||
|
v-shortkey="['ctrl', 'alt', 'd']"
|
||||||
|
@shortkey="$store.dispatch('toggleCSS')"
|
||||||
|
>
|
||||||
|
Style Toggle
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
style="display: none"
|
||||||
|
v-shortkey="['f9']"
|
||||||
|
@shortkey="$store.dispatch('toggleCSS')"
|
||||||
|
>
|
||||||
|
Style Toggle
|
||||||
|
</button>
|
||||||
<template v-if="$route.name === 'Communications'">
|
<template v-if="$route.name === 'Communications'">
|
||||||
<v-toolbar-title v-if="$store.state.selectedChat?.chat?.type">
|
<v-toolbar-title v-if="$store.state.selectedChat?.chat?.type">
|
||||||
{{
|
{{
|
||||||
|
@ -271,7 +336,6 @@
|
||||||
: $store.state.selectedChat?.chat?.name
|
: $store.state.selectedChat?.chat?.name
|
||||||
}}
|
}}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
|
@ -279,11 +343,7 @@
|
||||||
>
|
>
|
||||||
<v-icon>mdi-magnify</v-icon>
|
<v-icon>mdi-magnify</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn icon @click="$store.state.userPanel = !$store.state.userPanel">
|
||||||
icon
|
|
||||||
@click="$store.state.userPanel = !$store.state.userPanel"
|
|
||||||
v-if="selected?.chat?.type === 'group'"
|
|
||||||
>
|
|
||||||
<v-icon>mdi-account-group</v-icon>
|
<v-icon>mdi-account-group</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
|
@ -293,148 +353,147 @@
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
</template>
|
</template>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-navigation-drawer color="bg" floating app style="max-height: 100%">
|
<v-navigation-drawer
|
||||||
|
color="bg"
|
||||||
|
floating
|
||||||
|
app
|
||||||
|
style="max-height: 100%"
|
||||||
|
v-model="$store.state.drawer"
|
||||||
|
:width="$vuetify.breakpoint.mobile ? 270 : 320"
|
||||||
|
>
|
||||||
<v-container>
|
<v-container>
|
||||||
<v-btn
|
<v-list dense nav id="comms-sidebar-list">
|
||||||
color="toolbar"
|
<template v-if="$vuetify.breakpoint.mobile">
|
||||||
to="/communications/friends"
|
<v-btn
|
||||||
block
|
color="toolbar"
|
||||||
class="mb-3 rounded-xl"
|
to="/communications/friends"
|
||||||
elevation="2"
|
width="48%"
|
||||||
>
|
class="mb-3 mr-1 rounded-xl"
|
||||||
<v-icon left>mdi-account-multiple</v-icon>
|
elevation="2"
|
||||||
Friends
|
>
|
||||||
</v-btn>
|
<v-icon left>mdi-account-multiple</v-icon>
|
||||||
<v-text-field
|
Friends
|
||||||
class="rounded-xl"
|
</v-btn>
|
||||||
filled
|
<v-btn
|
||||||
solo
|
color="toolbar"
|
||||||
label="Search..."
|
width="48%"
|
||||||
append-icon="mdi-magnify"
|
class="mb-3 ml-1 rounded-xl"
|
||||||
background-color="toolbar"
|
elevation="2"
|
||||||
style="margin-bottom: -18px"
|
@click="dialogs.new = true"
|
||||||
elevation="2"
|
>
|
||||||
></v-text-field>
|
<v-icon left>mdi-plus</v-icon>
|
||||||
<v-toolbar color="toolbar" class="rounded-xl mb-3" elevation="2">
|
New
|
||||||
<v-toolbar-title class="subtitle-1">
|
</v-btn>
|
||||||
CHATS ({{ $store.state.chats.length }})
|
</template>
|
||||||
</v-toolbar-title>
|
<template v-else>
|
||||||
<v-spacer></v-spacer>
|
<v-btn
|
||||||
<v-btn icon @click="dialogs.new = true">
|
color="toolbar"
|
||||||
<v-icon>mdi-plus</v-icon>
|
to="/communications/friends"
|
||||||
</v-btn>
|
block
|
||||||
</v-toolbar>
|
class="mb-3 rounded-xl"
|
||||||
<v-card height="100%" color="transparent" class="mt-2" elevation="0">
|
elevation="2"
|
||||||
<v-list height="100%" two-line color="transparent" elevation="0">
|
>
|
||||||
<v-list-item-group v-model="selected">
|
<v-icon left>mdi-account-multiple</v-icon>
|
||||||
<template v-for="(item, index) in $store.state.chats">
|
Friends
|
||||||
<v-list-item
|
</v-btn>
|
||||||
:key="item.title"
|
<v-text-field
|
||||||
:to="'/communications/' + item.id"
|
class="rounded-xl"
|
||||||
|
filled
|
||||||
|
solo
|
||||||
|
label="Search..."
|
||||||
|
append-icon="mdi-magnify"
|
||||||
|
background-color="toolbar"
|
||||||
|
style="margin-bottom: -18px"
|
||||||
|
elevation="2"
|
||||||
|
></v-text-field>
|
||||||
|
<v-toolbar color="toolbar" class="rounded-xl mb-3" elevation="2">
|
||||||
|
<v-toolbar-title class="subtitle-1">
|
||||||
|
CHATS ({{ $store.state.chats.length }})
|
||||||
|
</v-toolbar-title>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn icon @click="dialogs.new = true">
|
||||||
|
<v-icon>mdi-plus</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-toolbar></template
|
||||||
|
>
|
||||||
|
<v-list v-for="item in $store.state.chats" :key="item.id">
|
||||||
|
<template>
|
||||||
|
<v-list-item
|
||||||
|
:to="'/communications/' + item.id"
|
||||||
|
@contextmenu="
|
||||||
|
show($event, 'user', getDirectRecipient(item), item.id)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<v-badge
|
||||||
|
bordered
|
||||||
|
bottom
|
||||||
|
:color="getStatus(item)"
|
||||||
|
v-if="item.chat.type === 'direct'"
|
||||||
|
dot
|
||||||
|
offset-x="24"
|
||||||
|
offset-y="20"
|
||||||
>
|
>
|
||||||
<v-badge
|
<v-list-item-avatar
|
||||||
bordered
|
:color="$vuetify.theme.themes.dark.primary"
|
||||||
bottom
|
|
||||||
:color="getStatus(item)"
|
|
||||||
v-if="item.chat.type === 'direct'"
|
|
||||||
dot
|
|
||||||
offset-x="24"
|
|
||||||
offset-y="26"
|
|
||||||
>
|
>
|
||||||
<v-list-item-avatar
|
<v-icon v-if="item.chat.type === 'group'">
|
||||||
:color="$vuetify.theme.themes.dark.primary"
|
mdi-account-group
|
||||||
>
|
</v-icon>
|
||||||
<v-icon v-if="item.chat.type === 'group'">
|
<v-img
|
||||||
mdi-account-group
|
v-else-if="
|
||||||
</v-icon>
|
item.chat.type === 'direct' &&
|
||||||
<v-img
|
getDirectRecipient(item).avatar
|
||||||
v-else-if="
|
"
|
||||||
item.chat.type === 'direct' &&
|
:src="
|
||||||
getDirectRecipient(item).avatar
|
$store.state.baseURL +
|
||||||
"
|
'/usercontent/' +
|
||||||
:src="'/usercontent/' + getDirectRecipient(item).avatar"
|
getDirectRecipient(item).avatar
|
||||||
/>
|
"
|
||||||
<v-icon v-else-if="item.chat.type === 'direct'">
|
/>
|
||||||
mdi-account
|
<v-icon v-else-if="item.chat.type === 'direct'">
|
||||||
</v-icon>
|
mdi-account
|
||||||
</v-list-item-avatar>
|
</v-icon>
|
||||||
</v-badge>
|
</v-list-item-avatar>
|
||||||
<v-badge dot color="none" v-else>
|
</v-badge>
|
||||||
<v-list-item-avatar
|
<v-badge dot color="none" v-else>
|
||||||
:color="$vuetify.theme.themes.dark.primary"
|
<v-list-item-avatar
|
||||||
>
|
:color="$vuetify.theme.themes.dark.primary"
|
||||||
<v-icon v-if="item.chat.type === 'group'">
|
>
|
||||||
mdi-account-group
|
<v-icon v-if="item.chat.type === 'group'">
|
||||||
</v-icon>
|
mdi-account-group
|
||||||
</v-list-item-avatar>
|
</v-icon>
|
||||||
</v-badge>
|
</v-list-item-avatar>
|
||||||
<template>
|
</v-badge>
|
||||||
<v-list-item-content>
|
<template>
|
||||||
<v-list-item-title v-if="item.chat.type === 'direct'">
|
<v-list-item-content>
|
||||||
{{ getDirectRecipient(item).username }}
|
<v-list-item-title v-if="item.chat.type === 'direct'">
|
||||||
<v-badge
|
{{ getDirectRecipient(item).name }}
|
||||||
v-if="
|
</v-list-item-title>
|
||||||
getLastRead(item).count >= 1 &&
|
<v-list-item-title v-else>
|
||||||
$route.params.id !== item.id.toString()
|
<span> {{ item.chat.name }} </span>
|
||||||
"
|
</v-list-item-title>
|
||||||
color="red"
|
|
||||||
class="ml-2"
|
|
||||||
:content="getLastRead(item).count"
|
|
||||||
>
|
|
||||||
</v-badge>
|
|
||||||
</v-list-item-title>
|
|
||||||
<v-list-item-title v-else>
|
|
||||||
{{ item.chat.name }}
|
|
||||||
<v-badge
|
|
||||||
v-if="
|
|
||||||
getLastRead(item).count >= 1 &&
|
|
||||||
$route.params.id !== item.id.toString()
|
|
||||||
"
|
|
||||||
color="red"
|
|
||||||
class="ml-2"
|
|
||||||
:content="getLastRead(item).count"
|
|
||||||
>
|
|
||||||
</v-badge>
|
|
||||||
</v-list-item-title>
|
|
||||||
|
|
||||||
<v-list-item-subtitle v-if="item.chat.type === 'group'">
|
<v-list-item-subtitle v-if="item.chat.type === 'group'">
|
||||||
{{ item.chat.users.length }} Members
|
{{ item.chat.users.length }} Members
|
||||||
</v-list-item-subtitle>
|
</v-list-item-subtitle>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
<v-list-item-action>
|
<v-list-item-action
|
||||||
<v-icon
|
v-if="
|
||||||
@click="
|
item.unread >= 1 &&
|
||||||
leave.item = item
|
$route.params.id !== item.id.toString()
|
||||||
leave.dialog = true
|
"
|
||||||
"
|
>
|
||||||
>
|
<v-badge color="red" inline :content="item.unread">
|
||||||
mdi-exit-to-app
|
</v-badge>
|
||||||
</v-icon>
|
</v-list-item-action>
|
||||||
</v-list-item-action>
|
</template>
|
||||||
<v-list-item-action>
|
</v-list-item>
|
||||||
<v-icon
|
</template>
|
||||||
@click="
|
|
||||||
settings.item = item
|
|
||||||
settings.dialog = true
|
|
||||||
"
|
|
||||||
>
|
|
||||||
mdi-cog
|
|
||||||
</v-icon>
|
|
||||||
</v-list-item-action>
|
|
||||||
</template>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<v-divider
|
|
||||||
v-if="index < $store.state.chats.length - 1"
|
|
||||||
:key="index"
|
|
||||||
></v-divider>
|
|
||||||
</template>
|
|
||||||
</v-list-item-group>
|
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-card>
|
</v-list>
|
||||||
</v-container>
|
</v-container>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-card tile color="card" elevation="0">
|
<v-card tile color="bg" elevation="0">
|
||||||
<v-overlay :value="!$store.state.wsConnected" absolute>
|
<v-overlay :value="!$store.state.wsConnected" absolute>
|
||||||
<v-progress-circular indeterminate size="48"></v-progress-circular>
|
<v-progress-circular indeterminate size="48"></v-progress-circular>
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
|
@ -522,11 +581,19 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AjaxErrorHandler from "@/lib/errorHandler"
|
import AjaxErrorHandler from "@/lib/errorHandler"
|
||||||
|
import NicknameDialog from "@/components/NicknameDialog"
|
||||||
|
import Vue from "vue"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Header",
|
name: "Header",
|
||||||
|
components: { NicknameDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
nickname: {
|
||||||
|
dialog: false,
|
||||||
|
nickname: "",
|
||||||
|
user: {}
|
||||||
|
},
|
||||||
copyTooltip: false,
|
copyTooltip: false,
|
||||||
settings: {
|
settings: {
|
||||||
dialog: false,
|
dialog: false,
|
||||||
|
@ -538,6 +605,28 @@ export default {
|
||||||
},
|
},
|
||||||
item: null
|
item: null
|
||||||
},
|
},
|
||||||
|
context: {
|
||||||
|
user: {
|
||||||
|
value: false,
|
||||||
|
x: null,
|
||||||
|
y: null,
|
||||||
|
item: null,
|
||||||
|
id: 0
|
||||||
|
},
|
||||||
|
userPopout: {
|
||||||
|
value: false,
|
||||||
|
x: null,
|
||||||
|
y: null,
|
||||||
|
item: null,
|
||||||
|
id: 0
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
value: false,
|
||||||
|
x: null,
|
||||||
|
y: null,
|
||||||
|
item: null
|
||||||
|
}
|
||||||
|
},
|
||||||
selected: [2],
|
selected: [2],
|
||||||
loading: true,
|
loading: true,
|
||||||
leave: {
|
leave: {
|
||||||
|
@ -556,6 +645,23 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
groupSettings(id) {
|
||||||
|
this.settings.item = this.$store.state.chats.find(
|
||||||
|
(chat) => chat.id === id
|
||||||
|
)
|
||||||
|
this.settings.dialog = true
|
||||||
|
},
|
||||||
|
show(e, context, item, id) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.context[context].value = false
|
||||||
|
this.context[context].x = e.clientX
|
||||||
|
this.context[context].y = e.clientY
|
||||||
|
this.context[context].item = item
|
||||||
|
this.context[context].id = id
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.context[context].value = true
|
||||||
|
})
|
||||||
|
},
|
||||||
setStatus(status) {
|
setStatus(status) {
|
||||||
const previousStatus = {
|
const previousStatus = {
|
||||||
status: this.$store.state.user.status,
|
status: this.$store.state.user.status,
|
||||||
|
@ -740,13 +846,30 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getDirectRecipient(item) {
|
getDirectRecipient(item) {
|
||||||
const user = item.chat.users.find(
|
let user = item.chat.users.find(
|
||||||
(user) => user.id !== this.$store.state.user.id
|
(user) => user.id !== this.$store.state.user.id
|
||||||
)
|
)
|
||||||
if (user) {
|
if (user) {
|
||||||
return user
|
if (user.nickname?.nickname) {
|
||||||
|
user.name = user.nickname.nickname
|
||||||
|
} else {
|
||||||
|
user.name = user.username
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
type: item.chat.type
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return item.chat.users[0]
|
let user = item.chat.users[0]
|
||||||
|
if (user.nickname?.nickname) {
|
||||||
|
user.name = user.nickname.nickname
|
||||||
|
} else {
|
||||||
|
user.name = user.username
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
type: item.chat.type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createConversation() {
|
createConversation() {
|
||||||
|
@ -756,7 +879,6 @@ export default {
|
||||||
users: this.newConversation.users
|
users: this.newConversation.users
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.getChats()
|
|
||||||
this.newConversation.name = ""
|
this.newConversation.name = ""
|
||||||
this.newConversation.users = []
|
this.newConversation.users = []
|
||||||
this.newConversation.loading = false
|
this.newConversation.loading = false
|
||||||
|
@ -774,10 +896,10 @@ export default {
|
||||||
searchUsers() {
|
searchUsers() {
|
||||||
this.newConversation.loading = true
|
this.newConversation.loading = true
|
||||||
this.axios
|
this.axios
|
||||||
.get("/api/v1/communications/search?query=" + this.newConversation.name)
|
.get("/api/v1/communications/search?query=")
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.newConversation.loading = false
|
this.newConversation.loading = false
|
||||||
this.newConversation.results.push(...res.data)
|
this.newConversation.results = res.data
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.newConversation.loading = false
|
this.newConversation.loading = false
|
||||||
|
@ -785,12 +907,10 @@ export default {
|
||||||
},
|
},
|
||||||
searchUsersForGroupAdmin() {
|
searchUsersForGroupAdmin() {
|
||||||
this.axios
|
this.axios
|
||||||
.get(
|
.get("/api/v1/communications/search?query=")
|
||||||
"/api/v1/communications/search?query=" + this.settings.addMembers.name
|
|
||||||
)
|
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (this.settings.item) {
|
if (this.settings.item) {
|
||||||
this.settings.addMembers.results.push(...res.data)
|
this.settings.addMembers.results = res.data
|
||||||
this.settings.addMembers.results =
|
this.settings.addMembers.results =
|
||||||
this.settings.addMembers.results.filter(
|
this.settings.addMembers.results.filter(
|
||||||
(user) =>
|
(user) =>
|
||||||
|
@ -802,6 +922,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
Vue.axios.defaults.headers.common["Authorization"] =
|
||||||
|
localStorage.getItem("session")
|
||||||
this.searchUsers()
|
this.searchUsers()
|
||||||
this.searchUsersForGroupAdmin()
|
this.searchUsersForGroupAdmin()
|
||||||
this.$store.dispatch("getChats")
|
this.$store.dispatch("getChats")
|
||||||
|
@ -834,7 +956,6 @@ export default {
|
||||||
)
|
)
|
||||||
if (chat) {
|
if (chat) {
|
||||||
const index = this.$store.state.chats.indexOf(chat)
|
const index = this.$store.state.chats.indexOf(chat)
|
||||||
this.$store.state.chats[index].chat.lastMessages.unshift(message)
|
|
||||||
this.$store.state.chats.splice(index, 1)
|
this.$store.state.chats.splice(index, 1)
|
||||||
this.$store.state.chats.unshift(chat)
|
this.$store.state.chats.unshift(chat)
|
||||||
}
|
}
|
||||||
|
|
84
src/components/NicknameDialog.vue
Normal file
84
src/components/NicknameDialog.vue
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<v-dialog v-model="nickname.dialog" width="500px">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>
|
||||||
|
<span class="headline">{{ nickname.user.username }}</span>
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-text-field
|
||||||
|
v-model="nickname.nickname"
|
||||||
|
label="Nickname"
|
||||||
|
required
|
||||||
|
@keyup.enter="setFriendNickname"
|
||||||
|
></v-text-field>
|
||||||
|
<small>Friend nicknames only show to you.</small>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn
|
||||||
|
color="blue darken-1"
|
||||||
|
text
|
||||||
|
@click="
|
||||||
|
nickname.dialog = false
|
||||||
|
nickname.nickname = ''
|
||||||
|
nickname.user = {}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="blue darken-1" text @click="setFriendNickname">
|
||||||
|
Apply
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AjaxErrorHandler from "@/lib/errorHandler"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "NicknameDialog",
|
||||||
|
props: ["nickname"],
|
||||||
|
methods: {
|
||||||
|
setFriendNickname() {
|
||||||
|
this.axios
|
||||||
|
.post(
|
||||||
|
process.env.VUE_APP_BASE_URL +
|
||||||
|
"/api/v1/communications/nickname/" +
|
||||||
|
this.nickname.user.id,
|
||||||
|
{
|
||||||
|
nickname: this.nickname.nickname
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
this.$store.state.chats.forEach((item) => {
|
||||||
|
item.chat.associations.forEach((a) => {
|
||||||
|
if (a.user.id === this.nickname.user.id) {
|
||||||
|
a.user.nickname = {
|
||||||
|
nickname: res.data.nickname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
item.chat.users.forEach((u) => {
|
||||||
|
if (u.id === this.nickname.user.id) {
|
||||||
|
u.nickname = {
|
||||||
|
nickname: res.data.nickname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.nickname.dialog = false
|
||||||
|
this.nickname.nickname = ""
|
||||||
|
this.nickname.user = {}
|
||||||
|
this.$toast.success("Nickname changed successfully.")
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
AjaxErrorHandler(this.$store)(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -2,8 +2,14 @@ module.exports = function (vuex) {
|
||||||
return function (res, ignorePathErrorCb) {
|
return function (res, ignorePathErrorCb) {
|
||||||
let errors = []
|
let errors = []
|
||||||
|
|
||||||
if (res.response === undefined || res.response.data.errors === undefined) {
|
if (
|
||||||
if (res.response.status === 429) {
|
res?.response === undefined ||
|
||||||
|
res?.response?.data?.errors === undefined
|
||||||
|
) {
|
||||||
|
if (vuex.state.site.release === "dev") {
|
||||||
|
console.error(res)
|
||||||
|
}
|
||||||
|
if (res?.response?.status === 429) {
|
||||||
vuex._vm.$toast.error(
|
vuex._vm.$toast.error(
|
||||||
"You are being rate limited, retry in " +
|
"You are being rate limited, retry in " +
|
||||||
res.response.headers["ratelimit-reset"] +
|
res.response.headers["ratelimit-reset"] +
|
||||||
|
|
|
@ -4,17 +4,37 @@ import Vuetify from "../plugins/vuetify"
|
||||||
import AjaxErrorHandler from "@/lib/errorHandler"
|
import AjaxErrorHandler from "@/lib/errorHandler"
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
function getDirectRecipient(context, item) {
|
||||||
|
let user = item.chat.users.find((user) => user.id !== context.state.user.id)
|
||||||
|
if (user) {
|
||||||
|
if (user.nickname?.nickname) {
|
||||||
|
user.name = user.nickname.nickname
|
||||||
|
} else {
|
||||||
|
user.name = user.username
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
} else {
|
||||||
|
let user = item.chat.users[0]
|
||||||
|
if (user.nickname?.nickname) {
|
||||||
|
user.name = user.nickname.nickname
|
||||||
|
} else {
|
||||||
|
user.name = user.username
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
}
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
online: true,
|
online: true,
|
||||||
selectedChat: null,
|
selectedChat: null,
|
||||||
chats: [],
|
chats: [],
|
||||||
|
baseURL: process.env.VUE_APP_BASE_URL,
|
||||||
versioning: {
|
versioning: {
|
||||||
date: process.env.VUE_APP_BUILD_DATE,
|
date: process.env.VUE_APP_BUILD_DATE,
|
||||||
version: process.env.VUE_APP_VERSION,
|
version: process.env.VUE_APP_VERSION,
|
||||||
release: process.env.RELEASE
|
release: process.env.RELEASE
|
||||||
},
|
},
|
||||||
|
drawer: true,
|
||||||
site: {
|
site: {
|
||||||
release: "stable",
|
release: "stable",
|
||||||
loading: true
|
loading: true
|
||||||
|
@ -97,7 +117,7 @@ export default new Vuex.Store({
|
||||||
state.modals.search = value
|
state.modals.search = value
|
||||||
},
|
},
|
||||||
addChat(state, chat) {
|
addChat(state, chat) {
|
||||||
state.chats.push(chat)
|
state.chats.unshift(chat)
|
||||||
},
|
},
|
||||||
updateQuickSwitchCache(state, value) {
|
updateQuickSwitchCache(state, value) {
|
||||||
state.quickSwitchCache.push({
|
state.quickSwitchCache.push({
|
||||||
|
@ -119,34 +139,16 @@ export default new Vuex.Store({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getCommunicationsUnread(context) {
|
getCommunicationsUnread(context) {
|
||||||
Vue.axios.defaults.headers.common["Authorization"] =
|
Vue.axios
|
||||||
localStorage.getItem("session")
|
.get(process.env.VUE_APP_BASE_URL + "/api/v1/communications")
|
||||||
Vue.axios.get("/api/v1/communications").then((res) => {
|
.then((res) => {
|
||||||
context.state.communicationNotifications = 0
|
context.commit("setChats", res.data)
|
||||||
res.data.forEach((item) => {
|
context.dispatch("updateQuickSwitch")
|
||||||
const message = item.chat.lastMessages.find(
|
context.state.communicationNotifications = 0
|
||||||
(message) => message.id === item.lastRead
|
res.data.forEach((item) => {
|
||||||
)
|
context.state.communicationNotifications += item.unread
|
||||||
const lastMessage = item.chat.lastMessages.find(
|
})
|
||||||
(message) => message.userId !== context.state.user.id
|
|
||||||
)
|
|
||||||
let count
|
|
||||||
if (message && lastMessage) {
|
|
||||||
const index = item.chat.lastMessages.indexOf(message)
|
|
||||||
const indexLast = item.chat.lastMessages.indexOf(lastMessage)
|
|
||||||
let value = index - indexLast
|
|
||||||
if (value < 0) {
|
|
||||||
value = 0
|
|
||||||
}
|
|
||||||
count = value
|
|
||||||
} else if (!message) {
|
|
||||||
count = item.chat.lastMessages.length
|
|
||||||
} else {
|
|
||||||
count = 0
|
|
||||||
}
|
|
||||||
context.state.communicationNotifications += count
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
discardTheme(context) {
|
discardTheme(context) {
|
||||||
context.state.themeEngine.theme = {
|
context.state.themeEngine.theme = {
|
||||||
|
@ -323,9 +325,12 @@ export default new Vuex.Store({
|
||||||
})
|
})
|
||||||
context.state.chats.forEach((chat) => {
|
context.state.chats.forEach((chat) => {
|
||||||
context.commit("updateQuickSwitchCache", {
|
context.commit("updateQuickSwitchCache", {
|
||||||
...chat,
|
id: chat.id,
|
||||||
customType: 2,
|
subjectLongName:
|
||||||
route: "/communications/" + chat.id
|
chat.chat.type === "group"
|
||||||
|
? chat.chat.name
|
||||||
|
: getDirectRecipient(context, chat).name,
|
||||||
|
customType: 3
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -21,16 +21,13 @@
|
||||||
<v-list-item-avatar
|
<v-list-item-avatar
|
||||||
@click="userProfile(user)"
|
@click="userProfile(user)"
|
||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
|
:color="$vuetify.theme.themes.dark.primary"
|
||||||
>
|
>
|
||||||
<v-list-item-avatar
|
<v-img
|
||||||
:color="$vuetify.theme.themes.dark.primary"
|
:src="'/usercontent/' + user.avatar"
|
||||||
>
|
v-if="user.avatar"
|
||||||
<v-img
|
/>
|
||||||
:src="'/usercontent/' + user.avatar"
|
<v-icon v-else> mdi-account </v-icon>
|
||||||
v-if="user.avatar"
|
|
||||||
/>
|
|
||||||
<v-icon v-else> mdi-account </v-icon>
|
|
||||||
</v-list-item-avatar>
|
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
|
|
||||||
<v-list-item-content
|
<v-list-item-content
|
||||||
|
@ -90,16 +87,15 @@
|
||||||
v-for="friend in computePendingIncoming"
|
v-for="friend in computePendingIncoming"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
>
|
>
|
||||||
<v-list-item-avatar @click="userProfile(friend.user2)">
|
<v-list-item-avatar
|
||||||
<v-list-item-avatar
|
@click="userProfile(friend.user2)"
|
||||||
:color="$vuetify.theme.themes.dark.primary"
|
:color="$vuetify.theme.themes.dark.primary"
|
||||||
>
|
>
|
||||||
<img
|
<v-img
|
||||||
:src="'/usercontent/' + friend.user2.avatar"
|
:src="'/usercontent/' + friend.user2.avatar"
|
||||||
v-if="friend.user2.avatar"
|
v-if="friend.user2.avatar"
|
||||||
/>
|
/>
|
||||||
<v-icon v-else> mdi-account </v-icon>
|
<v-icon v-else> mdi-account </v-icon>
|
||||||
</v-list-item-avatar>
|
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
|
|
||||||
<v-list-item-content @click="userProfile(friend.user2)">
|
<v-list-item-content @click="userProfile(friend.user2)">
|
||||||
|
@ -136,16 +132,13 @@
|
||||||
<v-list-item-avatar
|
<v-list-item-avatar
|
||||||
@click="userProfile(friend.user2)"
|
@click="userProfile(friend.user2)"
|
||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
|
:color="$vuetify.theme.themes.dark.primary"
|
||||||
>
|
>
|
||||||
<v-list-item-avatar
|
<v-img
|
||||||
:color="$vuetify.theme.themes.dark.primary"
|
:src="'/usercontent/' + friend.user2.avatar"
|
||||||
>
|
v-if="friend.user2.avatar"
|
||||||
<img
|
/>
|
||||||
:src="'/usercontent/' + friend.user2.avatar"
|
<v-icon v-else> mdi-account </v-icon>
|
||||||
v-if="friend.user2.avatar"
|
|
||||||
/>
|
|
||||||
<v-icon v-else> mdi-account </v-icon>
|
|
||||||
</v-list-item-avatar>
|
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
|
|
||||||
<v-list-item-content
|
<v-list-item-content
|
||||||
|
@ -175,16 +168,13 @@
|
||||||
<v-list-item-avatar
|
<v-list-item-avatar
|
||||||
@click="userProfile(friend.user2)"
|
@click="userProfile(friend.user2)"
|
||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
|
:color="$vuetify.theme.themes.dark.primary"
|
||||||
>
|
>
|
||||||
<v-list-item-avatar
|
<v-img
|
||||||
:color="$vuetify.theme.themes.dark.primary"
|
:src="'/usercontent/' + friend.user2.avatar"
|
||||||
>
|
v-if="friend.user2.avatar"
|
||||||
<img
|
/>
|
||||||
:src="'/usercontent/' + friend.user2.avatar"
|
<v-icon v-else> mdi-account </v-icon>
|
||||||
v-if="friend.user2.avatar"
|
|
||||||
/>
|
|
||||||
<v-icon v-else> mdi-account </v-icon>
|
|
||||||
</v-list-item-avatar>
|
|
||||||
</v-list-item-avatar>
|
</v-list-item-avatar>
|
||||||
|
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
|
|
|
@ -118,6 +118,7 @@ export default {
|
||||||
this.$store.commit("setToken", res.data.session)
|
this.$store.commit("setToken", res.data.session)
|
||||||
await this.$store.dispatch("getUserInfo")
|
await this.$store.dispatch("getUserInfo")
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
this.$socket.disconnect()
|
||||||
this.$socket.connect()
|
this.$socket.connect()
|
||||||
this.$router.push("/")
|
this.$router.push("/")
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,27 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="login" v-if="!$store.state.user?.bcUser?.id">
|
<div id="login" v-if="!$store.state.user?.bcUser?.id">
|
||||||
<v-dialog v-model="totpDialog" max-width="500px">
|
|
||||||
<v-card color="card">
|
|
||||||
<v-toolbar color="toolbar">
|
|
||||||
<v-toolbar-title> Two-Factor Authentication </v-toolbar-title>
|
|
||||||
</v-toolbar>
|
|
||||||
<v-container>
|
|
||||||
<v-card-text
|
|
||||||
>You are seeing this because you have enabled Two-Factor
|
|
||||||
Authentication on BetterCompass.<br />
|
|
||||||
Please check your phone, or authenticator app to obtain the 6 digit
|
|
||||||
code and enter it here.</v-card-text
|
|
||||||
>
|
|
||||||
<v-otp-input
|
|
||||||
length="6"
|
|
||||||
@keyup.enter="doLogin()"
|
|
||||||
@finish="doLogin()"
|
|
||||||
v-model="totp"
|
|
||||||
:disabled="loading"
|
|
||||||
></v-otp-input>
|
|
||||||
</v-container>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
<div :class="{ outer: !$vuetify.breakpoint.mobile }">
|
<div :class="{ outer: !$vuetify.breakpoint.mobile }">
|
||||||
<div :class="{ middle: !$vuetify.breakpoint.mobile }">
|
<div :class="{ middle: !$vuetify.breakpoint.mobile }">
|
||||||
<div :class="{ innerLogin: !$vuetify.breakpoint.mobile }">
|
<div :class="{ innerLogin: !$vuetify.breakpoint.mobile }">
|
||||||
|
@ -121,6 +99,7 @@ export default {
|
||||||
this.$store.commit("setToken", res.data.session)
|
this.$store.commit("setToken", res.data.session)
|
||||||
await this.$store.dispatch("getUserInfo")
|
await this.$store.dispatch("getUserInfo")
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
this.$socket.disconnect()
|
||||||
this.$socket.connect()
|
this.$socket.connect()
|
||||||
this.$router.push("/")
|
this.$router.push("/")
|
||||||
})
|
})
|
||||||
|
|
|
@ -179,12 +179,7 @@ export default {
|
||||||
sheet: "#181818",
|
sheet: "#181818",
|
||||||
text: "#000000",
|
text: "#000000",
|
||||||
dark: "#151515",
|
dark: "#151515",
|
||||||
bg: "#151515",
|
bg: "#151515"
|
||||||
calendarNormalActivity: "#3f51b5",
|
|
||||||
calendarActivityType7: "#f44336",
|
|
||||||
calendarActivityType8: "#4caf50",
|
|
||||||
calendarActivityType10: "#ff9800",
|
|
||||||
calendarExternalActivity: "#2196f3"
|
|
||||||
},
|
},
|
||||||
light: {
|
light: {
|
||||||
primary: "#0190ea",
|
primary: "#0190ea",
|
||||||
|
@ -199,68 +194,14 @@ export default {
|
||||||
sheet: "#f8f8f8",
|
sheet: "#f8f8f8",
|
||||||
text: "#000000",
|
text: "#000000",
|
||||||
dark: "#f8f8f8",
|
dark: "#f8f8f8",
|
||||||
bg: "#f8f8f8",
|
bg: "#f8f8f8"
|
||||||
calendarNormalActivity: "#3f51b5",
|
|
||||||
calendarActivityType7: "#f44336",
|
|
||||||
calendarActivityType8: "#4caf50",
|
|
||||||
calendarActivityType10: "#ff9800",
|
|
||||||
calendarExternalActivity: "#2196f3"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
createTheme: false,
|
createTheme: false,
|
||||||
createThemeCSS: false,
|
createThemeCSS: false,
|
||||||
name: "",
|
name: "",
|
||||||
creatorType: "create",
|
creatorType: "create",
|
||||||
themes: [],
|
themes: []
|
||||||
fakeEvents: [
|
|
||||||
{
|
|
||||||
name: "English",
|
|
||||||
content: "English",
|
|
||||||
color: "#dce6f4",
|
|
||||||
start: new Date().setHours(9, 0, 0, 0),
|
|
||||||
end: new Date().setHours(10, 0, 0, 0),
|
|
||||||
timed: true,
|
|
||||||
activityType: 1,
|
|
||||||
activityId: 0,
|
|
||||||
calendarId: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Maths",
|
|
||||||
content: "Maths",
|
|
||||||
color: "#f4dcdc",
|
|
||||||
start: new Date().setHours(10, 0, 0, 0),
|
|
||||||
end: new Date().setHours(11, 0, 0, 0),
|
|
||||||
timed: true,
|
|
||||||
activityType: 1,
|
|
||||||
activityId: 0,
|
|
||||||
instanceId: 0,
|
|
||||||
calendarId: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Lunch Break",
|
|
||||||
content: "Lunch Break",
|
|
||||||
color: "#dce6f4",
|
|
||||||
start: new Date().setHours(11, 0, 0, 0),
|
|
||||||
end: new Date().setHours(11, 30, 0, 0),
|
|
||||||
timed: true,
|
|
||||||
activityType: 1,
|
|
||||||
activityId: 0,
|
|
||||||
instanceId: 0,
|
|
||||||
calendarId: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Excursion",
|
|
||||||
content: "Excursion",
|
|
||||||
color: "#dce6f4",
|
|
||||||
start: new Date().setHours(11, 0, 0, 0),
|
|
||||||
end: new Date().setHours(13, 30, 0, 0),
|
|
||||||
timed: true,
|
|
||||||
activityType: 1,
|
|
||||||
activityId: 0,
|
|
||||||
instanceId: 0,
|
|
||||||
calendarId: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -308,33 +249,6 @@ div {
|
||||||
document.head.appendChild(style)
|
document.head.appendChild(style)
|
||||||
this.$store.dispatch("saveOnlineSettings")
|
this.$store.dispatch("saveOnlineSettings")
|
||||||
},
|
},
|
||||||
computeColor(event) {
|
|
||||||
if (event.color === "#003300") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarActivityType8
|
|
||||||
} else if (event.color === "#133897") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarExternalActivity
|
|
||||||
} else if (event.activityType === 7 || event.color === "#f4dcdc") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarActivityType7
|
|
||||||
} else if (event.color === "#dce6f4") {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarNormalActivity
|
|
||||||
} else if (event.activityType === 10) {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarActivityType10
|
|
||||||
} else {
|
|
||||||
return this.$vuetify.theme.themes[
|
|
||||||
this.$store.state.user.theme || "dark"
|
|
||||||
].calendarNormalActivity
|
|
||||||
}
|
|
||||||
},
|
|
||||||
editorInit() {
|
editorInit() {
|
||||||
require("brace/ext/language_tools")
|
require("brace/ext/language_tools")
|
||||||
require("brace/mode/css")
|
require("brace/mode/css")
|
||||||
|
@ -357,18 +271,7 @@ div {
|
||||||
sheet: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
sheet: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
||||||
text: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
text: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
||||||
dark: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
dark: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
||||||
bg: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
bg: "#" + Math.floor(Math.random() * 16777215).toString(16)
|
||||||
calendarNormalActivity:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarActivityType7:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarActivityType8:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarActivityType10:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarExternalActivity: Math.floor(Math.random() * 16777215).toString(
|
|
||||||
16
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
this.creator.dark = {
|
this.creator.dark = {
|
||||||
primary: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
primary: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
||||||
|
@ -383,18 +286,7 @@ div {
|
||||||
sheet: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
sheet: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
||||||
text: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
text: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
||||||
dark: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
dark: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
||||||
bg: "#" + Math.floor(Math.random() * 16777215).toString(16),
|
bg: "#" + Math.floor(Math.random() * 16777215).toString(16)
|
||||||
calendarNormalActivity:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarActivityType7:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarActivityType8:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarActivityType10:
|
|
||||||
"#" + Math.floor(Math.random() * 16777215).toString(16),
|
|
||||||
calendarExternalActivity: Math.floor(Math.random() * 16777215).toString(
|
|
||||||
16
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doDeleteTheme(theme) {
|
doDeleteTheme(theme) {
|
||||||
|
@ -500,17 +392,7 @@ div {
|
||||||
a.dispatchEvent(e)
|
a.dispatchEvent(e)
|
||||||
},
|
},
|
||||||
friendlyName(index) {
|
friendlyName(index) {
|
||||||
if (index === "calendarNormalActivity") {
|
if (index === "bg") {
|
||||||
return "Standard Class"
|
|
||||||
} else if (index === "calendarActivityType7") {
|
|
||||||
return "Relief Event"
|
|
||||||
} else if (index === "calendarActivityType8") {
|
|
||||||
return "Generic Type 8"
|
|
||||||
} else if (index === "calendarActivityType10") {
|
|
||||||
return "Learning Task"
|
|
||||||
} else if (index === "calendarExternalActivity") {
|
|
||||||
return "External Activity"
|
|
||||||
} else if (index === "bg") {
|
|
||||||
return "Background"
|
return "Background"
|
||||||
} else if (index === "dark") {
|
} else if (index === "dark") {
|
||||||
return "Sidebar & Header"
|
return "Sidebar & Header"
|
||||||
|
@ -581,7 +463,6 @@ div {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
console.log(document.querySelectorAll(".editor__toolbar"))
|
|
||||||
this.defineAccent = this.$store.state.user.accentColor !== null
|
this.defineAccent = this.$store.state.user.accentColor !== null
|
||||||
this.accent = this.$store.state.user.accentColor
|
this.accent = this.$store.state.user.accentColor
|
||||||
this.name = this.$vuetify.theme.themes.name
|
this.name = this.$vuetify.theme.themes.name
|
||||||
|
|
|
@ -1194,6 +1194,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz#a53515db25d8038374381b73af20bb4f2e508d87"
|
resolved "https://registry.yarnpkg.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz#a53515db25d8038374381b73af20bb4f2e508d87"
|
||||||
integrity sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==
|
integrity sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==
|
||||||
|
|
||||||
|
"@tauri-apps/api@^1.0.2":
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.0.2.tgz#5228720e35d50fd08df87067dc29e7306c1f7a10"
|
||||||
|
integrity sha512-yuNW0oeJ1/ZA7wNF1KgxhHrSu5viPVzY/UgUczzN5ptLM8dH15Juy5rEGkoHfeXGju90Y/l22hi3BtIrp/za+w==
|
||||||
|
|
||||||
"@types/body-parser@*":
|
"@types/body-parser@*":
|
||||||
version "1.19.2"
|
version "1.19.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
|
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
|
||||||
|
|
Loading…
Reference in a new issue