feat: Implement core chat application UI, including chat, voice, members, DMs, and shared components.
Some checks failed
Build and Release / build-and-release (push) Failing after 0s
Some checks failed
Build and Release / build-and-release (push) Failing after 0s
This commit is contained in:
@@ -33,7 +33,8 @@
|
||||
"Bash(npm ls:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git push:*)"
|
||||
"Bash(git push:*)",
|
||||
"Bash(npm run build:web:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
21
TODO.md
21
TODO.md
@@ -32,14 +32,25 @@
|
||||
|
||||
<!-- - Make people type passwords twice to make sure they dont mess up typing their password for registration. -->
|
||||
|
||||
|
||||
|
||||
<!-- How can we save user preferences for the app like individual user volumes, the position and size they have the floating stream popout, if they have categories collaped, the last channel they were in so we can open that channel when they open the app, etc. -->
|
||||
|
||||
Can we make sure Voice and Video work. We have the users input and output devices but if i select any it dosent show it changed. I want to make sure that the users can select their input and output devices and that it works for livekit.
|
||||
|
||||
<!-- - Lets make it so we can upload a custom image for the server that will show on the sidebar. Make the image editing like how we do it for avatars but instead of a circle that we have to show users cut off its a square with a border radius, match it to the boarder radius of the server-item-wrapper -->
|
||||
|
||||
|
||||
- On mobile. lets redo the settings page to be more mobile friendly. I want it to look exactly the same on desktop but i need a little more mobile friendly for mobile.
|
||||
|
||||
What about the gitea workflow. Does that build the electron app still with the new setup? I dont need the android build right now just the same functionality as before with building for electron.
|
||||
<!-- - WHen i go idle and then come back sometimes it dosent show im online again, or maybe im idle and close the app and relaunch it, it says im idle still and i have to manualy change to online. -->
|
||||
|
||||
- Lets make a Popup menu on chat input to paste image or anything from clipboard. So its on option called "Paste". Should only popup if you right click the chat input box.
|
||||
|
||||
<!-- - If app is in the background or minimized it dosent show im online shows im offline -->
|
||||
|
||||
<!-- - When someone is sharing their screen and we are getting their audio lets ignore any sounds coming from our app. -->
|
||||
|
||||
<!-- - Custom intro sounds repeat when another user joins the chat even though the user with that custom sound didnt join and is already in voice chat.
|
||||
|
||||
- In the user options when you right click on a user in voice we should have the option to disconnect user from voice channel -->
|
||||
|
||||
<!-- - Can we Add volume slider for screenshare audio, and add a fullscreen button for screenshare, where we fullscreen the voice stage. -->
|
||||
|
||||
- Can we add a way to tell the user they are connecting to voice. Like show them its connecting so the user knows something is happening instead of them clicking on the voice stage again and again.
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@discord-clone/electron",
|
||||
"private": true,
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.16",
|
||||
"description": "Discord Clone - Electron app",
|
||||
"author": "Moyettes",
|
||||
"type": "module",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<title>Discord Clone</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
383
convex/auth.js
Normal file
383
convex/auth.js
Normal file
@@ -0,0 +1,383 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.updateStatus = exports.getMyJoinSoundUrl = exports.updateProfile = exports.getPublicKeys = exports.createUserWithProfile = exports.verifyUser = exports.getSalt = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var storageUrl_1 = require("./storageUrl");
|
||||
function sha256Hex(input) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var buffer;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, crypto.subtle.digest("SHA-256", new TextEncoder().encode(input))];
|
||||
case 1:
|
||||
buffer = _a.sent();
|
||||
return [2 /*return*/, Array.from(new Uint8Array(buffer))
|
||||
.map(function (b) { return b.toString(16).padStart(2, "0"); })
|
||||
.join("")];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// Get salt for a username (returns fake salt for non-existent users)
|
||||
exports.getSalt = (0, server_1.query)({
|
||||
args: { username: values_1.v.string() },
|
||||
returns: values_1.v.object({ salt: values_1.v.string() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var user, fakeSalt;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("userProfiles")
|
||||
.withIndex("by_username", function (q) { return q.eq("username", args.username); })
|
||||
.unique()];
|
||||
case 1:
|
||||
user = _a.sent();
|
||||
if (user) {
|
||||
return [2 /*return*/, { salt: user.clientSalt }];
|
||||
}
|
||||
return [4 /*yield*/, sha256Hex("SERVER_SECRET_KEY" + args.username)];
|
||||
case 2:
|
||||
fakeSalt = _a.sent();
|
||||
return [2 /*return*/, { salt: fakeSalt }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Verify user credentials (DAK comparison)
|
||||
exports.verifyUser = (0, server_1.mutation)({
|
||||
args: {
|
||||
username: values_1.v.string(),
|
||||
dak: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.union(values_1.v.object({
|
||||
success: values_1.v.boolean(),
|
||||
userId: values_1.v.string(),
|
||||
encryptedMK: values_1.v.string(),
|
||||
encryptedPrivateKeys: values_1.v.string(),
|
||||
publicKey: values_1.v.string(),
|
||||
}), values_1.v.object({ error: values_1.v.string() })),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var user, hashedDAK;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("userProfiles")
|
||||
.withIndex("by_username", function (q) { return q.eq("username", args.username); })
|
||||
.unique()];
|
||||
case 1:
|
||||
user = _a.sent();
|
||||
if (!user) {
|
||||
return [2 /*return*/, { error: "Invalid credentials" }];
|
||||
}
|
||||
return [4 /*yield*/, sha256Hex(args.dak)];
|
||||
case 2:
|
||||
hashedDAK = _a.sent();
|
||||
if (hashedDAK === user.hashedAuthKey) {
|
||||
return [2 /*return*/, {
|
||||
success: true,
|
||||
userId: user._id,
|
||||
encryptedMK: user.encryptedMasterKey,
|
||||
encryptedPrivateKeys: user.encryptedPrivateKeys,
|
||||
publicKey: user.publicIdentityKey,
|
||||
}];
|
||||
}
|
||||
return [2 /*return*/, { error: "Invalid credentials" }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Register new user with crypto keys
|
||||
exports.createUserWithProfile = (0, server_1.mutation)({
|
||||
args: {
|
||||
username: values_1.v.string(),
|
||||
salt: values_1.v.string(),
|
||||
encryptedMK: values_1.v.string(),
|
||||
hak: values_1.v.string(),
|
||||
publicKey: values_1.v.string(),
|
||||
signingKey: values_1.v.string(),
|
||||
encryptedPrivateKeys: values_1.v.string(),
|
||||
inviteCode: values_1.v.optional(values_1.v.string()),
|
||||
},
|
||||
returns: values_1.v.union(values_1.v.object({ success: values_1.v.boolean(), userId: values_1.v.string() }), values_1.v.object({ error: values_1.v.string() })),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing, isFirstUser, invite, userId, everyoneRoleId, ownerRoleId, everyoneRole;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("userProfiles")
|
||||
.withIndex("by_username", function (q) { return q.eq("username", args.username); })
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (existing) {
|
||||
return [2 /*return*/, { error: "Username taken" }];
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.query("userProfiles").first()];
|
||||
case 2:
|
||||
isFirstUser = (_a.sent()) === null;
|
||||
if (!!isFirstUser) return [3 /*break*/, 5];
|
||||
if (!args.inviteCode) {
|
||||
return [2 /*return*/, { error: "Invite code required" }];
|
||||
}
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("invites")
|
||||
.withIndex("by_code", function (q) { return q.eq("code", args.inviteCode); })
|
||||
.unique()];
|
||||
case 3:
|
||||
invite = _a.sent();
|
||||
if (!invite) {
|
||||
return [2 /*return*/, { error: "Invalid invite code" }];
|
||||
}
|
||||
if (invite.expiresAt && Date.now() > invite.expiresAt) {
|
||||
return [2 /*return*/, { error: "Invite expired" }];
|
||||
}
|
||||
if (invite.maxUses !== undefined &&
|
||||
invite.maxUses !== null &&
|
||||
invite.uses >= invite.maxUses) {
|
||||
return [2 /*return*/, { error: "Invite max uses reached" }];
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.patch(invite._id, { uses: invite.uses + 1 })];
|
||||
case 4:
|
||||
_a.sent();
|
||||
_a.label = 5;
|
||||
case 5: return [4 /*yield*/, ctx.db.insert("userProfiles", {
|
||||
username: args.username,
|
||||
clientSalt: args.salt,
|
||||
encryptedMasterKey: args.encryptedMK,
|
||||
hashedAuthKey: args.hak,
|
||||
publicIdentityKey: args.publicKey,
|
||||
publicSigningKey: args.signingKey,
|
||||
encryptedPrivateKeys: args.encryptedPrivateKeys,
|
||||
isAdmin: isFirstUser,
|
||||
})];
|
||||
case 6:
|
||||
userId = _a.sent();
|
||||
if (!isFirstUser) return [3 /*break*/, 11];
|
||||
return [4 /*yield*/, ctx.db.insert("roles", {
|
||||
name: "@everyone",
|
||||
color: "#99aab5",
|
||||
position: 0,
|
||||
permissions: {
|
||||
create_invite: true,
|
||||
embed_links: true,
|
||||
attach_files: true,
|
||||
},
|
||||
isHoist: false,
|
||||
})];
|
||||
case 7:
|
||||
everyoneRoleId = _a.sent();
|
||||
return [4 /*yield*/, ctx.db.insert("roles", {
|
||||
name: "Owner",
|
||||
color: "#e91e63",
|
||||
position: 100,
|
||||
permissions: {
|
||||
manage_channels: true,
|
||||
manage_roles: true,
|
||||
manage_messages: true,
|
||||
create_invite: true,
|
||||
embed_links: true,
|
||||
attach_files: true,
|
||||
},
|
||||
isHoist: true,
|
||||
})];
|
||||
case 8:
|
||||
ownerRoleId = _a.sent();
|
||||
return [4 /*yield*/, ctx.db.insert("userRoles", { userId: userId, roleId: everyoneRoleId })];
|
||||
case 9:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, ctx.db.insert("userRoles", { userId: userId, roleId: ownerRoleId })];
|
||||
case 10:
|
||||
_a.sent();
|
||||
return [3 /*break*/, 14];
|
||||
case 11: return [4 /*yield*/, ctx.db
|
||||
.query("roles")
|
||||
.filter(function (q) { return q.eq(q.field("name"), "@everyone"); })
|
||||
.first()];
|
||||
case 12:
|
||||
everyoneRole = _a.sent();
|
||||
if (!everyoneRole) return [3 /*break*/, 14];
|
||||
return [4 /*yield*/, ctx.db.insert("userRoles", {
|
||||
userId: userId,
|
||||
roleId: everyoneRole._id,
|
||||
})];
|
||||
case 13:
|
||||
_a.sent();
|
||||
_a.label = 14;
|
||||
case 14: return [2 /*return*/, { success: true, userId: userId }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get all users' public keys
|
||||
exports.getPublicKeys = (0, server_1.query)({
|
||||
args: {},
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
id: values_1.v.string(),
|
||||
username: values_1.v.string(),
|
||||
public_identity_key: values_1.v.string(),
|
||||
status: values_1.v.optional(values_1.v.string()),
|
||||
displayName: values_1.v.optional(values_1.v.string()),
|
||||
avatarUrl: values_1.v.optional(values_1.v.union(values_1.v.string(), values_1.v.null())),
|
||||
aboutMe: values_1.v.optional(values_1.v.string()),
|
||||
customStatus: values_1.v.optional(values_1.v.string()),
|
||||
joinSoundUrl: values_1.v.optional(values_1.v.union(values_1.v.string(), values_1.v.null())),
|
||||
})),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var users, results, _i, users_1, u, avatarUrl, joinSoundUrl;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("userProfiles").collect()];
|
||||
case 1:
|
||||
users = _a.sent();
|
||||
results = [];
|
||||
_i = 0, users_1 = users;
|
||||
_a.label = 2;
|
||||
case 2:
|
||||
if (!(_i < users_1.length)) return [3 /*break*/, 8];
|
||||
u = users_1[_i];
|
||||
avatarUrl = null;
|
||||
if (!u.avatarStorageId) return [3 /*break*/, 4];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, u.avatarStorageId)];
|
||||
case 3:
|
||||
avatarUrl = _a.sent();
|
||||
_a.label = 4;
|
||||
case 4:
|
||||
joinSoundUrl = null;
|
||||
if (!u.joinSoundStorageId) return [3 /*break*/, 6];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, u.joinSoundStorageId)];
|
||||
case 5:
|
||||
joinSoundUrl = _a.sent();
|
||||
_a.label = 6;
|
||||
case 6:
|
||||
results.push({
|
||||
id: u._id,
|
||||
username: u.username,
|
||||
public_identity_key: u.publicIdentityKey,
|
||||
status: u.status || "offline",
|
||||
displayName: u.displayName,
|
||||
avatarUrl: avatarUrl,
|
||||
aboutMe: u.aboutMe,
|
||||
customStatus: u.customStatus,
|
||||
joinSoundUrl: joinSoundUrl,
|
||||
});
|
||||
_a.label = 7;
|
||||
case 7:
|
||||
_i++;
|
||||
return [3 /*break*/, 2];
|
||||
case 8: return [2 /*return*/, results];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Update user profile (aboutMe, avatar, customStatus)
|
||||
exports.updateProfile = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
displayName: values_1.v.optional(values_1.v.string()),
|
||||
aboutMe: values_1.v.optional(values_1.v.string()),
|
||||
avatarStorageId: values_1.v.optional(values_1.v.id("_storage")),
|
||||
customStatus: values_1.v.optional(values_1.v.string()),
|
||||
joinSoundStorageId: values_1.v.optional(values_1.v.id("_storage")),
|
||||
removeJoinSound: values_1.v.optional(values_1.v.boolean()),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var patch;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
patch = {};
|
||||
if (args.displayName !== undefined)
|
||||
patch.displayName = args.displayName;
|
||||
if (args.aboutMe !== undefined)
|
||||
patch.aboutMe = args.aboutMe;
|
||||
if (args.avatarStorageId !== undefined)
|
||||
patch.avatarStorageId = args.avatarStorageId;
|
||||
if (args.customStatus !== undefined)
|
||||
patch.customStatus = args.customStatus;
|
||||
if (args.joinSoundStorageId !== undefined)
|
||||
patch.joinSoundStorageId = args.joinSoundStorageId;
|
||||
if (args.removeJoinSound)
|
||||
patch.joinSoundStorageId = undefined;
|
||||
return [4 /*yield*/, ctx.db.patch(args.userId, patch)];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get the current user's join sound URL
|
||||
exports.getMyJoinSoundUrl = (0, server_1.query)({
|
||||
args: { userId: values_1.v.id("userProfiles") },
|
||||
returns: values_1.v.union(values_1.v.string(), values_1.v.null()),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var user;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.userId)];
|
||||
case 1:
|
||||
user = _a.sent();
|
||||
if (!(user === null || user === void 0 ? void 0 : user.joinSoundStorageId))
|
||||
return [2 /*return*/, null];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, user.joinSoundStorageId)];
|
||||
case 2: return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Update user status
|
||||
exports.updateStatus = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
status: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.patch(args.userId, { status: args.status })];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
187
convex/categories.js
Normal file
187
convex/categories.js
Normal file
@@ -0,0 +1,187 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.reorder = exports.remove = exports.rename = exports.create = exports.list = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
// List all categories ordered by position
|
||||
exports.list = (0, server_1.query)({
|
||||
args: {},
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
_id: values_1.v.id("categories"),
|
||||
_creationTime: values_1.v.number(),
|
||||
name: values_1.v.string(),
|
||||
position: values_1.v.number(),
|
||||
})),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("categories")
|
||||
.withIndex("by_position")
|
||||
.collect()];
|
||||
case 1: return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Create a new category
|
||||
exports.create = (0, server_1.mutation)({
|
||||
args: { name: values_1.v.string() },
|
||||
returns: values_1.v.object({ id: values_1.v.id("categories") }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var name, all, maxPos, id;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
name = args.name.trim();
|
||||
if (!name)
|
||||
throw new Error("Category name required");
|
||||
return [4 /*yield*/, ctx.db.query("categories").collect()];
|
||||
case 1:
|
||||
all = _a.sent();
|
||||
maxPos = all.reduce(function (max, c) { return Math.max(max, c.position); }, -1000);
|
||||
return [4 /*yield*/, ctx.db.insert("categories", {
|
||||
name: name,
|
||||
position: maxPos + 1000,
|
||||
})];
|
||||
case 2:
|
||||
id = _a.sent();
|
||||
return [2 /*return*/, { id: id }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Rename a category
|
||||
exports.rename = (0, server_1.mutation)({
|
||||
args: {
|
||||
id: values_1.v.id("categories"),
|
||||
name: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var name, cat;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
name = args.name.trim();
|
||||
if (!name)
|
||||
throw new Error("Category name required");
|
||||
return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
cat = _a.sent();
|
||||
if (!cat)
|
||||
throw new Error("Category not found");
|
||||
return [4 /*yield*/, ctx.db.patch(args.id, { name: name })];
|
||||
case 2:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Delete a category (moves its channels to uncategorized)
|
||||
exports.remove = (0, server_1.mutation)({
|
||||
args: { id: values_1.v.id("categories") },
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var cat, channels, _i, channels_1, ch;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
cat = _a.sent();
|
||||
if (!cat)
|
||||
throw new Error("Category not found");
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("channels")
|
||||
.withIndex("by_category", function (q) { return q.eq("categoryId", args.id); })
|
||||
.collect()];
|
||||
case 2:
|
||||
channels = _a.sent();
|
||||
_i = 0, channels_1 = channels;
|
||||
_a.label = 3;
|
||||
case 3:
|
||||
if (!(_i < channels_1.length)) return [3 /*break*/, 6];
|
||||
ch = channels_1[_i];
|
||||
return [4 /*yield*/, ctx.db.patch(ch._id, { categoryId: undefined })];
|
||||
case 4:
|
||||
_a.sent();
|
||||
_a.label = 5;
|
||||
case 5:
|
||||
_i++;
|
||||
return [3 /*break*/, 3];
|
||||
case 6: return [4 /*yield*/, ctx.db.delete(args.id)];
|
||||
case 7:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Batch reorder categories
|
||||
exports.reorder = (0, server_1.mutation)({
|
||||
args: {
|
||||
updates: values_1.v.array(values_1.v.object({
|
||||
id: values_1.v.id("categories"),
|
||||
position: values_1.v.number(),
|
||||
})),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var _i, _a, u;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
_i = 0, _a = args.updates;
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
if (!(_i < _a.length)) return [3 /*break*/, 4];
|
||||
u = _a[_i];
|
||||
return [4 /*yield*/, ctx.db.patch(u.id, { position: u.position })];
|
||||
case 2:
|
||||
_b.sent();
|
||||
_b.label = 3;
|
||||
case 3:
|
||||
_i++;
|
||||
return [3 /*break*/, 1];
|
||||
case 4: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
138
convex/channelKeys.js
Normal file
138
convex/channelKeys.js
Normal file
@@ -0,0 +1,138 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getKeysForUser = exports.uploadKeys = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
// Batch upsert encrypted key bundles
|
||||
exports.uploadKeys = (0, server_1.mutation)({
|
||||
args: {
|
||||
keys: values_1.v.array(values_1.v.object({
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
encryptedKeyBundle: values_1.v.string(),
|
||||
keyVersion: values_1.v.number(),
|
||||
})),
|
||||
},
|
||||
returns: values_1.v.object({ success: values_1.v.boolean(), count: values_1.v.number() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var _loop_1, _i, _a, keyData;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
_loop_1 = function (keyData) {
|
||||
var existing;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
if (!keyData.channelId || !keyData.userId || !keyData.encryptedKeyBundle) {
|
||||
return [2 /*return*/, "continue"];
|
||||
}
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("channelKeys")
|
||||
.withIndex("by_channel_and_user", function (q) {
|
||||
return q.eq("channelId", keyData.channelId).eq("userId", keyData.userId);
|
||||
})
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _c.sent();
|
||||
if (!existing) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, {
|
||||
encryptedKeyBundle: keyData.encryptedKeyBundle,
|
||||
keyVersion: keyData.keyVersion,
|
||||
})];
|
||||
case 2:
|
||||
_c.sent();
|
||||
return [3 /*break*/, 5];
|
||||
case 3: return [4 /*yield*/, ctx.db.insert("channelKeys", {
|
||||
channelId: keyData.channelId,
|
||||
userId: keyData.userId,
|
||||
encryptedKeyBundle: keyData.encryptedKeyBundle,
|
||||
keyVersion: keyData.keyVersion,
|
||||
})];
|
||||
case 4:
|
||||
_c.sent();
|
||||
_c.label = 5;
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
};
|
||||
_i = 0, _a = args.keys;
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
if (!(_i < _a.length)) return [3 /*break*/, 4];
|
||||
keyData = _a[_i];
|
||||
return [5 /*yield**/, _loop_1(keyData)];
|
||||
case 2:
|
||||
_b.sent();
|
||||
_b.label = 3;
|
||||
case 3:
|
||||
_i++;
|
||||
return [3 /*break*/, 1];
|
||||
case 4: return [2 /*return*/, { success: true, count: args.keys.length }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get user's encrypted key bundles (reactive!)
|
||||
exports.getKeysForUser = (0, server_1.query)({
|
||||
args: { userId: values_1.v.id("userProfiles") },
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
channel_id: values_1.v.id("channels"),
|
||||
encrypted_key_bundle: values_1.v.string(),
|
||||
key_version: values_1.v.number(),
|
||||
})),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var keys;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("channelKeys")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.userId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
keys = _a.sent();
|
||||
return [2 /*return*/, keys.map(function (k) { return ({
|
||||
channel_id: k.channelId,
|
||||
encrypted_key_bundle: k.encryptedKeyBundle,
|
||||
key_version: k.keyVersion,
|
||||
}); })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
390
convex/channels.js
Normal file
390
convex/channels.js
Normal file
@@ -0,0 +1,390 @@
|
||||
"use strict";
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.remove = exports.reorderChannels = exports.moveChannel = exports.rename = exports.updateTopic = exports.create = exports.get = exports.list = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var api_1 = require("./_generated/api");
|
||||
function deleteByChannel(ctx, table, channelId) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var docs, _i, docs_1, doc;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query(table)
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", channelId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
docs = _a.sent();
|
||||
_i = 0, docs_1 = docs;
|
||||
_a.label = 2;
|
||||
case 2:
|
||||
if (!(_i < docs_1.length)) return [3 /*break*/, 5];
|
||||
doc = docs_1[_i];
|
||||
return [4 /*yield*/, ctx.db.delete(doc._id)];
|
||||
case 3:
|
||||
_a.sent();
|
||||
_a.label = 4;
|
||||
case 4:
|
||||
_i++;
|
||||
return [3 /*break*/, 2];
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// List all non-DM channels
|
||||
exports.list = (0, server_1.query)({
|
||||
args: {},
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
_id: values_1.v.id("channels"),
|
||||
_creationTime: values_1.v.number(),
|
||||
name: values_1.v.string(),
|
||||
type: values_1.v.string(),
|
||||
categoryId: values_1.v.optional(values_1.v.id("categories")),
|
||||
topic: values_1.v.optional(values_1.v.string()),
|
||||
position: values_1.v.optional(values_1.v.number()),
|
||||
})),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var channels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("channels").collect()];
|
||||
case 1:
|
||||
channels = _a.sent();
|
||||
return [2 /*return*/, channels
|
||||
.filter(function (c) { return c.type !== "dm"; })
|
||||
.sort(function (a, b) { var _a, _b; return ((_a = a.position) !== null && _a !== void 0 ? _a : 0) - ((_b = b.position) !== null && _b !== void 0 ? _b : 0) || a.name.localeCompare(b.name); })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get single channel by ID
|
||||
exports.get = (0, server_1.query)({
|
||||
args: { id: values_1.v.id("channels") },
|
||||
returns: values_1.v.union(values_1.v.object({
|
||||
_id: values_1.v.id("channels"),
|
||||
_creationTime: values_1.v.number(),
|
||||
name: values_1.v.string(),
|
||||
type: values_1.v.string(),
|
||||
categoryId: values_1.v.optional(values_1.v.id("categories")),
|
||||
topic: values_1.v.optional(values_1.v.string()),
|
||||
position: values_1.v.optional(values_1.v.number()),
|
||||
}), values_1.v.null()),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1: return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Create new channel
|
||||
exports.create = (0, server_1.mutation)({
|
||||
args: {
|
||||
name: values_1.v.string(),
|
||||
type: values_1.v.optional(values_1.v.string()),
|
||||
categoryId: values_1.v.optional(values_1.v.id("categories")),
|
||||
topic: values_1.v.optional(values_1.v.string()),
|
||||
position: values_1.v.optional(values_1.v.number()),
|
||||
},
|
||||
returns: values_1.v.object({ id: values_1.v.id("channels") }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing, position, allChannels, sameCategory, maxPos, id;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!args.name.trim()) {
|
||||
throw new Error("Channel name required");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("channels")
|
||||
.withIndex("by_name", function (q) { return q.eq("name", args.name); })
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (existing) {
|
||||
throw new Error("Channel already exists");
|
||||
}
|
||||
position = args.position;
|
||||
if (!(position === undefined)) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.query("channels").collect()];
|
||||
case 2:
|
||||
allChannels = _a.sent();
|
||||
sameCategory = allChannels.filter(function (c) { return c.categoryId === args.categoryId && c.type !== "dm"; });
|
||||
maxPos = sameCategory.reduce(function (max, c) { var _a; return Math.max(max, (_a = c.position) !== null && _a !== void 0 ? _a : 0); }, -1000);
|
||||
position = maxPos + 1000;
|
||||
_a.label = 3;
|
||||
case 3: return [4 /*yield*/, ctx.db.insert("channels", {
|
||||
name: args.name,
|
||||
type: args.type || "text",
|
||||
categoryId: args.categoryId,
|
||||
topic: args.topic,
|
||||
position: position,
|
||||
})];
|
||||
case 4:
|
||||
id = _a.sent();
|
||||
return [2 /*return*/, { id: id }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Update channel topic
|
||||
exports.updateTopic = (0, server_1.mutation)({
|
||||
args: {
|
||||
id: values_1.v.id("channels"),
|
||||
topic: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var channel;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
channel = _a.sent();
|
||||
if (!channel)
|
||||
throw new Error("Channel not found");
|
||||
return [4 /*yield*/, ctx.db.patch(args.id, { topic: args.topic })];
|
||||
case 2:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Rename channel
|
||||
exports.rename = (0, server_1.mutation)({
|
||||
args: {
|
||||
id: values_1.v.id("channels"),
|
||||
name: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.object({
|
||||
_id: values_1.v.id("channels"),
|
||||
_creationTime: values_1.v.number(),
|
||||
name: values_1.v.string(),
|
||||
type: values_1.v.string(),
|
||||
categoryId: values_1.v.optional(values_1.v.id("categories")),
|
||||
topic: values_1.v.optional(values_1.v.string()),
|
||||
position: values_1.v.optional(values_1.v.number()),
|
||||
}),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var channel;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!args.name.trim()) {
|
||||
throw new Error("Name required");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
channel = _a.sent();
|
||||
if (!channel) {
|
||||
throw new Error("Channel not found");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.patch(args.id, { name: args.name })];
|
||||
case 2:
|
||||
_a.sent();
|
||||
return [2 /*return*/, __assign(__assign({}, channel), { name: args.name })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Move a channel to a different category with a new position
|
||||
exports.moveChannel = (0, server_1.mutation)({
|
||||
args: {
|
||||
id: values_1.v.id("channels"),
|
||||
categoryId: values_1.v.optional(values_1.v.id("categories")),
|
||||
position: values_1.v.number(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var channel;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
channel = _a.sent();
|
||||
if (!channel)
|
||||
throw new Error("Channel not found");
|
||||
return [4 /*yield*/, ctx.db.patch(args.id, {
|
||||
categoryId: args.categoryId,
|
||||
position: args.position,
|
||||
})];
|
||||
case 2:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Batch reorder channels
|
||||
exports.reorderChannels = (0, server_1.mutation)({
|
||||
args: {
|
||||
updates: values_1.v.array(values_1.v.object({
|
||||
id: values_1.v.id("channels"),
|
||||
categoryId: values_1.v.optional(values_1.v.id("categories")),
|
||||
position: values_1.v.number(),
|
||||
})),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var _i, _a, u;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
_i = 0, _a = args.updates;
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
if (!(_i < _a.length)) return [3 /*break*/, 4];
|
||||
u = _a[_i];
|
||||
return [4 /*yield*/, ctx.db.patch(u.id, {
|
||||
categoryId: u.categoryId,
|
||||
position: u.position,
|
||||
})];
|
||||
case 2:
|
||||
_b.sent();
|
||||
_b.label = 3;
|
||||
case 3:
|
||||
_i++;
|
||||
return [3 /*break*/, 1];
|
||||
case 4: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Delete channel + cascade messages and keys
|
||||
exports.remove = (0, server_1.mutation)({
|
||||
args: { id: values_1.v.id("channels") },
|
||||
returns: values_1.v.object({ success: values_1.v.boolean() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var channel, messages, _loop_1, _i, messages_1, msg;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
channel = _a.sent();
|
||||
if (!channel) {
|
||||
throw new Error("Channel not found");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("messages")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", args.id); })
|
||||
.collect()];
|
||||
case 2:
|
||||
messages = _a.sent();
|
||||
_loop_1 = function (msg) {
|
||||
var reactions, _b, reactions_1, r;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("messageReactions")
|
||||
.withIndex("by_message", function (q) { return q.eq("messageId", msg._id); })
|
||||
.collect()];
|
||||
case 1:
|
||||
reactions = _c.sent();
|
||||
_b = 0, reactions_1 = reactions;
|
||||
_c.label = 2;
|
||||
case 2:
|
||||
if (!(_b < reactions_1.length)) return [3 /*break*/, 5];
|
||||
r = reactions_1[_b];
|
||||
return [4 /*yield*/, ctx.db.delete(r._id)];
|
||||
case 3:
|
||||
_c.sent();
|
||||
_c.label = 4;
|
||||
case 4:
|
||||
_b++;
|
||||
return [3 /*break*/, 2];
|
||||
case 5: return [4 /*yield*/, ctx.db.delete(msg._id)];
|
||||
case 6:
|
||||
_c.sent();
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
};
|
||||
_i = 0, messages_1 = messages;
|
||||
_a.label = 3;
|
||||
case 3:
|
||||
if (!(_i < messages_1.length)) return [3 /*break*/, 6];
|
||||
msg = messages_1[_i];
|
||||
return [5 /*yield**/, _loop_1(msg)];
|
||||
case 4:
|
||||
_a.sent();
|
||||
_a.label = 5;
|
||||
case 5:
|
||||
_i++;
|
||||
return [3 /*break*/, 3];
|
||||
case 6: return [4 /*yield*/, deleteByChannel(ctx, "channelKeys", args.id)];
|
||||
case 7:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, deleteByChannel(ctx, "dmParticipants", args.id)];
|
||||
case 8:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, deleteByChannel(ctx, "typingIndicators", args.id)];
|
||||
case 9:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, deleteByChannel(ctx, "voiceStates", args.id)];
|
||||
case 10:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, deleteByChannel(ctx, "channelReadState", args.id)];
|
||||
case 11:
|
||||
_a.sent();
|
||||
// Clear AFK setting if this channel was the AFK channel
|
||||
return [4 /*yield*/, ctx.runMutation(api_1.internal.serverSettings.clearAfkChannel, { channelId: args.id })];
|
||||
case 12:
|
||||
// Clear AFK setting if this channel was the AFK channel
|
||||
_a.sent();
|
||||
return [4 /*yield*/, ctx.db.delete(args.id)];
|
||||
case 13:
|
||||
_a.sent();
|
||||
return [2 /*return*/, { success: true }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
7
convex/convex.config.js
Normal file
7
convex/convex.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var server_1 = require("convex/server");
|
||||
var convex_config_js_1 = require("@convex-dev/presence/convex.config.js");
|
||||
var app = (0, server_1.defineApp)();
|
||||
app.use(convex_config_js_1.default);
|
||||
exports.default = app;
|
||||
158
convex/dms.js
Normal file
158
convex/dms.js
Normal file
@@ -0,0 +1,158 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.listDMs = exports.openDM = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var storageUrl_1 = require("./storageUrl");
|
||||
exports.openDM = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
targetUserId: values_1.v.id("userProfiles"),
|
||||
},
|
||||
returns: values_1.v.object({
|
||||
channelId: values_1.v.id("channels"),
|
||||
created: values_1.v.boolean(),
|
||||
}),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var sorted, dmName, existing, channelId;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (args.userId === args.targetUserId) {
|
||||
throw new Error("Cannot DM yourself");
|
||||
}
|
||||
sorted = [args.userId, args.targetUserId].sort();
|
||||
dmName = "dm-".concat(sorted[0], "-").concat(sorted[1]);
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("channels")
|
||||
.withIndex("by_name", function (q) { return q.eq("name", dmName); })
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (existing) {
|
||||
return [2 /*return*/, { channelId: existing._id, created: false }];
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.insert("channels", {
|
||||
name: dmName,
|
||||
type: "dm",
|
||||
})];
|
||||
case 2:
|
||||
channelId = _a.sent();
|
||||
return [4 /*yield*/, Promise.all([
|
||||
ctx.db.insert("dmParticipants", { channelId: channelId, userId: args.userId }),
|
||||
ctx.db.insert("dmParticipants", { channelId: channelId, userId: args.targetUserId }),
|
||||
])];
|
||||
case 3:
|
||||
_a.sent();
|
||||
return [2 /*return*/, { channelId: channelId, created: true }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.listDMs = (0, server_1.query)({
|
||||
args: { userId: values_1.v.id("userProfiles") },
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
channel_id: values_1.v.id("channels"),
|
||||
channel_name: values_1.v.string(),
|
||||
other_user_id: values_1.v.string(),
|
||||
other_username: values_1.v.string(),
|
||||
other_user_status: values_1.v.optional(values_1.v.string()),
|
||||
other_user_avatar_url: values_1.v.optional(values_1.v.union(values_1.v.string(), values_1.v.null())),
|
||||
})),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var myParticipations, results;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("dmParticipants")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.userId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
myParticipations = _a.sent();
|
||||
return [4 /*yield*/, Promise.all(myParticipations.map(function (part) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var channel, otherParts, otherPart, otherUser, avatarUrl, _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(part.channelId)];
|
||||
case 1:
|
||||
channel = _b.sent();
|
||||
if (!channel || channel.type !== "dm")
|
||||
return [2 /*return*/, null];
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("dmParticipants")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", part.channelId); })
|
||||
.collect()];
|
||||
case 2:
|
||||
otherParts = _b.sent();
|
||||
otherPart = otherParts.find(function (p) { return p.userId !== args.userId; });
|
||||
if (!otherPart)
|
||||
return [2 /*return*/, null];
|
||||
return [4 /*yield*/, ctx.db.get(otherPart.userId)];
|
||||
case 3:
|
||||
otherUser = _b.sent();
|
||||
if (!otherUser)
|
||||
return [2 /*return*/, null];
|
||||
if (!otherUser.avatarStorageId) return [3 /*break*/, 5];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, otherUser.avatarStorageId)];
|
||||
case 4:
|
||||
_a = _b.sent();
|
||||
return [3 /*break*/, 6];
|
||||
case 5:
|
||||
_a = null;
|
||||
_b.label = 6;
|
||||
case 6:
|
||||
avatarUrl = _a;
|
||||
return [2 /*return*/, {
|
||||
channel_id: part.channelId,
|
||||
channel_name: channel.name,
|
||||
other_user_id: otherUser._id,
|
||||
other_username: otherUser.username,
|
||||
other_user_status: otherUser.status || "offline",
|
||||
other_user_avatar_url: avatarUrl,
|
||||
}];
|
||||
}
|
||||
});
|
||||
}); }))];
|
||||
case 2:
|
||||
results = _a.sent();
|
||||
return [2 /*return*/, results.filter(function (r) { return r !== null; })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
71
convex/files.js
Normal file
71
convex/files.js
Normal file
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getFileUrl = exports.generateUploadUrl = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var storageUrl_1 = require("./storageUrl");
|
||||
// Generate upload URL for client-side uploads
|
||||
exports.generateUploadUrl = (0, server_1.mutation)({
|
||||
args: {},
|
||||
returns: values_1.v.string(),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var url;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.storage.generateUploadUrl()];
|
||||
case 1:
|
||||
url = _a.sent();
|
||||
return [2 /*return*/, (0, storageUrl_1.rewriteToPublicUrl)(url)];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get file URL from storage ID
|
||||
exports.getFileUrl = (0, server_1.query)({
|
||||
args: { storageId: values_1.v.id("_storage") },
|
||||
returns: values_1.v.union(values_1.v.string(), values_1.v.null()),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, args.storageId)];
|
||||
case 1: return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
86
convex/gifs.js
Normal file
86
convex/gifs.js
Normal file
@@ -0,0 +1,86 @@
|
||||
"use strict";
|
||||
"use node";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.categories = exports.search = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
// Search GIFs via Tenor API
|
||||
exports.search = (0, server_1.action)({
|
||||
args: {
|
||||
q: values_1.v.string(),
|
||||
limit: values_1.v.optional(values_1.v.number()),
|
||||
},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (_ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var apiKey, limit, url, response;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
apiKey = process.env.TENOR_API_KEY;
|
||||
if (!apiKey) {
|
||||
console.warn("TENOR_API_KEY missing");
|
||||
return [2 /*return*/, { results: [] }];
|
||||
}
|
||||
limit = args.limit || 8;
|
||||
url = "https://tenor.googleapis.com/v2/search?q=".concat(encodeURIComponent(args.q), "&key=").concat(apiKey, "&limit=").concat(limit);
|
||||
return [4 /*yield*/, fetch(url)];
|
||||
case 1:
|
||||
response = _a.sent();
|
||||
if (!response.ok) {
|
||||
console.error("Tenor API Error:", response.statusText);
|
||||
return [2 /*return*/, { results: [] }];
|
||||
}
|
||||
return [4 /*yield*/, response.json()];
|
||||
case 2: return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get GIF categories
|
||||
exports.categories = (0, server_1.action)({
|
||||
args: {},
|
||||
returns: values_1.v.any(),
|
||||
handler: function () { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
// Return static categories (same as the JSON file in backend)
|
||||
// These are loaded from the frontend data file
|
||||
return [2 /*return*/, { categories: [] }];
|
||||
});
|
||||
}); },
|
||||
});
|
||||
131
convex/invites.js
Normal file
131
convex/invites.js
Normal file
@@ -0,0 +1,131 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.revoke = exports.use = exports.create = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
// Create invite with encrypted payload
|
||||
exports.create = (0, server_1.mutation)({
|
||||
args: {
|
||||
code: values_1.v.string(),
|
||||
encryptedPayload: values_1.v.string(),
|
||||
createdBy: values_1.v.id("userProfiles"),
|
||||
maxUses: values_1.v.optional(values_1.v.number()),
|
||||
expiresAt: values_1.v.optional(values_1.v.number()),
|
||||
keyVersion: values_1.v.number(),
|
||||
},
|
||||
returns: values_1.v.object({ success: values_1.v.boolean() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.insert("invites", {
|
||||
code: args.code,
|
||||
encryptedPayload: args.encryptedPayload,
|
||||
createdBy: args.createdBy,
|
||||
maxUses: args.maxUses,
|
||||
uses: 0,
|
||||
expiresAt: args.expiresAt,
|
||||
keyVersion: args.keyVersion,
|
||||
})];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, { success: true }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Fetch and validate invite (returns encrypted payload)
|
||||
exports.use = (0, server_1.query)({
|
||||
args: { code: values_1.v.string() },
|
||||
returns: values_1.v.union(values_1.v.object({
|
||||
encryptedPayload: values_1.v.string(),
|
||||
keyVersion: values_1.v.number(),
|
||||
}), values_1.v.object({ error: values_1.v.string() })),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var invite;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("invites")
|
||||
.withIndex("by_code", function (q) { return q.eq("code", args.code); })
|
||||
.unique()];
|
||||
case 1:
|
||||
invite = _a.sent();
|
||||
if (!invite) {
|
||||
return [2 /*return*/, { error: "Invite not found" }];
|
||||
}
|
||||
if (invite.expiresAt && Date.now() > invite.expiresAt) {
|
||||
return [2 /*return*/, { error: "Invite expired" }];
|
||||
}
|
||||
if (invite.maxUses !== undefined &&
|
||||
invite.maxUses !== null &&
|
||||
invite.uses >= invite.maxUses) {
|
||||
return [2 /*return*/, { error: "Invite max uses reached" }];
|
||||
}
|
||||
return [2 /*return*/, {
|
||||
encryptedPayload: invite.encryptedPayload,
|
||||
keyVersion: invite.keyVersion,
|
||||
}];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Revoke invite
|
||||
exports.revoke = (0, server_1.mutation)({
|
||||
args: { code: values_1.v.string() },
|
||||
returns: values_1.v.object({ success: values_1.v.boolean() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var invite;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("invites")
|
||||
.withIndex("by_code", function (q) { return q.eq("code", args.code); })
|
||||
.unique()];
|
||||
case 1:
|
||||
invite = _a.sent();
|
||||
if (!invite) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.delete(invite._id)];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, { success: true }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
139
convex/members.js
Normal file
139
convex/members.js
Normal file
@@ -0,0 +1,139 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getChannelMembers = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var storageUrl_1 = require("./storageUrl");
|
||||
exports.getChannelMembers = (0, server_1.query)({
|
||||
args: {
|
||||
channelId: values_1.v.id("channels"),
|
||||
},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var channelKeyDocs, seenUsers, members, _loop_1, _i, channelKeyDocs_1, doc;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("channelKeys")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", args.channelId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
channelKeyDocs = _a.sent();
|
||||
seenUsers = new Set();
|
||||
members = [];
|
||||
_loop_1 = function (doc) {
|
||||
var odId, user, userRoleDocs, roles, _b, userRoleDocs_1, ur, role, avatarUrl;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
odId = doc.userId.toString();
|
||||
if (seenUsers.has(odId))
|
||||
return [2 /*return*/, "continue"];
|
||||
seenUsers.add(odId);
|
||||
return [4 /*yield*/, ctx.db.get(doc.userId)];
|
||||
case 1:
|
||||
user = _c.sent();
|
||||
if (!user)
|
||||
return [2 /*return*/, "continue"];
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", doc.userId); })
|
||||
.collect()];
|
||||
case 2:
|
||||
userRoleDocs = _c.sent();
|
||||
roles = [];
|
||||
_b = 0, userRoleDocs_1 = userRoleDocs;
|
||||
_c.label = 3;
|
||||
case 3:
|
||||
if (!(_b < userRoleDocs_1.length)) return [3 /*break*/, 6];
|
||||
ur = userRoleDocs_1[_b];
|
||||
return [4 /*yield*/, ctx.db.get(ur.roleId)];
|
||||
case 4:
|
||||
role = _c.sent();
|
||||
if (role) {
|
||||
roles.push({
|
||||
id: role._id,
|
||||
name: role.name,
|
||||
color: role.color,
|
||||
position: role.position,
|
||||
isHoist: role.isHoist,
|
||||
});
|
||||
}
|
||||
_c.label = 5;
|
||||
case 5:
|
||||
_b++;
|
||||
return [3 /*break*/, 3];
|
||||
case 6:
|
||||
avatarUrl = null;
|
||||
if (!user.avatarStorageId) return [3 /*break*/, 8];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, user.avatarStorageId)];
|
||||
case 7:
|
||||
avatarUrl = _c.sent();
|
||||
_c.label = 8;
|
||||
case 8:
|
||||
members.push({
|
||||
id: user._id,
|
||||
username: user.username,
|
||||
status: user.status || "offline",
|
||||
roles: roles.sort(function (a, b) { return b.position - a.position; }),
|
||||
avatarUrl: avatarUrl,
|
||||
aboutMe: user.aboutMe,
|
||||
customStatus: user.customStatus,
|
||||
});
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
};
|
||||
_i = 0, channelKeyDocs_1 = channelKeyDocs;
|
||||
_a.label = 2;
|
||||
case 2:
|
||||
if (!(_i < channelKeyDocs_1.length)) return [3 /*break*/, 5];
|
||||
doc = channelKeyDocs_1[_i];
|
||||
return [5 /*yield**/, _loop_1(doc)];
|
||||
case 3:
|
||||
_a.sent();
|
||||
_a.label = 4;
|
||||
case 4:
|
||||
_i++;
|
||||
return [3 /*break*/, 2];
|
||||
case 5: return [2 /*return*/, members];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
300
convex/messages.js
Normal file
300
convex/messages.js
Normal file
@@ -0,0 +1,300 @@
|
||||
"use strict";
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.remove = exports.listPinned = exports.pin = exports.edit = exports.send = exports.list = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var server_2 = require("convex/server");
|
||||
var values_1 = require("convex/values");
|
||||
var storageUrl_1 = require("./storageUrl");
|
||||
var roles_1 = require("./roles");
|
||||
function enrichMessage(ctx, msg, userId) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var sender, avatarUrl, reactionDocs, reactions, _i, reactionDocs_1, r, entry, replyToUsername, replyToContent, replyToNonce, replyToAvatarUrl, repliedMsg, repliedSender;
|
||||
var _a;
|
||||
var _b;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(msg.senderId)];
|
||||
case 1:
|
||||
sender = _c.sent();
|
||||
avatarUrl = null;
|
||||
if (!(sender === null || sender === void 0 ? void 0 : sender.avatarStorageId)) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, sender.avatarStorageId)];
|
||||
case 2:
|
||||
avatarUrl = _c.sent();
|
||||
_c.label = 3;
|
||||
case 3: return [4 /*yield*/, ctx.db
|
||||
.query("messageReactions")
|
||||
.withIndex("by_message", function (q) { return q.eq("messageId", msg._id); })
|
||||
.collect()];
|
||||
case 4:
|
||||
reactionDocs = _c.sent();
|
||||
reactions = {};
|
||||
for (_i = 0, reactionDocs_1 = reactionDocs; _i < reactionDocs_1.length; _i++) {
|
||||
r = reactionDocs_1[_i];
|
||||
entry = ((_a = reactions[_b = r.emoji]) !== null && _a !== void 0 ? _a : (reactions[_b] = { count: 0, me: false }));
|
||||
entry.count++;
|
||||
if (userId && r.userId === userId) {
|
||||
entry.me = true;
|
||||
}
|
||||
}
|
||||
replyToUsername = null;
|
||||
replyToContent = null;
|
||||
replyToNonce = null;
|
||||
replyToAvatarUrl = null;
|
||||
if (!msg.replyTo) return [3 /*break*/, 8];
|
||||
return [4 /*yield*/, ctx.db.get(msg.replyTo)];
|
||||
case 5:
|
||||
repliedMsg = _c.sent();
|
||||
if (!repliedMsg) return [3 /*break*/, 8];
|
||||
return [4 /*yield*/, ctx.db.get(repliedMsg.senderId)];
|
||||
case 6:
|
||||
repliedSender = _c.sent();
|
||||
replyToUsername = (repliedSender === null || repliedSender === void 0 ? void 0 : repliedSender.username) || "Unknown";
|
||||
replyToContent = repliedMsg.ciphertext;
|
||||
replyToNonce = repliedMsg.nonce;
|
||||
if (!(repliedSender === null || repliedSender === void 0 ? void 0 : repliedSender.avatarStorageId)) return [3 /*break*/, 8];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, repliedSender.avatarStorageId)];
|
||||
case 7:
|
||||
replyToAvatarUrl = _c.sent();
|
||||
_c.label = 8;
|
||||
case 8: return [2 /*return*/, {
|
||||
id: msg._id,
|
||||
channel_id: msg.channelId,
|
||||
sender_id: msg.senderId,
|
||||
ciphertext: msg.ciphertext,
|
||||
nonce: msg.nonce,
|
||||
signature: msg.signature,
|
||||
key_version: msg.keyVersion,
|
||||
created_at: new Date(msg._creationTime).toISOString(),
|
||||
username: (sender === null || sender === void 0 ? void 0 : sender.username) || "Unknown",
|
||||
public_signing_key: (sender === null || sender === void 0 ? void 0 : sender.publicSigningKey) || "",
|
||||
avatarUrl: avatarUrl,
|
||||
reactions: Object.keys(reactions).length > 0 ? reactions : null,
|
||||
replyToId: msg.replyTo || null,
|
||||
replyToUsername: replyToUsername,
|
||||
replyToContent: replyToContent,
|
||||
replyToNonce: replyToNonce,
|
||||
replyToAvatarUrl: replyToAvatarUrl,
|
||||
editedAt: msg.editedAt || null,
|
||||
pinned: msg.pinned || false,
|
||||
}];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.list = (0, server_1.query)({
|
||||
args: {
|
||||
paginationOpts: server_2.paginationOptsValidator,
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.optional(values_1.v.id("userProfiles")),
|
||||
},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var result, enrichedPage;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("messages")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", args.channelId); })
|
||||
.order("desc")
|
||||
.paginate(args.paginationOpts)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, Promise.all(result.page.map(function (msg) { return enrichMessage(ctx, msg, args.userId); }))];
|
||||
case 2:
|
||||
enrichedPage = _a.sent();
|
||||
return [2 /*return*/, __assign(__assign({}, result), { page: enrichedPage })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.send = (0, server_1.mutation)({
|
||||
args: {
|
||||
channelId: values_1.v.id("channels"),
|
||||
senderId: values_1.v.id("userProfiles"),
|
||||
ciphertext: values_1.v.string(),
|
||||
nonce: values_1.v.string(),
|
||||
signature: values_1.v.string(),
|
||||
keyVersion: values_1.v.number(),
|
||||
replyTo: values_1.v.optional(values_1.v.id("messages")),
|
||||
},
|
||||
returns: values_1.v.object({ id: values_1.v.id("messages") }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var id;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.insert("messages", {
|
||||
channelId: args.channelId,
|
||||
senderId: args.senderId,
|
||||
ciphertext: args.ciphertext,
|
||||
nonce: args.nonce,
|
||||
signature: args.signature,
|
||||
keyVersion: args.keyVersion,
|
||||
replyTo: args.replyTo,
|
||||
})];
|
||||
case 1:
|
||||
id = _a.sent();
|
||||
return [2 /*return*/, { id: id }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.edit = (0, server_1.mutation)({
|
||||
args: {
|
||||
id: values_1.v.id("messages"),
|
||||
ciphertext: values_1.v.string(),
|
||||
nonce: values_1.v.string(),
|
||||
signature: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.patch(args.id, {
|
||||
ciphertext: args.ciphertext,
|
||||
nonce: args.nonce,
|
||||
signature: args.signature,
|
||||
editedAt: Date.now(),
|
||||
})];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.pin = (0, server_1.mutation)({
|
||||
args: {
|
||||
id: values_1.v.id("messages"),
|
||||
pinned: values_1.v.boolean(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.patch(args.id, { pinned: args.pinned })];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.listPinned = (0, server_1.query)({
|
||||
args: {
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.optional(values_1.v.id("userProfiles")),
|
||||
},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var pinned;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("messages")
|
||||
.withIndex("by_channel_pinned", function (q) {
|
||||
return q.eq("channelId", args.channelId).eq("pinned", true);
|
||||
})
|
||||
.collect()];
|
||||
case 1:
|
||||
pinned = _a.sent();
|
||||
return [2 /*return*/, Promise.all(pinned.map(function (msg) { return enrichMessage(ctx, msg, args.userId); }))];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.remove = (0, server_1.mutation)({
|
||||
args: { id: values_1.v.id("messages"), userId: values_1.v.id("userProfiles") },
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var message, isSender, roles, canManage, reactions, _i, reactions_1, r;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
message = _a.sent();
|
||||
if (!message)
|
||||
throw new Error("Message not found");
|
||||
isSender = message.senderId === args.userId;
|
||||
if (!!isSender) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, (0, roles_1.getRolesForUser)(ctx, args.userId)];
|
||||
case 2:
|
||||
roles = _a.sent();
|
||||
canManage = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a.manage_messages; });
|
||||
if (!canManage) {
|
||||
throw new Error("Not authorized to delete this message");
|
||||
}
|
||||
_a.label = 3;
|
||||
case 3: return [4 /*yield*/, ctx.db
|
||||
.query("messageReactions")
|
||||
.withIndex("by_message", function (q) { return q.eq("messageId", args.id); })
|
||||
.collect()];
|
||||
case 4:
|
||||
reactions = _a.sent();
|
||||
_i = 0, reactions_1 = reactions;
|
||||
_a.label = 5;
|
||||
case 5:
|
||||
if (!(_i < reactions_1.length)) return [3 /*break*/, 8];
|
||||
r = reactions_1[_i];
|
||||
return [4 /*yield*/, ctx.db.delete(r._id)];
|
||||
case 6:
|
||||
_a.sent();
|
||||
_a.label = 7;
|
||||
case 7:
|
||||
_i++;
|
||||
return [3 /*break*/, 5];
|
||||
case 8: return [4 /*yield*/, ctx.db.delete(args.id)];
|
||||
case 9:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
85
convex/presence.js
Normal file
85
convex/presence.js
Normal file
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.disconnect = exports.list = exports.heartbeat = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var api_1 = require("./_generated/api");
|
||||
var values_1 = require("convex/values");
|
||||
var presence_1 = require("@convex-dev/presence");
|
||||
var presence = new presence_1.Presence(api_1.components.presence);
|
||||
exports.heartbeat = (0, server_1.mutation)({
|
||||
args: {
|
||||
roomId: values_1.v.string(),
|
||||
userId: values_1.v.string(),
|
||||
sessionId: values_1.v.string(),
|
||||
interval: values_1.v.number(),
|
||||
},
|
||||
handler: function (ctx_1, _a) { return __awaiter(void 0, [ctx_1, _a], void 0, function (ctx, _b) {
|
||||
var roomId = _b.roomId, userId = _b.userId, sessionId = _b.sessionId, interval = _b.interval;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, presence.heartbeat(ctx, roomId, userId, sessionId, interval)];
|
||||
case 1: return [2 /*return*/, _c.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.list = (0, server_1.query)({
|
||||
args: { roomToken: values_1.v.string() },
|
||||
handler: function (ctx_1, _a) { return __awaiter(void 0, [ctx_1, _a], void 0, function (ctx, _b) {
|
||||
var roomToken = _b.roomToken;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, presence.list(ctx, roomToken)];
|
||||
case 1: return [2 /*return*/, _c.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.disconnect = (0, server_1.mutation)({
|
||||
args: { sessionToken: values_1.v.string() },
|
||||
handler: function (ctx_1, _a) { return __awaiter(void 0, [ctx_1, _a], void 0, function (ctx, _b) {
|
||||
var sessionToken = _b.sessionToken;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, presence.disconnect(ctx, sessionToken)];
|
||||
case 1: return [2 /*return*/, _c.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
111
convex/reactions.js
Normal file
111
convex/reactions.js
Normal file
@@ -0,0 +1,111 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.remove = exports.add = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
// Add reaction (upsert - no duplicates)
|
||||
exports.add = (0, server_1.mutation)({
|
||||
args: {
|
||||
messageId: values_1.v.id("messages"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
emoji: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("messageReactions")
|
||||
.withIndex("by_message_user_emoji", function (q) {
|
||||
return q
|
||||
.eq("messageId", args.messageId)
|
||||
.eq("userId", args.userId)
|
||||
.eq("emoji", args.emoji);
|
||||
})
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (!!existing) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.insert("messageReactions", {
|
||||
messageId: args.messageId,
|
||||
userId: args.userId,
|
||||
emoji: args.emoji,
|
||||
})];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Remove reaction
|
||||
exports.remove = (0, server_1.mutation)({
|
||||
args: {
|
||||
messageId: values_1.v.id("messages"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
emoji: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("messageReactions")
|
||||
.withIndex("by_message_user_emoji", function (q) {
|
||||
return q
|
||||
.eq("messageId", args.messageId)
|
||||
.eq("userId", args.userId)
|
||||
.eq("emoji", args.emoji);
|
||||
})
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (!existing) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.delete(existing._id)];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
191
convex/readState.js
Normal file
191
convex/readState.js
Normal file
@@ -0,0 +1,191 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getLatestMessageTimestamps = exports.getAllReadStates = exports.markRead = exports.getReadState = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
// Get read state for a single channel
|
||||
exports.getReadState = (0, server_1.query)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
channelId: values_1.v.id("channels"),
|
||||
},
|
||||
returns: values_1.v.union(values_1.v.object({
|
||||
lastReadTimestamp: values_1.v.number(),
|
||||
}), values_1.v.null()),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var state;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("channelReadState")
|
||||
.withIndex("by_user_and_channel", function (q) {
|
||||
return q.eq("userId", args.userId).eq("channelId", args.channelId);
|
||||
})
|
||||
.unique()];
|
||||
case 1:
|
||||
state = _a.sent();
|
||||
if (!state)
|
||||
return [2 /*return*/, null];
|
||||
return [2 /*return*/, { lastReadTimestamp: state.lastReadTimestamp }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Mark a channel as read up to a given timestamp
|
||||
exports.markRead = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
channelId: values_1.v.id("channels"),
|
||||
lastReadTimestamp: values_1.v.number(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("channelReadState")
|
||||
.withIndex("by_user_and_channel", function (q) {
|
||||
return q.eq("userId", args.userId).eq("channelId", args.channelId);
|
||||
})
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (!existing) return [3 /*break*/, 4];
|
||||
if (!(args.lastReadTimestamp > existing.lastReadTimestamp)) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, {
|
||||
lastReadTimestamp: args.lastReadTimestamp,
|
||||
})];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [3 /*break*/, 6];
|
||||
case 4: return [4 /*yield*/, ctx.db.insert("channelReadState", {
|
||||
userId: args.userId,
|
||||
channelId: args.channelId,
|
||||
lastReadTimestamp: args.lastReadTimestamp,
|
||||
})];
|
||||
case 5:
|
||||
_a.sent();
|
||||
_a.label = 6;
|
||||
case 6: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get all read states for a user (used by Sidebar)
|
||||
exports.getAllReadStates = (0, server_1.query)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
},
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
channelId: values_1.v.id("channels"),
|
||||
lastReadTimestamp: values_1.v.number(),
|
||||
})),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var states;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("channelReadState")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.userId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
states = _a.sent();
|
||||
return [2 /*return*/, states.map(function (s) { return ({
|
||||
channelId: s.channelId,
|
||||
lastReadTimestamp: s.lastReadTimestamp,
|
||||
}); })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get the latest message timestamp per channel (used by Sidebar)
|
||||
exports.getLatestMessageTimestamps = (0, server_1.query)({
|
||||
args: {
|
||||
channelIds: values_1.v.array(values_1.v.id("channels")),
|
||||
},
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
channelId: values_1.v.id("channels"),
|
||||
latestTimestamp: values_1.v.number(),
|
||||
})),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var results, _loop_1, _i, _a, channelId;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
results = [];
|
||||
_loop_1 = function (channelId) {
|
||||
var latestMsg;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("messages")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", channelId); })
|
||||
.order("desc")
|
||||
.first()];
|
||||
case 1:
|
||||
latestMsg = _c.sent();
|
||||
if (latestMsg) {
|
||||
results.push({
|
||||
channelId: channelId,
|
||||
latestTimestamp: Math.floor(latestMsg._creationTime),
|
||||
});
|
||||
}
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
};
|
||||
_i = 0, _a = args.channelIds;
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
if (!(_i < _a.length)) return [3 /*break*/, 4];
|
||||
channelId = _a[_i];
|
||||
return [5 /*yield**/, _loop_1(channelId)];
|
||||
case 2:
|
||||
_b.sent();
|
||||
_b.label = 3;
|
||||
case 3:
|
||||
_i++;
|
||||
return [3 /*break*/, 1];
|
||||
case 4: return [2 /*return*/, results];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
330
convex/roles.js
Normal file
330
convex/roles.js
Normal file
@@ -0,0 +1,330 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
var __rest = (this && this.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getMyPermissions = exports.unassign = exports.assign = exports.listMembers = exports.remove = exports.update = exports.create = exports.list = void 0;
|
||||
exports.getRolesForUser = getRolesForUser;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var PERMISSION_KEYS = [
|
||||
"manage_channels",
|
||||
"manage_roles",
|
||||
"manage_messages",
|
||||
"create_invite",
|
||||
"embed_links",
|
||||
"attach_files",
|
||||
"move_members",
|
||||
"mute_members",
|
||||
];
|
||||
function getRolesForUser(ctx, userId) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var assignments, roles;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", userId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
assignments = _a.sent();
|
||||
return [4 /*yield*/, Promise.all(assignments.map(function (ur) { return ctx.db.get(ur.roleId); }))];
|
||||
case 2:
|
||||
roles = _a.sent();
|
||||
return [2 /*return*/, roles.filter(function (r) { return r !== null; })];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// List all roles
|
||||
exports.list = (0, server_1.query)({
|
||||
args: {},
|
||||
returns: values_1.v.array(values_1.v.any()),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("roles").collect()];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
return [2 /*return*/, roles.sort(function (a, b) { return (b.position || 0) - (a.position || 0); })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Create new role
|
||||
exports.create = (0, server_1.mutation)({
|
||||
args: {
|
||||
name: values_1.v.optional(values_1.v.string()),
|
||||
color: values_1.v.optional(values_1.v.string()),
|
||||
permissions: values_1.v.optional(values_1.v.any()),
|
||||
position: values_1.v.optional(values_1.v.number()),
|
||||
isHoist: values_1.v.optional(values_1.v.boolean()),
|
||||
},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var id;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.insert("roles", {
|
||||
name: args.name || "new role",
|
||||
color: args.color || "#99aab5",
|
||||
position: args.position || 0,
|
||||
permissions: args.permissions || {},
|
||||
isHoist: args.isHoist || false,
|
||||
})];
|
||||
case 1:
|
||||
id = _a.sent();
|
||||
return [4 /*yield*/, ctx.db.get(id)];
|
||||
case 2: return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Update role properties
|
||||
exports.update = (0, server_1.mutation)({
|
||||
args: {
|
||||
id: values_1.v.id("roles"),
|
||||
name: values_1.v.optional(values_1.v.string()),
|
||||
color: values_1.v.optional(values_1.v.string()),
|
||||
permissions: values_1.v.optional(values_1.v.any()),
|
||||
position: values_1.v.optional(values_1.v.number()),
|
||||
isHoist: values_1.v.optional(values_1.v.boolean()),
|
||||
},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var role, id, fields, updates, _i, _a, _b, key, value;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
role = _c.sent();
|
||||
if (!role)
|
||||
throw new Error("Role not found");
|
||||
id = args.id, fields = __rest(args, ["id"]);
|
||||
updates = {};
|
||||
for (_i = 0, _a = Object.entries(fields); _i < _a.length; _i++) {
|
||||
_b = _a[_i], key = _b[0], value = _b[1];
|
||||
if (value !== undefined)
|
||||
updates[key] = value;
|
||||
}
|
||||
if (!(Object.keys(updates).length > 0)) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.patch(id, updates)];
|
||||
case 2:
|
||||
_c.sent();
|
||||
_c.label = 3;
|
||||
case 3: return [4 /*yield*/, ctx.db.get(id)];
|
||||
case 4: return [2 /*return*/, _c.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Delete role
|
||||
exports.remove = (0, server_1.mutation)({
|
||||
args: { id: values_1.v.id("roles") },
|
||||
returns: values_1.v.object({ success: values_1.v.boolean() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var role, assignments, _i, assignments_1, a;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.get(args.id)];
|
||||
case 1:
|
||||
role = _a.sent();
|
||||
if (!role)
|
||||
throw new Error("Role not found");
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_role", function (q) { return q.eq("roleId", args.id); })
|
||||
.collect()];
|
||||
case 2:
|
||||
assignments = _a.sent();
|
||||
_i = 0, assignments_1 = assignments;
|
||||
_a.label = 3;
|
||||
case 3:
|
||||
if (!(_i < assignments_1.length)) return [3 /*break*/, 6];
|
||||
a = assignments_1[_i];
|
||||
return [4 /*yield*/, ctx.db.delete(a._id)];
|
||||
case 4:
|
||||
_a.sent();
|
||||
_a.label = 5;
|
||||
case 5:
|
||||
_i++;
|
||||
return [3 /*break*/, 3];
|
||||
case 6: return [4 /*yield*/, ctx.db.delete(args.id)];
|
||||
case 7:
|
||||
_a.sent();
|
||||
return [2 /*return*/, { success: true }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// List members with roles
|
||||
exports.listMembers = (0, server_1.query)({
|
||||
args: {},
|
||||
returns: values_1.v.array(values_1.v.any()),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var users;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("userProfiles").collect()];
|
||||
case 1:
|
||||
users = _a.sent();
|
||||
return [4 /*yield*/, Promise.all(users.map(function (user) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
_a = {
|
||||
id: user._id,
|
||||
username: user.username,
|
||||
public_identity_key: user.publicIdentityKey
|
||||
};
|
||||
return [4 /*yield*/, getRolesForUser(ctx, user._id)];
|
||||
case 1: return [2 /*return*/, (_a.roles = _b.sent(),
|
||||
_a)];
|
||||
}
|
||||
});
|
||||
}); }))];
|
||||
case 2: return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Assign role to user
|
||||
exports.assign = (0, server_1.mutation)({
|
||||
args: {
|
||||
roleId: values_1.v.id("roles"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
},
|
||||
returns: values_1.v.object({ success: values_1.v.boolean() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user_and_role", function (q) {
|
||||
return q.eq("userId", args.userId).eq("roleId", args.roleId);
|
||||
})
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (!!existing) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.insert("userRoles", {
|
||||
userId: args.userId,
|
||||
roleId: args.roleId,
|
||||
})];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, { success: true }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Remove role from user
|
||||
exports.unassign = (0, server_1.mutation)({
|
||||
args: {
|
||||
roleId: values_1.v.id("roles"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
},
|
||||
returns: values_1.v.object({ success: values_1.v.boolean() }),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("userRoles")
|
||||
.withIndex("by_user_and_role", function (q) {
|
||||
return q.eq("userId", args.userId).eq("roleId", args.roleId);
|
||||
})
|
||||
.unique()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (!existing) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.delete(existing._id)];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, { success: true }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
// Get current user's aggregated permissions
|
||||
exports.getMyPermissions = (0, server_1.query)({
|
||||
args: { userId: values_1.v.id("userProfiles") },
|
||||
returns: values_1.v.object({
|
||||
manage_channels: values_1.v.boolean(),
|
||||
manage_roles: values_1.v.boolean(),
|
||||
manage_messages: values_1.v.boolean(),
|
||||
create_invite: values_1.v.boolean(),
|
||||
embed_links: values_1.v.boolean(),
|
||||
attach_files: values_1.v.boolean(),
|
||||
move_members: values_1.v.boolean(),
|
||||
mute_members: values_1.v.boolean(),
|
||||
}),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles, finalPerms, _loop_1, _i, PERMISSION_KEYS_1, key;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, getRolesForUser(ctx, args.userId)];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
finalPerms = {};
|
||||
_loop_1 = function (key) {
|
||||
finalPerms[key] = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a[key]; });
|
||||
};
|
||||
for (_i = 0, PERMISSION_KEYS_1 = PERMISSION_KEYS; _i < PERMISSION_KEYS_1.length; _i++) {
|
||||
key = PERMISSION_KEYS_1[_i];
|
||||
_loop_1(key);
|
||||
}
|
||||
return [2 /*return*/, finalPerms];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
123
convex/schema.js
Normal file
123
convex/schema.js
Normal file
@@ -0,0 +1,123 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var server_1 = require("convex/server");
|
||||
var values_1 = require("convex/values");
|
||||
exports.default = (0, server_1.defineSchema)({
|
||||
userProfiles: (0, server_1.defineTable)({
|
||||
username: values_1.v.string(),
|
||||
clientSalt: values_1.v.string(),
|
||||
encryptedMasterKey: values_1.v.string(),
|
||||
hashedAuthKey: values_1.v.string(),
|
||||
publicIdentityKey: values_1.v.string(),
|
||||
publicSigningKey: values_1.v.string(),
|
||||
encryptedPrivateKeys: values_1.v.string(),
|
||||
isAdmin: values_1.v.boolean(),
|
||||
status: values_1.v.optional(values_1.v.string()),
|
||||
displayName: values_1.v.optional(values_1.v.string()),
|
||||
avatarStorageId: values_1.v.optional(values_1.v.id("_storage")),
|
||||
aboutMe: values_1.v.optional(values_1.v.string()),
|
||||
customStatus: values_1.v.optional(values_1.v.string()),
|
||||
joinSoundStorageId: values_1.v.optional(values_1.v.id("_storage")),
|
||||
}).index("by_username", ["username"]),
|
||||
categories: (0, server_1.defineTable)({
|
||||
name: values_1.v.string(),
|
||||
position: values_1.v.number(),
|
||||
}).index("by_position", ["position"]),
|
||||
channels: (0, server_1.defineTable)({
|
||||
name: values_1.v.string(),
|
||||
type: values_1.v.string(), // 'text' | 'voice' | 'dm'
|
||||
categoryId: values_1.v.optional(values_1.v.id("categories")),
|
||||
topic: values_1.v.optional(values_1.v.string()),
|
||||
position: values_1.v.optional(values_1.v.number()),
|
||||
}).index("by_name", ["name"])
|
||||
.index("by_category", ["categoryId"]),
|
||||
messages: (0, server_1.defineTable)({
|
||||
channelId: values_1.v.id("channels"),
|
||||
senderId: values_1.v.id("userProfiles"),
|
||||
ciphertext: values_1.v.string(),
|
||||
nonce: values_1.v.string(),
|
||||
signature: values_1.v.string(),
|
||||
keyVersion: values_1.v.number(),
|
||||
replyTo: values_1.v.optional(values_1.v.id("messages")),
|
||||
editedAt: values_1.v.optional(values_1.v.number()),
|
||||
pinned: values_1.v.optional(values_1.v.boolean()),
|
||||
}).index("by_channel", ["channelId"])
|
||||
.index("by_channel_pinned", ["channelId", "pinned"]),
|
||||
messageReactions: (0, server_1.defineTable)({
|
||||
messageId: values_1.v.id("messages"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
emoji: values_1.v.string(),
|
||||
})
|
||||
.index("by_message", ["messageId"])
|
||||
.index("by_message_user_emoji", ["messageId", "userId", "emoji"]),
|
||||
channelKeys: (0, server_1.defineTable)({
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
encryptedKeyBundle: values_1.v.string(),
|
||||
keyVersion: values_1.v.number(),
|
||||
})
|
||||
.index("by_channel", ["channelId"])
|
||||
.index("by_user", ["userId"])
|
||||
.index("by_channel_and_user", ["channelId", "userId"]),
|
||||
roles: (0, server_1.defineTable)({
|
||||
name: values_1.v.string(),
|
||||
color: values_1.v.string(),
|
||||
position: values_1.v.number(),
|
||||
permissions: values_1.v.any(), // JSON object of permissions
|
||||
isHoist: values_1.v.boolean(),
|
||||
}),
|
||||
userRoles: (0, server_1.defineTable)({
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
roleId: values_1.v.id("roles"),
|
||||
})
|
||||
.index("by_user", ["userId"])
|
||||
.index("by_role", ["roleId"])
|
||||
.index("by_user_and_role", ["userId", "roleId"]),
|
||||
invites: (0, server_1.defineTable)({
|
||||
code: values_1.v.string(),
|
||||
encryptedPayload: values_1.v.string(),
|
||||
createdBy: values_1.v.id("userProfiles"),
|
||||
maxUses: values_1.v.optional(values_1.v.number()),
|
||||
uses: values_1.v.number(),
|
||||
expiresAt: values_1.v.optional(values_1.v.number()), // timestamp
|
||||
keyVersion: values_1.v.number(),
|
||||
}).index("by_code", ["code"]),
|
||||
dmParticipants: (0, server_1.defineTable)({
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
})
|
||||
.index("by_channel", ["channelId"])
|
||||
.index("by_user", ["userId"]),
|
||||
typingIndicators: (0, server_1.defineTable)({
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
username: values_1.v.string(),
|
||||
expiresAt: values_1.v.number(), // timestamp
|
||||
}).index("by_channel", ["channelId"]),
|
||||
voiceStates: (0, server_1.defineTable)({
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
username: values_1.v.string(),
|
||||
isMuted: values_1.v.boolean(),
|
||||
isDeafened: values_1.v.boolean(),
|
||||
isScreenSharing: values_1.v.boolean(),
|
||||
isServerMuted: values_1.v.boolean(),
|
||||
watchingStream: values_1.v.optional(values_1.v.id("userProfiles")),
|
||||
})
|
||||
.index("by_channel", ["channelId"])
|
||||
.index("by_user", ["userId"]),
|
||||
channelReadState: (0, server_1.defineTable)({
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
channelId: values_1.v.id("channels"),
|
||||
lastReadTimestamp: values_1.v.number(),
|
||||
})
|
||||
.index("by_user", ["userId"])
|
||||
.index("by_channel", ["channelId"])
|
||||
.index("by_user_and_channel", ["userId", "channelId"]),
|
||||
serverSettings: (0, server_1.defineTable)({
|
||||
serverName: values_1.v.optional(values_1.v.string()),
|
||||
afkChannelId: values_1.v.optional(values_1.v.id("channels")),
|
||||
afkTimeout: values_1.v.number(), // seconds (default 300 = 5 min)
|
||||
iconStorageId: values_1.v.optional(values_1.v.id("_storage")),
|
||||
}),
|
||||
});
|
||||
230
convex/serverSettings.js
Normal file
230
convex/serverSettings.js
Normal file
@@ -0,0 +1,230 @@
|
||||
"use strict";
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.clearAfkChannel = exports.updateIcon = exports.updateName = exports.update = exports.get = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var roles_1 = require("./roles");
|
||||
exports.get = (0, server_1.query)({
|
||||
args: {},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var settings, iconUrl;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("serverSettings").first()];
|
||||
case 1:
|
||||
settings = _a.sent();
|
||||
if (!settings)
|
||||
return [2 /*return*/, null];
|
||||
iconUrl = null;
|
||||
if (!settings.iconStorageId) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.storage.getUrl(settings.iconStorageId)];
|
||||
case 2:
|
||||
iconUrl = _a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, __assign(__assign({}, settings), { iconUrl: iconUrl })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.update = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
afkChannelId: values_1.v.optional(values_1.v.id("channels")),
|
||||
afkTimeout: values_1.v.number(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles, canManage, channel, existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, roles_1.getRolesForUser)(ctx, args.userId)];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
canManage = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a["manage_channels"]; });
|
||||
if (!canManage) {
|
||||
throw new Error("You don't have permission to manage server settings");
|
||||
}
|
||||
// Validate timeout range
|
||||
if (args.afkTimeout < 60 || args.afkTimeout > 3600) {
|
||||
throw new Error("AFK timeout must be between 60 and 3600 seconds");
|
||||
}
|
||||
if (!args.afkChannelId) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.get(args.afkChannelId)];
|
||||
case 2:
|
||||
channel = _a.sent();
|
||||
if (!channel)
|
||||
throw new Error("AFK channel not found");
|
||||
if (channel.type !== "voice")
|
||||
throw new Error("AFK channel must be a voice channel");
|
||||
_a.label = 3;
|
||||
case 3: return [4 /*yield*/, ctx.db.query("serverSettings").first()];
|
||||
case 4:
|
||||
existing = _a.sent();
|
||||
if (!existing) return [3 /*break*/, 6];
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, {
|
||||
afkChannelId: args.afkChannelId,
|
||||
afkTimeout: args.afkTimeout,
|
||||
})];
|
||||
case 5:
|
||||
_a.sent();
|
||||
return [3 /*break*/, 8];
|
||||
case 6: return [4 /*yield*/, ctx.db.insert("serverSettings", {
|
||||
afkChannelId: args.afkChannelId,
|
||||
afkTimeout: args.afkTimeout,
|
||||
})];
|
||||
case 7:
|
||||
_a.sent();
|
||||
_a.label = 8;
|
||||
case 8: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.updateName = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
serverName: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles, canManage, name, existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, roles_1.getRolesForUser)(ctx, args.userId)];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
canManage = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a["manage_channels"]; });
|
||||
if (!canManage) {
|
||||
throw new Error("You don't have permission to manage server settings");
|
||||
}
|
||||
name = args.serverName.trim();
|
||||
if (name.length === 0 || name.length > 100) {
|
||||
throw new Error("Server name must be between 1 and 100 characters");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.query("serverSettings").first()];
|
||||
case 2:
|
||||
existing = _a.sent();
|
||||
if (!existing) return [3 /*break*/, 4];
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, { serverName: name })];
|
||||
case 3:
|
||||
_a.sent();
|
||||
return [3 /*break*/, 6];
|
||||
case 4: return [4 /*yield*/, ctx.db.insert("serverSettings", {
|
||||
serverName: name,
|
||||
afkTimeout: 300,
|
||||
})];
|
||||
case 5:
|
||||
_a.sent();
|
||||
_a.label = 6;
|
||||
case 6: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.updateIcon = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
iconStorageId: values_1.v.optional(values_1.v.id("_storage")),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles, canManage, existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, roles_1.getRolesForUser)(ctx, args.userId)];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
canManage = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a["manage_channels"]; });
|
||||
if (!canManage) {
|
||||
throw new Error("You don't have permission to manage server settings");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.query("serverSettings").first()];
|
||||
case 2:
|
||||
existing = _a.sent();
|
||||
if (!existing) return [3 /*break*/, 4];
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, {
|
||||
iconStorageId: args.iconStorageId,
|
||||
})];
|
||||
case 3:
|
||||
_a.sent();
|
||||
return [3 /*break*/, 6];
|
||||
case 4: return [4 /*yield*/, ctx.db.insert("serverSettings", {
|
||||
iconStorageId: args.iconStorageId,
|
||||
afkTimeout: 300,
|
||||
})];
|
||||
case 5:
|
||||
_a.sent();
|
||||
_a.label = 6;
|
||||
case 6: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.clearAfkChannel = (0, server_1.internalMutation)({
|
||||
args: { channelId: values_1.v.id("channels") },
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var settings;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("serverSettings").first()];
|
||||
case 1:
|
||||
settings = _a.sent();
|
||||
if (!(settings && settings.afkChannelId === args.channelId)) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.patch(settings._id, { afkChannelId: undefined })];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
72
convex/storageUrl.js
Normal file
72
convex/storageUrl.js
Normal file
@@ -0,0 +1,72 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.rewriteToPublicUrl = rewriteToPublicUrl;
|
||||
exports.getPublicStorageUrl = getPublicStorageUrl;
|
||||
// Change this if your public IP changes
|
||||
var PUBLIC_CONVEX_URL = "http://72.26.56.3:3210";
|
||||
/** Rewrite any URL to use the public hostname/port/protocol */
|
||||
function rewriteToPublicUrl(url) {
|
||||
try {
|
||||
var original = new URL(url);
|
||||
var target = new URL(PUBLIC_CONVEX_URL);
|
||||
original.hostname = target.hostname;
|
||||
original.port = target.port;
|
||||
original.protocol = target.protocol;
|
||||
return original.toString();
|
||||
}
|
||||
catch (_a) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
/** Get a storage file URL rewritten to the public address */
|
||||
function getPublicStorageUrl(ctx, storageId) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.storage.getUrl(storageId)];
|
||||
case 1:
|
||||
url = _a.sent();
|
||||
if (!url)
|
||||
return [2 /*return*/, null];
|
||||
return [2 /*return*/, rewriteToPublicUrl(url)];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
167
convex/typing.js
Normal file
167
convex/typing.js
Normal file
@@ -0,0 +1,167 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.cleanExpired = exports.getTyping = exports.stopTyping = exports.startTyping = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var api_1 = require("./_generated/api");
|
||||
var TYPING_TTL_MS = 6000;
|
||||
exports.startTyping = (0, server_1.mutation)({
|
||||
args: {
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
username: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var expiresAt, existing, userTyping;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
expiresAt = Date.now() + TYPING_TTL_MS;
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("typingIndicators")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", args.channelId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
userTyping = existing.find(function (t) { return t.userId === args.userId; });
|
||||
if (!userTyping) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.patch(userTyping._id, { expiresAt: expiresAt })];
|
||||
case 2:
|
||||
_a.sent();
|
||||
return [3 /*break*/, 5];
|
||||
case 3: return [4 /*yield*/, ctx.db.insert("typingIndicators", {
|
||||
channelId: args.channelId,
|
||||
userId: args.userId,
|
||||
username: args.username,
|
||||
expiresAt: expiresAt,
|
||||
})];
|
||||
case 4:
|
||||
_a.sent();
|
||||
_a.label = 5;
|
||||
case 5: return [4 /*yield*/, ctx.scheduler.runAfter(TYPING_TTL_MS, api_1.internal.typing.cleanExpired, {})];
|
||||
case 6:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.stopTyping = (0, server_1.mutation)({
|
||||
args: {
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var indicators, mine;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("typingIndicators")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", args.channelId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
indicators = _a.sent();
|
||||
mine = indicators.find(function (t) { return t.userId === args.userId; });
|
||||
if (!mine) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.delete(mine._id)];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.getTyping = (0, server_1.query)({
|
||||
args: { channelId: values_1.v.id("channels") },
|
||||
returns: values_1.v.array(values_1.v.object({
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
username: values_1.v.string(),
|
||||
})),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var now, indicators;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
now = Date.now();
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("typingIndicators")
|
||||
.withIndex("by_channel", function (q) { return q.eq("channelId", args.channelId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
indicators = _a.sent();
|
||||
return [2 /*return*/, indicators
|
||||
.filter(function (t) { return t.expiresAt > now; })
|
||||
.map(function (t) { return ({ userId: t.userId, username: t.username }); })];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.cleanExpired = (0, server_1.internalMutation)({
|
||||
args: {},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var now, expired, _i, expired_1, t;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
now = Date.now();
|
||||
return [4 /*yield*/, ctx.db.query("typingIndicators").collect()];
|
||||
case 1:
|
||||
expired = _a.sent();
|
||||
_i = 0, expired_1 = expired;
|
||||
_a.label = 2;
|
||||
case 2:
|
||||
if (!(_i < expired_1.length)) return [3 /*break*/, 5];
|
||||
t = expired_1[_i];
|
||||
if (!(t.expiresAt <= now)) return [3 /*break*/, 4];
|
||||
return [4 /*yield*/, ctx.db.delete(t._id)];
|
||||
case 3:
|
||||
_a.sent();
|
||||
_a.label = 4;
|
||||
case 4:
|
||||
_i++;
|
||||
return [3 /*break*/, 2];
|
||||
case 5: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
76
convex/voice.js
Normal file
76
convex/voice.js
Normal file
@@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
"use node";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getToken = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var livekit_server_sdk_1 = require("livekit-server-sdk");
|
||||
// Generate LiveKit token for voice channel
|
||||
exports.getToken = (0, server_1.action)({
|
||||
args: {
|
||||
channelId: values_1.v.string(),
|
||||
userId: values_1.v.string(),
|
||||
username: values_1.v.string(),
|
||||
},
|
||||
returns: values_1.v.object({ token: values_1.v.string() }),
|
||||
handler: function (_ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var apiKey, apiSecret, at, token;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
apiKey = process.env.LIVEKIT_API_KEY || "devkey";
|
||||
apiSecret = process.env.LIVEKIT_API_SECRET || "secret";
|
||||
at = new livekit_server_sdk_1.AccessToken(apiKey, apiSecret, {
|
||||
identity: args.userId,
|
||||
name: args.username,
|
||||
});
|
||||
at.addGrant({
|
||||
roomJoin: true,
|
||||
room: args.channelId,
|
||||
canPublish: true,
|
||||
canSubscribe: true,
|
||||
});
|
||||
return [4 /*yield*/, at.toJwt()];
|
||||
case 1:
|
||||
token = _a.sent();
|
||||
return [2 /*return*/, { token: token }];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
456
convex/voiceState.js
Normal file
456
convex/voiceState.js
Normal file
@@ -0,0 +1,456 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
var __rest = (this && this.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.moveUser = exports.disconnectUser = exports.afkMove = exports.getAll = exports.setWatchingStream = exports.serverMute = exports.updateState = exports.leave = exports.join = void 0;
|
||||
var server_1 = require("./_generated/server");
|
||||
var values_1 = require("convex/values");
|
||||
var storageUrl_1 = require("./storageUrl");
|
||||
var roles_1 = require("./roles");
|
||||
function removeUserVoiceStates(ctx, userId) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var existing, _i, existing_1, vs;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("voiceStates")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", userId); })
|
||||
.collect()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
_i = 0, existing_1 = existing;
|
||||
_a.label = 2;
|
||||
case 2:
|
||||
if (!(_i < existing_1.length)) return [3 /*break*/, 5];
|
||||
vs = existing_1[_i];
|
||||
return [4 /*yield*/, ctx.db.delete(vs._id)];
|
||||
case 3:
|
||||
_a.sent();
|
||||
_a.label = 4;
|
||||
case 4:
|
||||
_i++;
|
||||
return [3 /*break*/, 2];
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.join = (0, server_1.mutation)({
|
||||
args: {
|
||||
channelId: values_1.v.id("channels"),
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
username: values_1.v.string(),
|
||||
isMuted: values_1.v.boolean(),
|
||||
isDeafened: values_1.v.boolean(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, removeUserVoiceStates(ctx, args.userId)];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, ctx.db.insert("voiceStates", {
|
||||
channelId: args.channelId,
|
||||
userId: args.userId,
|
||||
username: args.username,
|
||||
isMuted: args.isMuted,
|
||||
isDeafened: args.isDeafened,
|
||||
isScreenSharing: false,
|
||||
isServerMuted: false,
|
||||
})];
|
||||
case 2:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.leave = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, removeUserVoiceStates(ctx, args.userId)];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.updateState = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
isMuted: values_1.v.optional(values_1.v.boolean()),
|
||||
isDeafened: values_1.v.optional(values_1.v.boolean()),
|
||||
isScreenSharing: values_1.v.optional(values_1.v.boolean()),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing, _1, updates, filtered, allStates, _i, allStates_1, s;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("voiceStates")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.userId); })
|
||||
.first()];
|
||||
case 1:
|
||||
existing = _a.sent();
|
||||
if (!existing) return [3 /*break*/, 7];
|
||||
_1 = args.userId, updates = __rest(args, ["userId"]);
|
||||
filtered = Object.fromEntries(Object.entries(updates).filter(function (_a) {
|
||||
var val = _a[1];
|
||||
return val !== undefined;
|
||||
}));
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, filtered)];
|
||||
case 2:
|
||||
_a.sent();
|
||||
if (!(args.isScreenSharing === false)) return [3 /*break*/, 7];
|
||||
return [4 /*yield*/, ctx.db.query("voiceStates").collect()];
|
||||
case 3:
|
||||
allStates = _a.sent();
|
||||
_i = 0, allStates_1 = allStates;
|
||||
_a.label = 4;
|
||||
case 4:
|
||||
if (!(_i < allStates_1.length)) return [3 /*break*/, 7];
|
||||
s = allStates_1[_i];
|
||||
if (!(s.watchingStream === args.userId)) return [3 /*break*/, 6];
|
||||
return [4 /*yield*/, ctx.db.patch(s._id, { watchingStream: undefined })];
|
||||
case 5:
|
||||
_a.sent();
|
||||
_a.label = 6;
|
||||
case 6:
|
||||
_i++;
|
||||
return [3 /*break*/, 4];
|
||||
case 7: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.serverMute = (0, server_1.mutation)({
|
||||
args: {
|
||||
actorUserId: values_1.v.id("userProfiles"),
|
||||
targetUserId: values_1.v.id("userProfiles"),
|
||||
isServerMuted: values_1.v.boolean(),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles, canMute, existing;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, roles_1.getRolesForUser)(ctx, args.actorUserId)];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
canMute = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a["mute_members"]; });
|
||||
if (!canMute) {
|
||||
throw new Error("You don't have permission to server mute members");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("voiceStates")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.targetUserId); })
|
||||
.first()];
|
||||
case 2:
|
||||
existing = _a.sent();
|
||||
if (!existing)
|
||||
throw new Error("Target user is not in a voice channel");
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, { isServerMuted: args.isServerMuted })];
|
||||
case 3:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.setWatchingStream = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
watchingStream: values_1.v.optional(values_1.v.id("userProfiles")),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var existing;
|
||||
var _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db
|
||||
.query("voiceStates")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.userId); })
|
||||
.first()];
|
||||
case 1:
|
||||
existing = _b.sent();
|
||||
if (!existing) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, ctx.db.patch(existing._id, {
|
||||
watchingStream: (_a = args.watchingStream) !== null && _a !== void 0 ? _a : undefined,
|
||||
})];
|
||||
case 2:
|
||||
_b.sent();
|
||||
_b.label = 3;
|
||||
case 3: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.getAll = (0, server_1.query)({
|
||||
args: {},
|
||||
returns: values_1.v.any(),
|
||||
handler: function (ctx) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var states, grouped, _i, states_1, s, user, avatarUrl, joinSoundUrl;
|
||||
var _a, _b;
|
||||
var _c;
|
||||
return __generator(this, function (_d) {
|
||||
switch (_d.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("voiceStates").collect()];
|
||||
case 1:
|
||||
states = _d.sent();
|
||||
grouped = {};
|
||||
_i = 0, states_1 = states;
|
||||
_d.label = 2;
|
||||
case 2:
|
||||
if (!(_i < states_1.length)) return [3 /*break*/, 9];
|
||||
s = states_1[_i];
|
||||
return [4 /*yield*/, ctx.db.get(s.userId)];
|
||||
case 3:
|
||||
user = _d.sent();
|
||||
avatarUrl = null;
|
||||
if (!(user === null || user === void 0 ? void 0 : user.avatarStorageId)) return [3 /*break*/, 5];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, user.avatarStorageId)];
|
||||
case 4:
|
||||
avatarUrl = _d.sent();
|
||||
_d.label = 5;
|
||||
case 5:
|
||||
joinSoundUrl = null;
|
||||
if (!(user === null || user === void 0 ? void 0 : user.joinSoundStorageId)) return [3 /*break*/, 7];
|
||||
return [4 /*yield*/, (0, storageUrl_1.getPublicStorageUrl)(ctx, user.joinSoundStorageId)];
|
||||
case 6:
|
||||
joinSoundUrl = _d.sent();
|
||||
_d.label = 7;
|
||||
case 7:
|
||||
((_a = grouped[_c = s.channelId]) !== null && _a !== void 0 ? _a : (grouped[_c] = [])).push({
|
||||
userId: s.userId,
|
||||
username: s.username,
|
||||
isMuted: s.isMuted,
|
||||
isDeafened: s.isDeafened,
|
||||
isScreenSharing: s.isScreenSharing,
|
||||
isServerMuted: s.isServerMuted,
|
||||
avatarUrl: avatarUrl,
|
||||
joinSoundUrl: joinSoundUrl,
|
||||
watchingStream: (_b = s.watchingStream) !== null && _b !== void 0 ? _b : null,
|
||||
});
|
||||
_d.label = 8;
|
||||
case 8:
|
||||
_i++;
|
||||
return [3 /*break*/, 2];
|
||||
case 9: return [2 /*return*/, grouped];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.afkMove = (0, server_1.mutation)({
|
||||
args: {
|
||||
userId: values_1.v.id("userProfiles"),
|
||||
afkChannelId: values_1.v.id("channels"),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var settings, currentState, allStates, _i, allStates_2, s;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, ctx.db.query("serverSettings").first()];
|
||||
case 1:
|
||||
settings = _a.sent();
|
||||
if (!settings || settings.afkChannelId !== args.afkChannelId) {
|
||||
throw new Error("Invalid AFK channel");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("voiceStates")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.userId); })
|
||||
.first()];
|
||||
case 2:
|
||||
currentState = _a.sent();
|
||||
// No-op if not in voice or already in AFK channel
|
||||
if (!currentState || currentState.channelId === args.afkChannelId)
|
||||
return [2 /*return*/, null];
|
||||
// Move to AFK channel: delete old state, insert new one muted
|
||||
return [4 /*yield*/, ctx.db.delete(currentState._id)];
|
||||
case 3:
|
||||
// Move to AFK channel: delete old state, insert new one muted
|
||||
_a.sent();
|
||||
return [4 /*yield*/, ctx.db.insert("voiceStates", {
|
||||
channelId: args.afkChannelId,
|
||||
userId: args.userId,
|
||||
username: currentState.username,
|
||||
isMuted: true,
|
||||
isDeafened: currentState.isDeafened,
|
||||
isScreenSharing: false,
|
||||
isServerMuted: currentState.isServerMuted,
|
||||
})];
|
||||
case 4:
|
||||
_a.sent();
|
||||
return [4 /*yield*/, ctx.db.query("voiceStates").collect()];
|
||||
case 5:
|
||||
allStates = _a.sent();
|
||||
_i = 0, allStates_2 = allStates;
|
||||
_a.label = 6;
|
||||
case 6:
|
||||
if (!(_i < allStates_2.length)) return [3 /*break*/, 9];
|
||||
s = allStates_2[_i];
|
||||
if (!(s.watchingStream === args.userId)) return [3 /*break*/, 8];
|
||||
return [4 /*yield*/, ctx.db.patch(s._id, { watchingStream: undefined })];
|
||||
case 7:
|
||||
_a.sent();
|
||||
_a.label = 8;
|
||||
case 8:
|
||||
_i++;
|
||||
return [3 /*break*/, 6];
|
||||
case 9: return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.disconnectUser = (0, server_1.mutation)({
|
||||
args: {
|
||||
actorUserId: values_1.v.id("userProfiles"),
|
||||
targetUserId: values_1.v.id("userProfiles"),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles, canMove, allStates, _i, allStates_3, s;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, roles_1.getRolesForUser)(ctx, args.actorUserId)];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
canMove = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a["move_members"]; });
|
||||
if (!canMove) {
|
||||
throw new Error("You don't have permission to disconnect members");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.query("voiceStates").collect()];
|
||||
case 2:
|
||||
allStates = _a.sent();
|
||||
_i = 0, allStates_3 = allStates;
|
||||
_a.label = 3;
|
||||
case 3:
|
||||
if (!(_i < allStates_3.length)) return [3 /*break*/, 6];
|
||||
s = allStates_3[_i];
|
||||
if (!(s.watchingStream === args.targetUserId)) return [3 /*break*/, 5];
|
||||
return [4 /*yield*/, ctx.db.patch(s._id, { watchingStream: undefined })];
|
||||
case 4:
|
||||
_a.sent();
|
||||
_a.label = 5;
|
||||
case 5:
|
||||
_i++;
|
||||
return [3 /*break*/, 3];
|
||||
case 6: return [4 /*yield*/, removeUserVoiceStates(ctx, args.targetUserId)];
|
||||
case 7:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
exports.moveUser = (0, server_1.mutation)({
|
||||
args: {
|
||||
actorUserId: values_1.v.id("userProfiles"),
|
||||
targetUserId: values_1.v.id("userProfiles"),
|
||||
targetChannelId: values_1.v.id("channels"),
|
||||
},
|
||||
returns: values_1.v.null(),
|
||||
handler: function (ctx, args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var roles, canMove, targetChannel, currentState;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, roles_1.getRolesForUser)(ctx, args.actorUserId)];
|
||||
case 1:
|
||||
roles = _a.sent();
|
||||
canMove = roles.some(function (role) { var _a; return (_a = role.permissions) === null || _a === void 0 ? void 0 : _a["move_members"]; });
|
||||
if (!canMove) {
|
||||
throw new Error("You don't have permission to move members");
|
||||
}
|
||||
return [4 /*yield*/, ctx.db.get(args.targetChannelId)];
|
||||
case 2:
|
||||
targetChannel = _a.sent();
|
||||
if (!targetChannel)
|
||||
throw new Error("Target channel not found");
|
||||
if (targetChannel.type !== "voice")
|
||||
throw new Error("Target channel is not a voice channel");
|
||||
return [4 /*yield*/, ctx.db
|
||||
.query("voiceStates")
|
||||
.withIndex("by_user", function (q) { return q.eq("userId", args.targetUserId); })
|
||||
.first()];
|
||||
case 3:
|
||||
currentState = _a.sent();
|
||||
if (!currentState)
|
||||
throw new Error("Target user is not in a voice channel");
|
||||
// No-op if already in the target channel
|
||||
if (currentState.channelId === args.targetChannelId)
|
||||
return [2 /*return*/, null];
|
||||
// Delete old voice state and insert new one preserving mute/deaf/screenshare
|
||||
return [4 /*yield*/, ctx.db.delete(currentState._id)];
|
||||
case 4:
|
||||
// Delete old voice state and insert new one preserving mute/deaf/screenshare
|
||||
_a.sent();
|
||||
return [4 /*yield*/, ctx.db.insert("voiceStates", {
|
||||
channelId: args.targetChannelId,
|
||||
userId: args.targetUserId,
|
||||
username: currentState.username,
|
||||
isMuted: currentState.isMuted,
|
||||
isDeafened: currentState.isDeafened,
|
||||
isScreenSharing: currentState.isScreenSharing,
|
||||
isServerMuted: currentState.isServerMuted,
|
||||
})];
|
||||
case 5:
|
||||
_a.sent();
|
||||
return [2 /*return*/, null];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
});
|
||||
@@ -229,6 +229,35 @@ export const afkMove = mutation({
|
||||
},
|
||||
});
|
||||
|
||||
export const disconnectUser = mutation({
|
||||
args: {
|
||||
actorUserId: v.id("userProfiles"),
|
||||
targetUserId: v.id("userProfiles"),
|
||||
},
|
||||
returns: v.null(),
|
||||
handler: async (ctx, args) => {
|
||||
const roles = await getRolesForUser(ctx, args.actorUserId);
|
||||
const canMove = roles.some(
|
||||
(role) => (role.permissions as Record<string, boolean>)?.["move_members"]
|
||||
);
|
||||
if (!canMove) {
|
||||
throw new Error("You don't have permission to disconnect members");
|
||||
}
|
||||
|
||||
// Clear viewers watching the target user's stream
|
||||
const allStates = await ctx.db.query("voiceStates").collect();
|
||||
for (const s of allStates) {
|
||||
if (s.watchingStream === args.targetUserId) {
|
||||
await ctx.db.patch(s._id, { watchingStream: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
await removeUserVoiceStates(ctx, args.targetUserId);
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
export const moveUser = mutation({
|
||||
args: {
|
||||
actorUserId: v.id("userProfiles"),
|
||||
|
||||
585
log/log1.txt
585
log/log1.txt
@@ -1,585 +0,0 @@
|
||||
2026-02-13T18:56:03.9277925Z e0b0e1a10f48(version:v0.2.13) received task 41 of job build-and-release, be triggered by event: push
|
||||
2026-02-13T18:56:03.9279801Z workflow prepared
|
||||
2026-02-13T18:56:03.9280260Z evaluating expression 'success()'
|
||||
2026-02-13T18:56:03.9280635Z expression 'success()' evaluated to 'true'
|
||||
2026-02-13T18:56:03.9280737Z 🚀 Start image=moyettes/eb
|
||||
2026-02-13T18:56:03.9315843Z 🐳 docker pull image=moyettes/eb platform= username= forcePull=false
|
||||
2026-02-13T18:56:03.9315992Z 🐳 docker pull moyettes/eb
|
||||
2026-02-13T18:56:03.9322701Z Image exists? true
|
||||
2026-02-13T18:56:03.9421327Z Cleaning up network for job build-and-release, and network name is: GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network
|
||||
2026-02-13T18:56:04.5791683Z 🐳 docker create image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T18:56:04.5902212Z Custom container.Config from options ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image: Volumes:map[] WorkingDir: Entrypoint:[] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T18:56:04.5902464Z Merged container.Config ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image:moyettes/eb Volumes:map[] WorkingDir:/workspace/Moyettes/DiscordClone Entrypoint:[/bin/sleep 10800] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T18:56:04.5902671Z Custom container.HostConfig from options ==> &{Binds:[] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:false VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc000314948 OomKillDisable:0xc000314843 PidsLimit:0xc0003149a8 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T18:56:04.5902878Z --network and --net in the options will be ignored.
|
||||
2026-02-13T18:56:04.5903023Z Merged container.HostConfig ==> &{Binds:[/var/run/docker.sock:/var/run/docker.sock] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:true VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc000314948 OomKillDisable:0xc000314843 PidsLimit:0xc0003149a8 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[{Type:volume Source:act-toolcache Target:/opt/hostedtoolcache ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release-env Target:/var/run/act ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release Target:/workspace/Moyettes/DiscordClone ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>}] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T18:56:06.1535282Z Created container name=GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release id=c552a4a1f514ed2e1c9ef47d4faf8897b005de0741436f0a80ede1e382a52974 from image moyettes/eb (platform: )
|
||||
2026-02-13T18:56:06.1535514Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-02-13T18:56:06.1535590Z 🐳 docker run image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-41_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T18:56:06.1535682Z Starting container: c552a4a1f514ed2e1c9ef47d4faf8897b005de0741436f0a80ede1e382a52974
|
||||
2026-02-13T18:56:08.8613961Z Started container: c552a4a1f514ed2e1c9ef47d4faf8897b005de0741436f0a80ede1e382a52974
|
||||
2026-02-13T18:56:08.9370013Z Writing entry to tarball workflow/event.json len:5058
|
||||
2026-02-13T18:56:08.9370330Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T18:56:08.9370414Z Extracting content to '/var/run/act/'
|
||||
2026-02-13T18:56:08.9433584Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
|
||||
2026-02-13T18:56:08.9433717Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T18:56:09.3391634Z Unable to pull refs/heads/v4: non-fast-forward update
|
||||
2026-02-13T18:56:09.3391874Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T18:56:09.3503369Z Checked out v4
|
||||
2026-02-13T18:56:09.3834837Z ☁ git clone 'https://github.com/actions/cache' # ref=v4
|
||||
2026-02-13T18:56:09.3834986Z cloning https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T18:56:09.5963515Z Unable to pull refs/heads/v4: worktree contains unstaged changes
|
||||
2026-02-13T18:56:09.5963817Z Cloned https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T18:56:09.6440660Z Checked out v4
|
||||
2026-02-13T18:56:09.6545051Z evaluating expression ''
|
||||
2026-02-13T18:56:09.6545296Z expression '' evaluated to 'true'
|
||||
2026-02-13T18:56:09.6545356Z ⭐ Run Main Checkout repository
|
||||
2026-02-13T18:56:09.6545443Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T18:56:09.6545535Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T18:56:09.6545585Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T18:56:09.6545636Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T18:56:09.6545677Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T18:56:09.6545730Z Extracting content to '/var/run/act'
|
||||
2026-02-13T18:56:09.6578043Z expression '${{ github.repository }}' rewritten to 'format('{0}', github.repository)'
|
||||
2026-02-13T18:56:09.6578182Z evaluating expression 'format('{0}', github.repository)'
|
||||
2026-02-13T18:56:09.6578404Z expression 'format('{0}', github.repository)' evaluated to '%!t(string=Moyettes/DiscordClone)'
|
||||
2026-02-13T18:56:09.6578666Z expression '${{ github.token }}' rewritten to 'format('{0}', github.token)'
|
||||
2026-02-13T18:56:09.6578737Z evaluating expression 'format('{0}', github.token)'
|
||||
2026-02-13T18:56:09.6578885Z expression 'format('{0}', github.token)' evaluated to '%!t(string=***)'
|
||||
2026-02-13T18:56:09.6579059Z type=remote-action actionDir=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab actionPath= workdir=/workspace/Moyettes/DiscordClone actionCacheDir=/root/.cache/act actionName=c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab containerActionDir=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T18:56:09.6579185Z /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T18:56:09.6579323Z Removing /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/.gitignore before docker cp
|
||||
2026-02-13T18:56:09.6579849Z 🐳 docker cp src=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ dst=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T18:56:09.6580477Z Writing tarball /tmp/act3990701342 from /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T18:56:09.6580587Z Stripping prefix:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ src:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T18:56:09.6904677Z Extracting content from '/tmp/act3990701342' to '/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/'
|
||||
2026-02-13T18:56:09.7566990Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T18:56:09.7567337Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T18:56:09.7567416Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T18:56:09.7567627Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T18:56:09.8954730Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T18:56:09.8954830Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T18:56:09.8956435Z Syncing repository: Moyettes/DiscordClone
|
||||
2026-02-13T18:56:09.8957540Z ::group::Getting Git version info
|
||||
2026-02-13T18:56:09.8957739Z Working directory is '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T18:56:09.8970747Z [command]/usr/bin/git version
|
||||
2026-02-13T18:56:09.9008141Z git version 2.34.1
|
||||
2026-02-13T18:56:09.9024718Z ::endgroup::
|
||||
2026-02-13T18:56:09.9031378Z Copying '/root/.gitconfig' to '/tmp/40732c9a-9cc3-4b96-b881-da6d2848b7b2/.gitconfig'
|
||||
2026-02-13T18:56:09.9037647Z Temporarily overriding HOME='/tmp/40732c9a-9cc3-4b96-b881-da6d2848b7b2' before making global git config changes
|
||||
2026-02-13T18:56:09.9037895Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T18:56:09.9041756Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T18:56:09.9062633Z Deleting the contents of '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T18:56:09.9064767Z ::group::Initializing the repository
|
||||
2026-02-13T18:56:09.9073312Z [command]/usr/bin/git init /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T18:56:09.9093068Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-02-13T18:56:09.9093226Z hint: is subject to change. To configure the initial branch name to use in all
|
||||
2026-02-13T18:56:09.9093297Z hint: of your new repositories, which will suppress this warning, call:
|
||||
2026-02-13T18:56:09.9093373Z hint:
|
||||
2026-02-13T18:56:09.9093441Z hint: git config --global init.defaultBranch <name>
|
||||
2026-02-13T18:56:09.9093489Z hint:
|
||||
2026-02-13T18:56:09.9093532Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-02-13T18:56:09.9093578Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-02-13T18:56:09.9093625Z hint:
|
||||
2026-02-13T18:56:09.9093662Z hint: git branch -m <name>
|
||||
2026-02-13T18:56:09.9094450Z Initialized empty Git repository in /workspace/Moyettes/DiscordClone/.git/
|
||||
2026-02-13T18:56:09.9099533Z [command]/usr/bin/git remote add origin http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T18:56:09.9115095Z ::endgroup::
|
||||
2026-02-13T18:56:09.9115189Z ::group::Disabling automatic garbage collection
|
||||
2026-02-13T18:56:09.9117322Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-02-13T18:56:09.9131021Z ::endgroup::
|
||||
2026-02-13T18:56:09.9131118Z ::group::Setting up auth
|
||||
2026-02-13T18:56:09.9134113Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T18:56:09.9149874Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T18:56:09.9313358Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T18:56:09.9328018Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T18:56:09.9474257Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T18:56:09.9488458Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T18:56:09.9630037Z [command]/usr/bin/git config --local http.http://192.168.125.15:4000/.extraheader AUTHORIZATION: basic ***
|
||||
2026-02-13T18:56:09.9645770Z ::endgroup::
|
||||
2026-02-13T18:56:09.9645859Z ::group::Fetching the repository
|
||||
2026-02-13T18:56:09.9649291Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +df9d0f2e4b62eb720eeb9808388ec06923a0296b:refs/remotes/origin/main
|
||||
2026-02-13T18:56:12.4387290Z From http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T18:56:12.4387533Z * [new ref] df9d0f2e4b62eb720eeb9808388ec06923a0296b -> origin/main
|
||||
2026-02-13T18:56:12.4400808Z ::endgroup::
|
||||
2026-02-13T18:56:12.4400969Z ::group::Determining the checkout info
|
||||
2026-02-13T18:56:12.4402089Z ::endgroup::
|
||||
2026-02-13T18:56:12.4404843Z [command]/usr/bin/git sparse-checkout disable
|
||||
2026-02-13T18:56:12.4426000Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
|
||||
2026-02-13T18:56:12.4441690Z ::group::Checking out the ref
|
||||
2026-02-13T18:56:12.4443971Z [command]/usr/bin/git checkout --progress --force -B main refs/remotes/origin/main
|
||||
2026-02-13T18:56:12.5345548Z Switched to a new branch 'main'
|
||||
2026-02-13T18:56:12.5345902Z Branch 'main' set up to track remote branch 'main' from 'origin'.
|
||||
2026-02-13T18:56:12.5349777Z ::endgroup::
|
||||
2026-02-13T18:56:12.5376410Z [command]/usr/bin/git log -1 --format=%H
|
||||
2026-02-13T18:56:12.5394523Z df9d0f2e4b62eb720eeb9808388ec06923a0296b
|
||||
2026-02-13T18:56:12.5402828Z ::remove-matcher owner=checkout-git::
|
||||
2026-02-13T18:56:14.1972264Z (node:216) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-02-13T18:56:14.1972406Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-02-13T18:56:34.2182156Z ::warning::Failed to restore: getCacheEntry failed: Request timeout: /_apis/artifactcache/cache?keys=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852%252Cnpm-electron-&version=f2531268ab9c19c75ce7b3eb23cc11c7f69fd3cf796834d4881591e430a373ff
|
||||
2026-02-13T18:56:34.2183457Z Cache not found for input keys: npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852, npm-electron-
|
||||
2026-02-13T18:56:34.9567318Z npm warn deprecated tar@6.2.1: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T18:56:35.2078374Z npm warn deprecated npmlog@6.0.2: This package is no longer supported.
|
||||
2026-02-13T18:56:46.9449453Z npm warn deprecated lodash.isequal@4.5.0: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
|
||||
2026-02-13T18:56:47.1382638Z npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
2026-02-13T18:56:47.1991712Z npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T18:56:47.2034495Z npm warn deprecated gauge@4.0.4: This package is no longer supported.
|
||||
2026-02-13T18:56:47.5067906Z npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
||||
2026-02-13T18:56:47.5186423Z npm warn deprecated are-we-there-yet@3.0.1: This package is no longer supported.
|
||||
2026-02-13T18:56:47.8245140Z npm warn deprecated @npmcli/move-file@2.0.1: This functionality has been moved to @npmcli/fs
|
||||
2026-02-13T18:56:48.4403127Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T18:56:48.4782352Z npm warn deprecated glob@9.3.5: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T18:56:48.5665447Z npm warn deprecated glob@10.5.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T18:56:48.5681963Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T18:56:48.5751339Z npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T18:56:48.5937092Z npm warn deprecated glob@8.1.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T18:56:48.5980233Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T18:56:51.1811306Z
|
||||
2026-02-13T18:56:51.1811605Z added 677 packages, and audited 683 packages in 17s
|
||||
2026-02-13T18:56:51.1811714Z
|
||||
2026-02-13T18:56:51.1811770Z 183 packages are looking for funding
|
||||
2026-02-13T18:56:51.1811897Z run `npm fund` for details
|
||||
2026-02-13T18:56:51.2002954Z
|
||||
2026-02-13T18:56:51.2003207Z 11 vulnerabilities (1 moderate, 10 high)
|
||||
2026-02-13T18:56:51.2003281Z
|
||||
2026-02-13T18:56:51.2003321Z To address all issues (including breaking changes), run:
|
||||
2026-02-13T18:56:51.2003366Z npm audit fix --force
|
||||
2026-02-13T18:56:51.2003405Z
|
||||
2026-02-13T18:56:51.2003444Z Run `npm audit` for details.
|
||||
2026-02-13T18:56:51.4285812Z
|
||||
2026-02-13T18:56:51.4286152Z > @discord-clone/electron@1.0.14 build
|
||||
2026-02-13T18:56:51.4286212Z > vite build
|
||||
2026-02-13T18:56:51.4286284Z
|
||||
2026-02-13T18:56:51.5751519Z [36mvite v7.3.1 [32mbuilding client environment for production...[36m[39m
|
||||
2026-02-13T18:56:51.6098207Z transforming...
|
||||
2026-02-13T18:56:51.9336851Z [33mThe glob option "as" has been deprecated in favour of "query". Please update `as: 'url'` to `query: '?url', import: 'default'`.[39m
|
||||
2026-02-13T18:56:52.0020219Z [33m[plugin vite:esbuild] ../../packages/shared/src/components/ScreenShareModal.jsx: [33mDuplicate key "width" in object literal[33m
|
||||
2026-02-13T18:56:52.0020649Z 71 | }}
|
||||
2026-02-13T18:56:52.0020738Z 72 | >
|
||||
2026-02-13T18:56:52.0020832Z 73 | ...le={{ position: 'relative', width: '100%', height: '250px', width: '450px', borderRadius: '8px', overflow: 'hidden...
|
||||
2026-02-13T18:56:52.0020931Z | ^
|
||||
2026-02-13T18:56:52.0021002Z 74 | {/* Thumbnail/Placeholder */}
|
||||
2026-02-13T18:56:52.0021076Z 75 | {item.thumbnail ? (
|
||||
2026-02-13T18:56:52.0021147Z [39m
|
||||
2026-02-13T18:56:53.8260295Z [32m✓[39m 4636 modules transformed.
|
||||
2026-02-13T18:56:54.3598471Z rendering chunks...
|
||||
2026-02-13T18:56:54.3996107Z computing gzip size...
|
||||
2026-02-13T18:56:54.5742439Z [2mdist-react/[22m[32mindex.html [39m[1m[2m 0.63 kB[22m[1m[22m[2m │ gzip: 0.39 kB[22m
|
||||
2026-02-13T18:56:54.5742839Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher-CXwESYU3.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T18:56:54.5743043Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher_tone2-DWJ6rjnf.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T18:56:54.5743102Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher_tone3-BvnMOsM7.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T18:56:54.5743157Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher_tone4-C9bkU449.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T18:56:54.5743205Z [2mdist-react/[22m[2massets/[22m[32mbubble_tea-Cy1d5egt.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.92 kB[22m
|
||||
2026-02-13T18:56:54.5743263Z [2mdist-react/[22m[2massets/[22m[32mflag_gq-B3TFx5qI.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.68 kB[22m
|
||||
2026-02-13T18:56:54.5743314Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position-9VFgclqE.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5743380Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone1-MK18jaNb.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5743434Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone2-Dzm_xqT2.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5743484Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone4-BtjpxNGo.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T18:56:54.5743541Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone3-DUxbd8tQ.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5743594Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone5-CinKf4VL.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T18:56:54.5743652Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone5-DOWKsCGX.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T18:56:54.5743708Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby-BLUtslbF.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T18:56:54.5743765Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone1-lg4dBAV2.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T18:56:54.5743818Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone2-BuF25R9x.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T18:56:54.5743870Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone3-DEYxzqY-.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T18:56:54.5743938Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone4-IRS8MZPe.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T18:56:54.5743985Z [2mdist-react/[22m[2massets/[22m[32mwoman_zombie-Cn4gQ0af.svg [39m[1m[2m 4.15 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5744041Z [2mdist-react/[22m[2massets/[22m[32mhiking_boot-CPXD60gE.svg [39m[1m[2m 4.17 kB[22m[1m[22m[2m │ gzip: 1.76 kB[22m
|
||||
2026-02-13T18:56:54.5744106Z [2mdist-react/[22m[2massets/[22m[32mjapanese_ogre-BsqNvmIl.svg [39m[1m[2m 4.22 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T18:56:54.5744162Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone5-CuQMgf5h.svg [39m[1m[2m 4.23 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T18:56:54.5744216Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer-D6jKOTyC.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5744271Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone1-A8sdhmvt.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5744409Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone2-DaHNn5-D.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5744462Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone3-DXZ2OdUI.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T18:56:54.5744520Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone4--Oe8w2XD.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T18:56:54.5744568Z [2mdist-react/[22m[2massets/[22m[32mferris_wheel-DvW0t9g3.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.19 kB[22m
|
||||
2026-02-13T18:56:54.5744622Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone5-Bk9uZHaS.svg [39m[1m[2m 4.26 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T18:56:54.5744675Z [2mdist-react/[22m[2massets/[22m[32mman_teacher-BRypTuYs.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T18:56:54.5744723Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone1-jNO2AiRD.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T18:56:54.5744787Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone2-rQoIFfFz.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T18:56:54.5744841Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone3-BEE8k6p5.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T18:56:54.5744889Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone4-C4j4RIq3.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.74 kB[22m
|
||||
2026-02-13T18:56:54.5744942Z [2mdist-react/[22m[2massets/[22m[32msari-BSRA0_R3.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.77 kB[22m
|
||||
2026-02-13T18:56:54.5745006Z [2mdist-react/[22m[2massets/[22m[32mgloves-BcY_RgAR.svg [39m[1m[2m 4.31 kB[22m[1m[22m[2m │ gzip: 1.62 kB[22m
|
||||
2026-02-13T18:56:54.5745060Z [2mdist-react/[22m[2massets/[22m[32mmoon_cake-BQr_VKRq.svg [39m[1m[2m 4.31 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5745114Z [2mdist-react/[22m[2massets/[22m[32mman_zombie-D5T1AZ12.svg [39m[1m[2m 4.34 kB[22m[1m[22m[2m │ gzip: 1.78 kB[22m
|
||||
2026-02-13T18:56:54.5745164Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone1-Mj4OdRWf.svg [39m[1m[2m 4.35 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5745227Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing-22i7hQgf.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T18:56:54.5745284Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone3-D1NUtDA8.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T18:56:54.5745340Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone2-BcJfdMyX.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T18:56:54.5745395Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone4-ByYdFJZp.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T18:56:54.5745462Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone5-CrN9a9WS.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T18:56:54.5745516Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane-DRcmbgmz.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T18:56:54.5745579Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone1-zKjrapc7.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.90 kB[22m
|
||||
2026-02-13T18:56:54.5745639Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone2-CY2wYkQb.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T18:56:54.5745702Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone3-CdJAKQXv.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T18:56:54.5745755Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone4-DXYc5Dlc.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T18:56:54.5745814Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone5-DFAMgo57.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T18:56:54.5745868Z [2mdist-react/[22m[2massets/[22m[32mskier-BTSq18N5.svg [39m[1m[2m 4.39 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T18:56:54.5746002Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone5-Co-5wXNK.svg [39m[1m[2m 4.40 kB[22m[1m[22m[2m │ gzip: 1.84 kB[22m
|
||||
2026-02-13T18:56:54.5746057Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone1-DbW2lM_k.svg [39m[1m[2m 4.42 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T18:56:54.5746114Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane-BarJlRlV.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5746170Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone2-CIeZuoUa.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5746224Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone3-GUZ14VpU.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5746278Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone1-D50RTI5B.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5746331Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone4-Bd6iA8-d.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5746383Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone5-BCpRNGU4.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5746442Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer-skPaDBsj.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.84 kB[22m
|
||||
2026-02-13T18:56:54.5746500Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone2-Bc-xqa4S.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T18:56:54.5746554Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone3-CNRJeino.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T18:56:54.5746617Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone4-CEfcWjkD.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.84 kB[22m
|
||||
2026-02-13T18:56:54.5746677Z [2mdist-react/[22m[2massets/[22m[32mflag_sz-CsAySmAn.svg [39m[1m[2m 4.46 kB[22m[1m[22m[2m │ gzip: 1.66 kB[22m
|
||||
2026-02-13T18:56:54.5746730Z [2mdist-react/[22m[2massets/[22m[32msnowman2-CeWFCRvE.svg [39m[1m[2m 4.48 kB[22m[1m[22m[2m │ gzip: 1.15 kB[22m
|
||||
2026-02-13T18:56:54.5746783Z [2mdist-react/[22m[2massets/[22m[32mman_surfing-fqnQ3hm1.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T18:56:54.5746836Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone1-BeC3CjNB.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.96 kB[22m
|
||||
2026-02-13T18:56:54.5746893Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone3-Dt-HUBR5.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T18:56:54.5746948Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone4-PI8ASA2j.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T18:56:54.5747002Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone5-CyvTB2HT.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T18:56:54.5747054Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone2-CDUKGUjg.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T18:56:54.5747105Z [2mdist-react/[22m[2massets/[22m[32mmotorized_wheelchair-DYMoavTH.svg [39m[1m[2m 4.50 kB[22m[1m[22m[2m │ gzip: 1.63 kB[22m
|
||||
2026-02-13T18:56:54.5747162Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone5-B6CmkSrw.svg [39m[1m[2m 4.54 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T18:56:54.5747220Z [2mdist-react/[22m[2massets/[22m[32mflag_ht-nORDdDQL.svg [39m[1m[2m 4.54 kB[22m[1m[22m[2m │ gzip: 1.88 kB[22m
|
||||
2026-02-13T18:56:54.5747274Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby-p-8aPRtV.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T18:56:54.5747326Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone1-BezQI8D_.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T18:56:54.5747384Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone2-CwnQLRQK.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T18:56:54.5747453Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone3-DlgTa1f-.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T18:56:54.5747517Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone4-DVay5Top.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T18:56:54.5747763Z [2mdist-react/[22m[2massets/[22m[32mflag_ad-CYOJPtjR.svg [39m[1m[2m 4.59 kB[22m[1m[22m[2m │ gzip: 1.79 kB[22m
|
||||
2026-02-13T18:56:54.5747825Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane-DyJEphms.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5747881Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone1-DuB7wHWP.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5747939Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone2-GEDDmrTN.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5747994Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone3-CeM4gv4f.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5748049Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone4-BaEnBpNy.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5748106Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone5-DimRSWot.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5748161Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone1-B_kCGm1b.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T18:56:54.5748222Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing-DEOz-TJs.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T18:56:54.5748275Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone2-D35jmuhN.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.11 kB[22m
|
||||
2026-02-13T18:56:54.5748329Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone3-C60a5Aj1.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T18:56:54.5748382Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone4-nIpCJUpE.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T18:56:54.5748438Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone5-CXXi5x8s.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T18:56:54.5748497Z [2mdist-react/[22m[2massets/[22m[32mflag_tc-Dn_lC0KY.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T18:56:54.5748549Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing-Mc5PuEC7.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5748603Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone2-BbPQ5nEE.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5748654Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone1-DhUQwNf7.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5748710Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone3-CKRqu9yJ.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5748763Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone4-DPEV2yNK.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5748821Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone5-Bgq3Ud_d.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5748878Z [2mdist-react/[22m[2massets/[22m[32mflag_ki-Ccc3Xi24.svg [39m[1m[2m 4.77 kB[22m[1m[22m[2m │ gzip: 1.66 kB[22m
|
||||
2026-02-13T18:56:54.5748931Z [2mdist-react/[22m[2massets/[22m[32mflag_mo-PAf1BQIO.svg [39m[1m[2m 4.77 kB[22m[1m[22m[2m │ gzip: 2.24 kB[22m
|
||||
2026-02-13T18:56:54.5748990Z [2mdist-react/[22m[2massets/[22m[32mflag_sh-CT89bJZi.svg [39m[1m[2m 4.80 kB[22m[1m[22m[2m │ gzip: 1.67 kB[22m
|
||||
2026-02-13T18:56:54.5749044Z [2mdist-react/[22m[2massets/[22m[32mcrab-D6qU1zIW.svg [39m[1m[2m 4.87 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T18:56:54.5749097Z [2mdist-react/[22m[2massets/[22m[32mcouple-KSrP6fk0.svg [39m[1m[2m 4.90 kB[22m[1m[22m[2m │ gzip: 2.04 kB[22m
|
||||
2026-02-13T18:56:54.5749150Z [2mdist-react/[22m[2massets/[22m[32mkiwi-BWXr7Vjo.svg [39m[1m[2m 4.91 kB[22m[1m[22m[2m │ gzip: 2.27 kB[22m
|
||||
2026-02-13T18:56:54.5749209Z [2mdist-react/[22m[2massets/[22m[32mhedgehog-CMNxZzfp.svg [39m[1m[2m 4.92 kB[22m[1m[22m[2m │ gzip: 1.82 kB[22m
|
||||
2026-02-13T18:56:54.5749263Z [2mdist-react/[22m[2massets/[22m[32mmoney_with_wings-BnGoAiwj.svg [39m[1m[2m 4.95 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T18:56:54.5749320Z [2mdist-react/[22m[2massets/[22m[32mflag_kg-D_P2G_Do.svg [39m[1m[2m 4.98 kB[22m[1m[22m[2m │ gzip: 2.14 kB[22m
|
||||
2026-02-13T18:56:54.5749430Z [2mdist-react/[22m[2massets/[22m[32mmaracas-kQiWhg0J.svg [39m[1m[2m 4.99 kB[22m[1m[22m[2m │ gzip: 1.76 kB[22m
|
||||
2026-02-13T18:56:54.5749487Z [2mdist-react/[22m[2massets/[22m[32mx_ray-CWrdKTDm.svg [39m[1m[2m 4.99 kB[22m[1m[22m[2m │ gzip: 2.37 kB[22m
|
||||
2026-02-13T18:56:54.5749545Z [2mdist-react/[22m[2massets/[22m[32mpeople_holding_hands-BRZihiu5.svg [39m[1m[2m 5.03 kB[22m[1m[22m[2m │ gzip: 1.68 kB[22m
|
||||
2026-02-13T18:56:54.5749602Z [2mdist-react/[22m[2massets/[22m[32mbutterfly-AxzpD-Pg.svg [39m[1m[2m 5.04 kB[22m[1m[22m[2m │ gzip: 1.86 kB[22m
|
||||
2026-02-13T18:56:54.5749658Z [2mdist-react/[22m[2massets/[22m[32mflag_xk-D1vfCqOd.svg [39m[1m[2m 5.08 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5749711Z [2mdist-react/[22m[2massets/[22m[32mflag_tm-_4vioey7.svg [39m[1m[2m 5.13 kB[22m[1m[22m[2m │ gzip: 1.54 kB[22m
|
||||
2026-02-13T18:56:54.5749765Z [2mdist-react/[22m[2massets/[22m[32mtwo_men_holding_hands-BKJxHZb_.svg [39m[1m[2m 5.15 kB[22m[1m[22m[2m │ gzip: 1.50 kB[22m
|
||||
2026-02-13T18:56:54.5749824Z [2mdist-react/[22m[2massets/[22m[32mseal-Djs0F0U5.svg [39m[1m[2m 5.16 kB[22m[1m[22m[2m │ gzip: 2.18 kB[22m
|
||||
2026-02-13T18:56:54.5750001Z [2mdist-react/[22m[2massets/[22m[32mspeak_no_evil-EoRZCJhS.svg [39m[1m[2m 5.20 kB[22m[1m[22m[2m │ gzip: 2.27 kB[22m
|
||||
2026-02-13T18:56:54.5750059Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone1-XI7g5maV.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5750113Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone2-DBvANdsI.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5750168Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone3-BK7ka3J3.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5750224Z [2mdist-react/[22m[2massets/[22m[32mman_dancing-Dg-6O6t7.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5750280Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone4-D9FZRxV5.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5750331Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone5-CQh9niVO.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T18:56:54.5750386Z [2mdist-react/[22m[2massets/[22m[32mflag_pf-OA_PTTaZ.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 1.81 kB[22m
|
||||
2026-02-13T18:56:54.5750442Z [2mdist-react/[22m[2massets/[22m[32mpie-DZ6nmSau.svg [39m[1m[2m 5.24 kB[22m[1m[22m[2m │ gzip: 1.98 kB[22m
|
||||
2026-02-13T18:56:54.5750496Z [2mdist-react/[22m[2massets/[22m[32mtwo_women_holding_hands-CnKtKnaZ.svg [39m[1m[2m 5.29 kB[22m[1m[22m[2m │ gzip: 1.71 kB[22m
|
||||
2026-02-13T18:56:54.5750552Z [2mdist-react/[22m[2massets/[22m[32mbrain-Czvux5Q4.svg [39m[1m[2m 5.32 kB[22m[1m[22m[2m │ gzip: 2.48 kB[22m
|
||||
2026-02-13T18:56:54.5750604Z [2mdist-react/[22m[2massets/[22m[32mlacrosse-DK95k1kF.svg [39m[1m[2m 5.33 kB[22m[1m[22m[2m │ gzip: 2.22 kB[22m
|
||||
2026-02-13T18:56:54.5750656Z [2mdist-react/[22m[2massets/[22m[32msee_no_evil-DnuksPIR.svg [39m[1m[2m 5.42 kB[22m[1m[22m[2m │ gzip: 2.38 kB[22m
|
||||
2026-02-13T18:56:54.5750712Z [2mdist-react/[22m[2massets/[22m[32mflag_vi-vzZjsoBi.svg [39m[1m[2m 5.43 kB[22m[1m[22m[2m │ gzip: 2.34 kB[22m
|
||||
2026-02-13T18:56:54.5750764Z [2mdist-react/[22m[2massets/[22m[32mdodo-CoZFlciJ.svg [39m[1m[2m 5.44 kB[22m[1m[22m[2m │ gzip: 2.31 kB[22m
|
||||
2026-02-13T18:56:54.5750818Z [2mdist-react/[22m[2massets/[22m[32mflag_hk-CzNuCBPg.svg [39m[1m[2m 5.46 kB[22m[1m[22m[2m │ gzip: 2.52 kB[22m
|
||||
2026-02-13T18:56:54.5750873Z [2mdist-react/[22m[2massets/[22m[32mspider_web-DPcv-q20.svg [39m[1m[2m 5.50 kB[22m[1m[22m[2m │ gzip: 2.43 kB[22m
|
||||
2026-02-13T18:56:54.5750929Z [2mdist-react/[22m[2massets/[22m[32mflag_bl-BoaeaHPp.svg [39m[1m[2m 5.58 kB[22m[1m[22m[2m │ gzip: 1.90 kB[22m
|
||||
2026-02-13T18:56:54.5750981Z [2mdist-react/[22m[2massets/[22m[32mflag_as-B43i20pO.svg [39m[1m[2m 5.61 kB[22m[1m[22m[2m │ gzip: 2.39 kB[22m
|
||||
2026-02-13T18:56:54.5751026Z [2mdist-react/[22m[2massets/[22m[32mflag_gp-DW1UVBGw.svg [39m[1m[2m 5.63 kB[22m[1m[22m[2m │ gzip: 2.05 kB[22m
|
||||
2026-02-13T18:56:54.5751084Z [2mdist-react/[22m[2massets/[22m[32mflag_ic-BrB5Xakj.svg [39m[1m[2m 5.63 kB[22m[1m[22m[2m │ gzip: 2.24 kB[22m
|
||||
2026-02-13T18:56:54.5751136Z [2mdist-react/[22m[2massets/[22m[32mman_golfing-DhSLN6KQ.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T18:56:54.5751198Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone3-vxj0o6sI.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T18:56:54.5751250Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone1-DBE1f7b1.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T18:56:54.5751305Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone2-CNmTGsfk.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T18:56:54.5751404Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone4-CsGYmisz.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T18:56:54.5751470Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone5-Cf_z4uyg.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.60 kB[22m
|
||||
2026-02-13T18:56:54.5751526Z [2mdist-react/[22m[2massets/[22m[32mflag_bm-CzSakp_Z.svg [39m[1m[2m 5.67 kB[22m[1m[22m[2m │ gzip: 2.37 kB[22m
|
||||
2026-02-13T18:56:54.5751569Z [2mdist-react/[22m[2massets/[22m[32mmap-BGXvLkiw.svg [39m[1m[2m 5.68 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5751626Z [2mdist-react/[22m[2massets/[22m[32mpeople_wrestling-DjCLlDDS.svg [39m[1m[2m 5.71 kB[22m[1m[22m[2m │ gzip: 2.48 kB[22m
|
||||
2026-02-13T18:56:54.5751672Z [2mdist-react/[22m[2massets/[22m[32mtamale-2biJGrAo.svg [39m[1m[2m 5.74 kB[22m[1m[22m[2m │ gzip: 2.53 kB[22m
|
||||
2026-02-13T18:56:54.5751727Z [2mdist-react/[22m[2massets/[22m[32mmen_wrestling-BNuLmHCV.svg [39m[1m[2m 5.74 kB[22m[1m[22m[2m │ gzip: 2.47 kB[22m
|
||||
2026-02-13T18:56:54.5751779Z [2mdist-react/[22m[2massets/[22m[32mempty_nest-DGy7reBo.svg [39m[1m[2m 5.79 kB[22m[1m[22m[2m │ gzip: 2.71 kB[22m
|
||||
2026-02-13T18:56:54.5751831Z [2mdist-react/[22m[2massets/[22m[32mworm-CxRJMG1n.svg [39m[1m[2m 5.86 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T18:56:54.5751883Z [2mdist-react/[22m[2massets/[22m[32mflag_bo-B7hNQ755.svg [39m[1m[2m 5.91 kB[22m[1m[22m[2m │ gzip: 2.22 kB[22m
|
||||
2026-02-13T18:56:54.5751940Z [2mdist-react/[22m[2massets/[22m[32mface_in_clouds-DBzCKo8S.svg [39m[1m[2m 5.94 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T18:56:54.5751995Z [2mdist-react/[22m[2massets/[22m[32mwomen_wrestling-CARP3ZvF.svg [39m[1m[2m 5.96 kB[22m[1m[22m[2m │ gzip: 2.59 kB[22m
|
||||
2026-02-13T18:56:54.5752046Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone5-cnCH-jDP.svg [39m[1m[2m 6.00 kB[22m[1m[22m[2m │ gzip: 2.06 kB[22m
|
||||
2026-02-13T18:56:54.5752106Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights-DkiBT0IO.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T18:56:54.5752165Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone1-DGilOf2d.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.06 kB[22m
|
||||
2026-02-13T18:56:54.5752280Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone2-CXUv2fBp.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T18:56:54.5752334Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone4-MLQqpJKZ.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T18:56:54.5752391Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone3-DhF3q93u.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T18:56:54.5752446Z [2mdist-react/[22m[2massets/[22m[32mflag_fj-B2-D6gPQ.svg [39m[1m[2m 6.04 kB[22m[1m[22m[2m │ gzip: 2.64 kB[22m
|
||||
2026-02-13T18:56:54.5752646Z [2mdist-react/[22m[2massets/[22m[32mflag_pn-Bde7vecB.svg [39m[1m[2m 6.05 kB[22m[1m[22m[2m │ gzip: 2.68 kB[22m
|
||||
2026-02-13T18:56:54.5752704Z [2mdist-react/[22m[2massets/[22m[32mflag_bt-COHVTZ6I.svg [39m[1m[2m 6.06 kB[22m[1m[22m[2m │ gzip: 2.52 kB[22m
|
||||
2026-02-13T18:56:54.5752755Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel-B6e7BEW_.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T18:56:54.5752811Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone2-BR4ztGzg.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T18:56:54.5752869Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone1-TBt_b-Oj.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T18:56:54.5752924Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone4-j074vq-9.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T18:56:54.5752981Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone3-BzmNF0vv.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T18:56:54.5753034Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone5-BzNEt2oA.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T18:56:54.5753095Z [2mdist-react/[22m[2massets/[22m[32maccordion-BPueGNgN.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 1.20 kB[22m
|
||||
2026-02-13T18:56:54.5753150Z [2mdist-react/[22m[2massets/[22m[32mlobster-Cfls8jg_.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.32 kB[22m
|
||||
2026-02-13T18:56:54.5753209Z [2mdist-react/[22m[2massets/[22m[32mvolcano-Bh_Lqk9r.svg [39m[1m[2m 6.14 kB[22m[1m[22m[2m │ gzip: 2.70 kB[22m
|
||||
2026-02-13T18:56:54.5753270Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling-NFQt9ZB9.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5753324Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone1-B3S_eUE1.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5753378Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone3-D2kqEChS.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5753437Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone2-CYBBI2iM.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5753493Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone4-B96D58fZ.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5753547Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone5-PFLWmq7Q.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5753600Z [2mdist-react/[22m[2massets/[22m[32mflag_lb-DHr4ylgr.svg [39m[1m[2m 6.19 kB[22m[1m[22m[2m │ gzip: 2.75 kB[22m
|
||||
2026-02-13T18:56:54.5753656Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone1-22QBgB92.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5753709Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone2-Bs8PtV12.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5753765Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball-C_yN7fGQ.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5753821Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone3-q-BDso_I.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5753873Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone4-BUH96fLA.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5753933Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone5-DK-UJ5SH.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5753987Z [2mdist-react/[22m[2massets/[22m[32mflag_fk-1KKBtSFw.svg [39m[1m[2m 6.22 kB[22m[1m[22m[2m │ gzip: 2.54 kB[22m
|
||||
2026-02-13T18:56:54.5754043Z [2mdist-react/[22m[2massets/[22m[32mflag_rs-CmpxaRIS.svg [39m[1m[2m 6.23 kB[22m[1m[22m[2m │ gzip: 2.35 kB[22m
|
||||
2026-02-13T18:56:54.5754095Z [2mdist-react/[22m[2massets/[22m[32mcucumber-oVkPYVB9.svg [39m[1m[2m 6.24 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T18:56:54.5754149Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone5-BJQrRdVE.svg [39m[1m[2m 6.25 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5754202Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone1-BpRsBk7z.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5754259Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone2-P18Nfbuz.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5754312Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights-CsixMYFL.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5754380Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone4-CQZmiYUl.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.16 kB[22m
|
||||
2026-02-13T18:56:54.5754433Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone3-C0gnGp49.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T18:56:54.5754487Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone5-DEciUSJH.svg [39m[1m[2m 6.29 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T18:56:54.5754567Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone1-CXfKAA0L.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.19 kB[22m
|
||||
2026-02-13T18:56:54.5754638Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights-Cn0dQ6qY.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T18:56:54.5754704Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone2-Dkw3-09P.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T18:56:54.5754780Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone4-C62SuN24.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T18:56:54.5754838Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone3-3OqiHF7e.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T18:56:54.5754893Z [2mdist-react/[22m[2massets/[22m[32mwomen_with_bunny_ears_partying-CKr9TLic.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 1.68 kB[22m
|
||||
2026-02-13T18:56:54.5754946Z [2mdist-react/[22m[2massets/[22m[32mflag_sm-BYO1ASeM.svg [39m[1m[2m 6.31 kB[22m[1m[22m[2m │ gzip: 2.30 kB[22m
|
||||
2026-02-13T18:56:54.5754999Z [2mdist-react/[22m[2massets/[22m[32mcoat-Cbu3wnI6.svg [39m[1m[2m 6.35 kB[22m[1m[22m[2m │ gzip: 2.52 kB[22m
|
||||
2026-02-13T18:56:54.5755052Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling-tGvm940R.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.78 kB[22m
|
||||
2026-02-13T18:56:54.5755103Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone1-fJFXi2hD.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.78 kB[22m
|
||||
2026-02-13T18:56:54.5755167Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone2-C5lE2K9g.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T18:56:54.5755221Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone4-CjyM2w54.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T18:56:54.5755275Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone3-BourpL3A.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T18:56:54.5755335Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone5-D-eW47Ua.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T18:56:54.5755399Z [2mdist-react/[22m[2massets/[22m[32mman_running-Bp7fZpx0.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T18:56:54.5755452Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone1-BbRoQah0.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T18:56:54.5755519Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone2-gBe1A9EP.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T18:56:54.5755594Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone3-DfAx9qZO.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T18:56:54.5755820Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone4-CeeXJkX_.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T18:56:54.5755889Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone5-Do-aIXEX.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T18:56:54.5755958Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone1-B_P42W0r.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T18:56:54.5756023Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball-fiyPmBDz.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T18:56:54.5756077Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone2-BtTxnxhZ.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T18:56:54.5756133Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone3-C7TXAAWV.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T18:56:54.5756186Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone4-CtCwRGCv.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T18:56:54.5756240Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone5-CmZlugee.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T18:56:54.5756296Z [2mdist-react/[22m[2massets/[22m[32mmicrobe-DHWlm4x3.svg [39m[1m[2m 6.48 kB[22m[1m[22m[2m │ gzip: 2.82 kB[22m
|
||||
2026-02-13T18:56:54.5756348Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing-Cd5KXigQ.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T18:56:54.5757442Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone1-BPFu29EM.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T18:56:54.5760546Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone2-kHM6lt0G.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T18:56:54.5760767Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone3-1prjoMK9.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T18:56:54.5761315Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone4-DZVx5-VD.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T18:56:54.5761996Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone5-DoKtvypB.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T18:56:54.5762193Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair-B2ofcHYu.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T18:56:54.5762355Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone1-BrR0l2XR.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T18:56:54.5763623Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone2-DmJ1Zffk.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T18:56:54.5763954Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone3-Bt_5AaRy.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T18:56:54.5764075Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone4-TZTDWyKD.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T18:56:54.5764178Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone5-DrOKlCDl.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T18:56:54.5764280Z [2mdist-react/[22m[2massets/[22m[32mburrito-B4L0kbwK.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.58 kB[22m
|
||||
2026-02-13T18:56:54.5764373Z [2mdist-react/[22m[2massets/[22m[32mperson_running-DNDUEkxU.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T18:56:54.5764461Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone1-B8sLRwke.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T18:56:54.5764558Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone2-DNzEDUb0.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T18:56:54.5764644Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone3-Dist2leS.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T18:56:54.5764737Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone4-DVBWC3-p.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T18:56:54.5764831Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone5-DEOJVy8u.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.93 kB[22m
|
||||
2026-02-13T18:56:54.5764922Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone1-Da2hybrT.svg [39m[1m[2m 6.57 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T18:56:54.5765018Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair-cGfKOLRc.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T18:56:54.5765114Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone2-BPBmkRcs.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T18:56:54.5765206Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone3-H5kpv3q_.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T18:56:54.5765291Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone4-BvKWPBcq.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T18:56:54.5765377Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone5-YZQTD5Nr.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T18:56:54.5765461Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone1-CbOONp_g.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5765554Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball-CH3hWpQR.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5765641Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone4-BsA09Avm.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5765729Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone2-jeC51_-P.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5765819Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone3-BGgWTsuS.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5765904Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone5-D_rmeJiN.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5766001Z [2mdist-react/[22m[2massets/[22m[32mmen_with_bunny_ears_partying-DabknRQ1.svg [39m[1m[2m 6.64 kB[22m[1m[22m[2m │ gzip: 1.78 kB[22m
|
||||
2026-02-13T18:56:54.5766089Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone1-BrCW39oq.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T18:56:54.5766175Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball-BCtAjpGP.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.04 kB[22m
|
||||
2026-02-13T18:56:54.5766259Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone3-CMYhYDFZ.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T18:56:54.5766343Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone2-pU3f7Oqo.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T18:56:54.5766429Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone4-BonEB_V5.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T18:56:54.5766516Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone5-mVU7qtFm.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T18:56:54.5766602Z [2mdist-react/[22m[2massets/[22m[32mwoman_running-_mwbLWM0.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T18:56:54.5766690Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone1-Dfqdg043.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T18:56:54.5766775Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone2-rXRqTMa0.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T18:56:54.5766860Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone3-BmRDPwCM.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T18:56:54.5766942Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone4-DmFzAsxD.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T18:56:54.5767033Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone5-C66GYSAh.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T18:56:54.5767122Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair-Ba72kfnU.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5767204Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone1-Ce9x88Rf.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5767291Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone2-CAKIPnJE.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5767378Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone3-D4YsEoBp.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5767464Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone4-BD3k04p2.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5767550Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone5-BmBeJ4-f.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T18:56:54.5767642Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair-DxhhvjYe.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5767809Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone1-Dcta4qUb.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.68 kB[22m
|
||||
2026-02-13T18:56:54.5767902Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone2-C8UQYonN.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5767986Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone3-BRD_Obbg.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5768077Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone4-DLSO0rlF.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5768163Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone5-SnULyxgF.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T18:56:54.5768244Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball-H1IsbPT2.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T18:56:54.5768326Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone2-9V5mlEG0.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T18:56:54.5768408Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone1-BIhBY2_P.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T18:56:54.5768960Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone3-DSpJYpZ1.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T18:56:54.5769102Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone4-BycyNnMy.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T18:56:54.5769171Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone5-C9pS5gcg.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T18:56:54.5769231Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone1-B-J_H3TB.svg [39m[1m[2m 6.85 kB[22m[1m[22m[2m │ gzip: 2.71 kB[22m
|
||||
2026-02-13T18:56:54.5769291Z [2mdist-react/[22m[2massets/[22m[32mflag_ms-BKjfidu-.svg [39m[1m[2m 6.86 kB[22m[1m[22m[2m │ gzip: 3.00 kB[22m
|
||||
2026-02-13T18:56:54.5769373Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair-CiMQlH-Z.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T18:56:54.5769434Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone2-DQy0C3Cx.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T18:56:54.5769492Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone3-DuduwQoe.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T18:56:54.5769549Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone4-CoEn9n-F.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T18:56:54.5769606Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone5-CgvQDAuT.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T18:56:54.5769659Z [2mdist-react/[22m[2massets/[22m[32mflag_ky-E8sT-Yzf.svg [39m[1m[2m 6.99 kB[22m[1m[22m[2m │ gzip: 2.92 kB[22m
|
||||
2026-02-13T18:56:54.5769714Z [2mdist-react/[22m[2massets/[22m[32manatomical_heart-DbQDqK_8.svg [39m[1m[2m 7.00 kB[22m[1m[22m[2m │ gzip: 3.14 kB[22m
|
||||
2026-02-13T18:56:54.5769770Z [2mdist-react/[22m[2massets/[22m[32mwales-ll0ySOk-.svg [39m[1m[2m 7.01 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T18:56:54.5769824Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair-CIaEP3y5.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T18:56:54.5769884Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone1-1BibIgKr.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.78 kB[22m
|
||||
2026-02-13T18:56:54.5769938Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone2-uhLYilhF.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T18:56:54.5769994Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone3-B51r71l0.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T18:56:54.5770049Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone4-oIvpxZcp.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T18:56:54.5770102Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone5-_fFN26h0.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T18:56:54.5770166Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball-B4V8jGG-.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.20 kB[22m
|
||||
2026-02-13T18:56:54.5770219Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone2-BPyPsinZ.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T18:56:54.5770273Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone1-I7gUQpbX.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T18:56:54.5770327Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone4-CtQI59zT.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T18:56:54.5770385Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone3-UqVs8gxM.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T18:56:54.5770438Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone5-BgHu12i2.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T18:56:54.5770489Z [2mdist-react/[22m[2massets/[22m[32mflag_va-BB2uDrB0.svg [39m[1m[2m 7.21 kB[22m[1m[22m[2m │ gzip: 2.49 kB[22m
|
||||
2026-02-13T18:56:54.5770541Z [2mdist-react/[22m[2massets/[22m[32mmammoth-Diaisynz.svg [39m[1m[2m 7.29 kB[22m[1m[22m[2m │ gzip: 3.06 kB[22m
|
||||
2026-02-13T18:56:54.5770595Z [2mdist-react/[22m[2massets/[22m[32mnest_with_eggs-C5ulh3Rz.svg [39m[1m[2m 7.36 kB[22m[1m[22m[2m │ gzip: 3.39 kB[22m
|
||||
2026-02-13T18:56:54.5770656Z [2mdist-react/[22m[2massets/[22m[32mphoenix-QKXqSCuH.svg [39m[1m[2m 7.57 kB[22m[1m[22m[2m │ gzip: 2.91 kB[22m
|
||||
2026-02-13T18:56:54.5770712Z [2mdist-react/[22m[2massets/[22m[32mflag_cy-JKjUtxO9.svg [39m[1m[2m 7.60 kB[22m[1m[22m[2m │ gzip: 3.32 kB[22m
|
||||
2026-02-13T18:56:54.5770764Z [2mdist-react/[22m[2massets/[22m[32mpeople_with_bunny_ears_partying-BVR6SBwD.svg [39m[1m[2m 7.63 kB[22m[1m[22m[2m │ gzip: 1.93 kB[22m
|
||||
2026-02-13T18:56:54.5770822Z [2mdist-react/[22m[2massets/[22m[32mflag_gu-CyZZwWUz.svg [39m[1m[2m 7.68 kB[22m[1m[22m[2m │ gzip: 3.24 kB[22m
|
||||
2026-02-13T18:56:54.5770877Z [2mdist-react/[22m[2massets/[22m[32mmute-CFH4QcSE.mp3 [39m[1m[2m 7.74 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5770934Z [2mdist-react/[22m[2massets/[22m[32mt_rex-BYG-fgI4.svg [39m[1m[2m 8.00 kB[22m[1m[22m[2m │ gzip: 3.35 kB[22m
|
||||
2026-02-13T18:56:54.5770987Z [2mdist-react/[22m[2massets/[22m[32mflag_vg-DWuAWiyw.svg [39m[1m[2m 8.21 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T18:56:54.5771040Z [2mdist-react/[22m[2massets/[22m[32mflag_yt-BfOxXbO5.svg [39m[1m[2m 8.42 kB[22m[1m[22m[2m │ gzip: 2.95 kB[22m
|
||||
2026-02-13T18:56:54.5771092Z [2mdist-react/[22m[2massets/[22m[32mpiñata-CQK6iMPe.svg [39m[1m[2m 8.47 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T18:56:54.5771148Z [2mdist-react/[22m[2massets/[22m[32mmirror_ball-R_criUm_.svg [39m[1m[2m 8.55 kB[22m[1m[22m[2m │ gzip: 3.31 kB[22m
|
||||
2026-02-13T18:56:54.5771202Z [2mdist-react/[22m[2massets/[22m[32mping-LfakLpwb.mp3 [39m[1m[2m 8.58 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5771255Z [2mdist-react/[22m[2massets/[22m[32mflag_gs-DhFNtBGF.svg [39m[1m[2m 8.86 kB[22m[1m[22m[2m │ gzip: 3.67 kB[22m
|
||||
2026-02-13T18:56:54.5771310Z [2mdist-react/[22m[2massets/[22m[32mknot-CpRGiIMe.svg [39m[1m[2m 8.92 kB[22m[1m[22m[2m │ gzip: 3.89 kB[22m
|
||||
2026-02-13T18:56:54.5771365Z [2mdist-react/[22m[2massets/[22m[32mflag_dg-DwJEN7pv.svg [39m[1m[2m 9.06 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T18:56:54.5771423Z [2mdist-react/[22m[2massets/[22m[32mflag_gt-CietPgvg.svg [39m[1m[2m 9.11 kB[22m[1m[22m[2m │ gzip: 3.86 kB[22m
|
||||
2026-02-13T18:56:54.5771475Z [2mdist-react/[22m[2massets/[22m[32mflag_mx-g-aNhK9D.svg [39m[1m[2m 9.66 kB[22m[1m[22m[2m │ gzip: 3.72 kB[22m
|
||||
2026-02-13T18:56:54.5771529Z [2mdist-react/[22m[2massets/[22m[32mflag_ta-Q6DTxsoW.svg [39m[1m[2m 10.30 kB[22m[1m[22m[2m │ gzip: 4.05 kB[22m
|
||||
2026-02-13T18:56:54.5771581Z [2mdist-react/[22m[2massets/[22m[32mflag_je-CGBxZBdT.svg [39m[1m[2m 10.35 kB[22m[1m[22m[2m │ gzip: 4.17 kB[22m
|
||||
2026-02-13T18:56:54.5771635Z [2mdist-react/[22m[2massets/[22m[32munmute-BDrGNM7s.mp3 [39m[1m[2m 10.67 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5771698Z [2mdist-react/[22m[2massets/[22m[32mflag_do-sBcfT32z.svg [39m[1m[2m 11.37 kB[22m[1m[22m[2m │ gzip: 4.67 kB[22m
|
||||
2026-02-13T18:56:54.5771751Z [2mdist-react/[22m[2massets/[22m[32mdeafen-BWE6ozKl.mp3 [39m[1m[2m 11.92 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5771806Z [2mdist-react/[22m[2massets/[22m[32mflag_sa-B3EC8eCD.svg [39m[1m[2m 12.29 kB[22m[1m[22m[2m │ gzip: 5.12 kB[22m
|
||||
2026-02-13T18:56:54.5771859Z [2mdist-react/[22m[2massets/[22m[32mflag_al-D439po3l.svg [39m[1m[2m 12.43 kB[22m[1m[22m[2m │ gzip: 5.32 kB[22m
|
||||
2026-02-13T18:56:54.5771914Z [2mdist-react/[22m[2massets/[22m[32mundeafen-DHeUZCMd.mp3 [39m[1m[2m 13.59 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5771968Z [2mdist-react/[22m[2massets/[22m[32mflag_bz-B34xZjVJ.svg [39m[1m[2m 13.64 kB[22m[1m[22m[2m │ gzip: 5.44 kB[22m
|
||||
2026-02-13T18:56:54.5772022Z [2mdist-react/[22m[2massets/[22m[32mflag_pm-C-C2d-w4.svg [39m[1m[2m 13.65 kB[22m[1m[22m[2m │ gzip: 3.80 kB[22m
|
||||
2026-02-13T18:56:54.5772076Z [2mdist-react/[22m[2massets/[22m[32mleave_call-DtItyZMg.mp3 [39m[1m[2m 14.43 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772128Z [2mdist-react/[22m[2massets/[22m[32mflag_nf-BjOIhoMF.svg [39m[1m[2m 14.69 kB[22m[1m[22m[2m │ gzip: 5.98 kB[22m
|
||||
2026-02-13T18:56:54.5772177Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_start-B8JctOq8.mp3 [39m[1m[2m 16.13 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772231Z [2mdist-react/[22m[2massets/[22m[32mflag_ac-Dr8n8VBW.svg [39m[1m[2m 16.66 kB[22m[1m[22m[2m │ gzip: 5.62 kB[22m
|
||||
2026-02-13T18:56:54.5772286Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_stop-DhppajDk.mp3 [39m[1m[2m 18.43 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772344Z [2mdist-react/[22m[2massets/[22m[32mpotted_plant-BHg6K0D8.svg [39m[1m[2m 21.00 kB[22m[1m[22m[2m │ gzip: 8.96 kB[22m
|
||||
2026-02-13T18:56:54.5772397Z [2mdist-react/[22m[2massets/[22m[32mflag_mp-Bs0Xr_ND.svg [39m[1m[2m 24.06 kB[22m[1m[22m[2m │ gzip: 9.57 kB[22m
|
||||
2026-02-13T18:56:54.5772452Z [2mdist-react/[22m[2massets/[22m[32mflag_af-CN78RMpg.svg [39m[1m[2m 24.13 kB[22m[1m[22m[2m │ gzip: 9.18 kB[22m
|
||||
2026-02-13T18:56:54.5772504Z [2mdist-react/[22m[2massets/[22m[32mflag_kz-D77IkgDL.svg [39m[1m[2m 26.58 kB[22m[1m[22m[2m │ gzip: 9.02 kB[22m
|
||||
2026-02-13T18:56:54.5772555Z [2mdist-react/[22m[2massets/[22m[32munited_nations-BC9awctQ.svg [39m[1m[2m 26.58 kB[22m[1m[22m[2m │ gzip: 10.32 kB[22m
|
||||
2026-02-13T18:56:54.5772610Z [2mdist-react/[22m[2massets/[22m[32mjoin_call-B65a9Ev2.mp3 [39m[1m[2m 30.54 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772666Z [2mdist-react/[22m[2massets/[22m[32mgg sans Regular-Bd8GJPVd.woff [39m[1m[2m 39.09 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772718Z [2mdist-react/[22m[2massets/[22m[32memojies_greyscale-CtRIvx0g.png [39m[1m[2m 39.11 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772770Z [2mdist-react/[22m[2massets/[22m[32mgg sans Bold-BGlwbW8t.woff [39m[1m[2m 40.13 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772823Z [2mdist-react/[22m[2massets/[22m[32mgg sans Medium-BMWm4JFW.woff [39m[1m[2m 40.32 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772875Z [2mdist-react/[22m[2massets/[22m[32mgg sans Semibold-xAGa8zYH.woff [39m[1m[2m 40.57 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772931Z [2mdist-react/[22m[2massets/[22m[32memojies_colored-Cxo2u_zo.png [39m[1m[2m 45.89 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5772985Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_viewer_join-BOPrADSV.mp3 [39m[1m[2m 67.54 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5773038Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_viewer_leave-BoDMhfvJ.mp3 [39m[1m[2m 67.54 kB[22m[1m[22m
|
||||
2026-02-13T18:56:54.5773093Z [2mdist-react/[22m[2massets/[22m[35mindex-DN2VsIhS.css [39m[1m[2m 72.50 kB[22m[1m[22m[2m │ gzip: 12.96 kB[22m
|
||||
2026-02-13T18:56:54.5773153Z [2mdist-react/[22m[2massets/[22m[36mindex-9riexBjR.js [39m[1m[33m8,425.21 kB[39m[22m[2m │ gzip: 1,537.92 kB[22m
|
||||
2026-02-13T18:56:54.5773212Z [33m
|
||||
2026-02-13T18:56:54.5773250Z (!) Some chunks are larger than 1000 kB after minification. Consider:
|
||||
2026-02-13T18:56:54.5773298Z - Using dynamic import() to code-split the application
|
||||
2026-02-13T18:56:54.5773340Z - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
||||
2026-02-13T18:56:54.5773394Z - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.[39m
|
||||
2026-02-13T18:56:54.5773447Z [32m✓ built in 2.99s[39m
|
||||
2026-02-13T18:56:55.0055993Z [34m•[39m electron-builder [34mversion[39m=25.1.8 [34mos[39m=6.12.54-Unraid
|
||||
2026-02-13T18:56:55.0057823Z [34m•[39m artifacts will be published if draft release exists [34mreason[39m=CI detected
|
||||
2026-02-13T18:56:55.0077289Z [34m•[39m loaded configuration [34mfile[39m=package.json ("build" field)
|
||||
2026-02-13T18:56:55.0853845Z [34m•[39m installing production dependencies [34mplatform[39m=linux [34march[39m=x64 [34mappDir[39m=/workspace/Moyettes/DiscordClone/apps/electron
|
||||
2026-02-13T18:56:55.6702292Z [31m⨯[39m [31mspawn /workspace/Moyettes/DiscordClone/node_modules/app-builder-bin/linux/x64/app-builder ENOENT[39m [31mfailedTask[39m=build [31mstackTrace[39m=Error: spawn /workspace/Moyettes/DiscordClone/node_modules/app-builder-bin/linux/x64/app-builder ENOENT
|
||||
2026-02-13T18:56:55.6702645Z at Process.ChildProcess._handle.onexit (node:internal/child_process:285:19)
|
||||
2026-02-13T18:56:55.6702714Z at onErrorNT (node:internal/child_process:483:16)
|
||||
2026-02-13T18:56:55.6702763Z at processTicksAndRejections (node:internal/process/task_queues:90:21)
|
||||
2026-02-13T18:56:55.6856601Z ❌ Failure - Main Build Electron app
|
||||
2026-02-13T18:56:55.6882903Z exitcode '1': failure
|
||||
2026-02-13T18:56:55.7208548Z expression 'npm-electron-${{ hashFiles('package-lock.json') }}' rewritten to 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T18:56:55.7208702Z evaluating expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T18:56:55.7208840Z Writing entry to tarball workflow/hashfiles/index.js len:168437
|
||||
2026-02-13T18:56:55.7211203Z Extracting content to '/var/run/act'
|
||||
2026-02-13T18:56:55.7221923Z 🐳 docker exec cmd=[node /var/run/act/workflow/hashfiles/index.js] user= workdir=
|
||||
2026-02-13T18:56:55.7222288Z Exec command '[node /var/run/act/workflow/hashfiles/index.js]'
|
||||
2026-02-13T18:56:55.7222419Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T18:56:55.7767128Z expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))' evaluated to '%!t(string=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852)'
|
||||
2026-02-13T18:56:55.7803356Z evaluating expression 'success()'
|
||||
2026-02-13T18:56:55.7803627Z expression 'success()' evaluated to 'false'
|
||||
2026-02-13T18:56:55.7803713Z Skipping step 'Cache npm and Electron' due to 'success()'
|
||||
2026-02-13T18:56:55.7895634Z evaluating expression 'always()'
|
||||
2026-02-13T18:56:55.7895891Z expression 'always()' evaluated to 'true'
|
||||
2026-02-13T18:56:55.7895990Z ⭐ Run Post Checkout repository
|
||||
2026-02-13T18:56:55.7896129Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T18:56:55.7896262Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T18:56:55.7896349Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T18:56:55.7896434Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T18:56:55.7896509Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T18:56:55.7896596Z Extracting content to '/var/run/act'
|
||||
2026-02-13T18:56:55.7906440Z run post step for 'Checkout repository'
|
||||
2026-02-13T18:56:55.7906756Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T18:56:55.7906848Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T18:56:55.7906914Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T18:56:55.7907081Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T18:56:55.8792208Z [command]/usr/bin/git version
|
||||
2026-02-13T18:56:55.8990232Z git version 2.34.1
|
||||
2026-02-13T18:56:55.9000518Z ***
|
||||
2026-02-13T18:56:55.9004484Z Copying '/root/.gitconfig' to '/tmp/51dee9ee-51c8-4042-bbdd-01a2df1f223b/.gitconfig'
|
||||
2026-02-13T18:56:55.9009834Z Temporarily overriding HOME='/tmp/51dee9ee-51c8-4042-bbdd-01a2df1f223b' before making global git config changes
|
||||
2026-02-13T18:56:55.9010645Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T18:56:55.9012991Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T18:56:55.9032089Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T18:56:55.9050125Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T18:56:55.9208756Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T18:56:55.9219838Z http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T18:56:55.9223833Z [command]/usr/bin/git config --local --unset-all http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T18:56:55.9239391Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T18:56:55.9392917Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T18:56:55.9407020Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T18:56:55.9590876Z ✅ Success - Post Checkout repository
|
||||
2026-02-13T18:56:55.9612358Z Cleaning up container for job build-and-release
|
||||
2026-02-13T18:57:55.9616842Z failed to remove container: Delete "http://%2Fvar%2Frun%2Fdocker.sock/v1.44/containers/c552a4a1f514ed2e1c9ef47d4faf8897b005de0741436f0a80ede1e382a52974?force=1&v=1": context deadline exceeded
|
||||
2026-02-13T18:57:55.9617116Z Removed container: c552a4a1f514ed2e1c9ef47d4faf8897b005de0741436f0a80ede1e382a52974
|
||||
2026-02-13T18:57:55.9617186Z Error while stop job container: context deadline exceeded
|
||||
2026-02-13T18:57:55.9617239Z 🏁 Job failed
|
||||
2026-02-13T18:57:55.9656964Z Error occurred running finally: Error occurred running finally: context deadline exceeded (original error: <nil>) (original error: <nil>)
|
||||
165
log/log2.txt
165
log/log2.txt
@@ -1,165 +0,0 @@
|
||||
2026-02-13T19:32:53.7334568Z e0b0e1a10f48(version:v0.2.13) received task 44 of job build-and-release, be triggered by event: push
|
||||
2026-02-13T19:32:53.7337270Z workflow prepared
|
||||
2026-02-13T19:32:53.7337920Z evaluating expression 'success()'
|
||||
2026-02-13T19:32:53.7338391Z expression 'success()' evaluated to 'true'
|
||||
2026-02-13T19:32:53.7338550Z 🚀 Start image=moyettes/eb
|
||||
2026-02-13T19:32:53.7380263Z 🐳 docker pull image=moyettes/eb platform= username= forcePull=false
|
||||
2026-02-13T19:32:53.7380377Z 🐳 docker pull moyettes/eb
|
||||
2026-02-13T19:32:53.7385010Z Image exists? true
|
||||
2026-02-13T19:32:53.7461341Z Cleaning up network for job build-and-release, and network name is: GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network
|
||||
2026-02-13T19:32:54.4179621Z 🐳 docker create image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T19:32:54.4261171Z Custom container.Config from options ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image: Volumes:map[] WorkingDir: Entrypoint:[] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T19:32:54.4261486Z Merged container.Config ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image:moyettes/eb Volumes:map[] WorkingDir:/workspace/Moyettes/DiscordClone Entrypoint:[/bin/sleep 10800] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T19:32:54.4261827Z Custom container.HostConfig from options ==> &{Binds:[] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:false VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc0001fa3c8 OomKillDisable:0xc0001fa2c3 PidsLimit:0xc0001fa428 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T19:32:54.4262179Z --network and --net in the options will be ignored.
|
||||
2026-02-13T19:32:54.4262424Z Merged container.HostConfig ==> &{Binds:[/var/run/docker.sock:/var/run/docker.sock] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:true VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc0001fa3c8 OomKillDisable:0xc0001fa2c3 PidsLimit:0xc0001fa428 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[{Type:volume Source:act-toolcache Target:/opt/hostedtoolcache ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release-env Target:/var/run/act ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release Target:/workspace/Moyettes/DiscordClone ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>}] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T19:32:55.8176538Z Created container name=GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release id=2debfc455c871161eed822c759604d5bde221adcab93035906c77fa9a3785090 from image moyettes/eb (platform: )
|
||||
2026-02-13T19:32:55.8176747Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-02-13T19:32:55.8176835Z 🐳 docker run image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-44_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T19:32:55.8176928Z Starting container: 2debfc455c871161eed822c759604d5bde221adcab93035906c77fa9a3785090
|
||||
2026-02-13T19:32:58.8503743Z Started container: 2debfc455c871161eed822c759604d5bde221adcab93035906c77fa9a3785090
|
||||
2026-02-13T19:32:58.9192530Z Writing entry to tarball workflow/event.json len:4862
|
||||
2026-02-13T19:32:58.9192933Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:32:58.9193078Z Extracting content to '/var/run/act/'
|
||||
2026-02-13T19:32:58.9271597Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
|
||||
2026-02-13T19:32:58.9271776Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:32:59.2987119Z Unable to pull refs/heads/v4: non-fast-forward update
|
||||
2026-02-13T19:32:59.2987344Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:32:59.3124333Z Checked out v4
|
||||
2026-02-13T19:32:59.3173710Z ☁ git clone 'https://github.com/actions/cache' # ref=v4
|
||||
2026-02-13T19:32:59.3173863Z cloning https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T19:32:59.5255777Z Unable to pull refs/heads/v4: worktree contains unstaged changes
|
||||
2026-02-13T19:32:59.5255989Z Cloned https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T19:32:59.5732184Z Checked out v4
|
||||
2026-02-13T19:32:59.5839013Z evaluating expression ''
|
||||
2026-02-13T19:32:59.5839259Z expression '' evaluated to 'true'
|
||||
2026-02-13T19:32:59.5839318Z ⭐ Run Main Checkout repository
|
||||
2026-02-13T19:32:59.5839403Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T19:32:59.5839488Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T19:32:59.5839536Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T19:32:59.5839583Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:32:59.5839629Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T19:32:59.5839679Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:32:59.5878878Z expression '${{ github.repository }}' rewritten to 'format('{0}', github.repository)'
|
||||
2026-02-13T19:32:59.5879022Z evaluating expression 'format('{0}', github.repository)'
|
||||
2026-02-13T19:32:59.5879251Z expression 'format('{0}', github.repository)' evaluated to '%!t(string=Moyettes/DiscordClone)'
|
||||
2026-02-13T19:32:59.5879412Z expression '${{ github.token }}' rewritten to 'format('{0}', github.token)'
|
||||
2026-02-13T19:32:59.5879488Z evaluating expression 'format('{0}', github.token)'
|
||||
2026-02-13T19:32:59.5879626Z expression 'format('{0}', github.token)' evaluated to '%!t(string=***)'
|
||||
2026-02-13T19:32:59.5880002Z type=remote-action actionDir=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab actionPath= workdir=/workspace/Moyettes/DiscordClone actionCacheDir=/root/.cache/act actionName=c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab containerActionDir=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:32:59.5880134Z /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:32:59.5880271Z Removing /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/.gitignore before docker cp
|
||||
2026-02-13T19:32:59.5880764Z 🐳 docker cp src=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ dst=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:32:59.5881407Z Writing tarball /tmp/act1688867230 from /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:32:59.5881517Z Stripping prefix:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ src:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:32:59.6215690Z Extracting content from '/tmp/act1688867230' to '/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/'
|
||||
2026-02-13T19:32:59.6652539Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T19:32:59.6652906Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T19:32:59.6652986Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T19:32:59.6653186Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:32:59.7589420Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T19:32:59.7589499Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T19:32:59.7591129Z Syncing repository: Moyettes/DiscordClone
|
||||
2026-02-13T19:32:59.7592301Z ::group::Getting Git version info
|
||||
2026-02-13T19:32:59.7592477Z Working directory is '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:32:59.7604667Z [command]/usr/bin/git version
|
||||
2026-02-13T19:32:59.7632808Z git version 2.34.1
|
||||
2026-02-13T19:32:59.7642833Z ::endgroup::
|
||||
2026-02-13T19:32:59.7647656Z Copying '/root/.gitconfig' to '/tmp/138033a9-6df9-46f3-a309-e608610a1345/.gitconfig'
|
||||
2026-02-13T19:32:59.7651244Z Temporarily overriding HOME='/tmp/138033a9-6df9-46f3-a309-e608610a1345' before making global git config changes
|
||||
2026-02-13T19:32:59.7651330Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T19:32:59.7653938Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:32:59.7673993Z Deleting the contents of '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:32:59.7680064Z ::group::Initializing the repository
|
||||
2026-02-13T19:32:59.7681486Z [command]/usr/bin/git init /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:32:59.7702970Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-02-13T19:32:59.7703162Z hint: is subject to change. To configure the initial branch name to use in all
|
||||
2026-02-13T19:32:59.7703252Z hint: of your new repositories, which will suppress this warning, call:
|
||||
2026-02-13T19:32:59.7703336Z hint:
|
||||
2026-02-13T19:32:59.7703421Z hint: git config --global init.defaultBranch <name>
|
||||
2026-02-13T19:32:59.7703499Z hint:
|
||||
2026-02-13T19:32:59.7703562Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-02-13T19:32:59.7703635Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-02-13T19:32:59.7703711Z hint:
|
||||
2026-02-13T19:32:59.7703776Z hint: git branch -m <name>
|
||||
2026-02-13T19:32:59.7706673Z Initialized empty Git repository in /workspace/Moyettes/DiscordClone/.git/
|
||||
2026-02-13T19:32:59.7714124Z [command]/usr/bin/git remote add origin http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T19:32:59.7732694Z ::endgroup::
|
||||
2026-02-13T19:32:59.7732878Z ::group::Disabling automatic garbage collection
|
||||
2026-02-13T19:32:59.7734538Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-02-13T19:32:59.7748794Z ::endgroup::
|
||||
2026-02-13T19:32:59.7748891Z ::group::Setting up auth
|
||||
2026-02-13T19:32:59.7753015Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T19:32:59.7768050Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T19:32:59.7931408Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T19:32:59.7945319Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T19:32:59.8093184Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T19:32:59.8106922Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T19:32:59.8252768Z [command]/usr/bin/git config --local http.http://192.168.125.15:4000/.extraheader AUTHORIZATION: basic ***
|
||||
2026-02-13T19:32:59.8269559Z ::endgroup::
|
||||
2026-02-13T19:32:59.8269664Z ::group::Fetching the repository
|
||||
2026-02-13T19:32:59.8272971Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +e61df284e909fb14b5b88c1309704f76820910e5:refs/remotes/origin/main
|
||||
2026-02-13T19:33:02.2026273Z From http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T19:33:02.2026536Z * [new ref] e61df284e909fb14b5b88c1309704f76820910e5 -> origin/main
|
||||
2026-02-13T19:33:02.2039430Z ::endgroup::
|
||||
2026-02-13T19:33:02.2039550Z ::group::Determining the checkout info
|
||||
2026-02-13T19:33:02.2040586Z ::endgroup::
|
||||
2026-02-13T19:33:02.2043493Z [command]/usr/bin/git sparse-checkout disable
|
||||
2026-02-13T19:33:02.2065932Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
|
||||
2026-02-13T19:33:02.2082220Z ::group::Checking out the ref
|
||||
2026-02-13T19:33:02.2084395Z [command]/usr/bin/git checkout --progress --force -B main refs/remotes/origin/main
|
||||
2026-02-13T19:33:02.2947591Z Switched to a new branch 'main'
|
||||
2026-02-13T19:33:02.2948234Z Branch 'main' set up to track remote branch 'main' from 'origin'.
|
||||
2026-02-13T19:33:02.2952747Z ::endgroup::
|
||||
2026-02-13T19:33:02.2979370Z [command]/usr/bin/git log -1 --format=%H
|
||||
2026-02-13T19:33:02.2999322Z e61df284e909fb14b5b88c1309704f76820910e5
|
||||
2026-02-13T19:33:02.3007012Z ::remove-matcher owner=checkout-git::
|
||||
2026-02-13T19:33:03.3278336Z (node:217) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-02-13T19:33:03.3278474Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-02-13T19:33:23.3475905Z ::warning::Failed to restore: getCacheEntry failed: Request timeout: /_apis/artifactcache/cache?keys=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852%252Cnpm-electron-&version=f2531268ab9c19c75ce7b3eb23cc11c7f69fd3cf796834d4881591e430a373ff
|
||||
2026-02-13T19:33:23.3477842Z Cache not found for input keys: npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852, npm-electron-
|
||||
2026-02-13T19:33:23.4124247Z /var/run/act/workflow/3.sh: 2: set: Illegal option -o pipefail
|
||||
2026-02-13T19:33:23.4127538Z ❌ Failure - Main Install dependencies
|
||||
2026-02-13T19:33:23.4147342Z exitcode '2': failure
|
||||
2026-02-13T19:33:23.4638842Z expression 'npm-electron-${{ hashFiles('package-lock.json') }}' rewritten to 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T19:33:23.4639046Z evaluating expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T19:33:23.4639254Z Writing entry to tarball workflow/hashfiles/index.js len:168437
|
||||
2026-02-13T19:33:23.4640133Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:33:23.4649485Z 🐳 docker exec cmd=[node /var/run/act/workflow/hashfiles/index.js] user= workdir=
|
||||
2026-02-13T19:33:23.4649638Z Exec command '[node /var/run/act/workflow/hashfiles/index.js]'
|
||||
2026-02-13T19:33:23.4649734Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:33:23.5271132Z expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))' evaluated to '%!t(string=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852)'
|
||||
2026-02-13T19:33:23.5310392Z evaluating expression 'success()'
|
||||
2026-02-13T19:33:23.5310589Z expression 'success()' evaluated to 'false'
|
||||
2026-02-13T19:33:23.5310671Z Skipping step 'Cache npm and Electron' due to 'success()'
|
||||
2026-02-13T19:33:23.5404097Z evaluating expression 'always()'
|
||||
2026-02-13T19:33:23.5404401Z expression 'always()' evaluated to 'true'
|
||||
2026-02-13T19:33:23.5404499Z ⭐ Run Post Checkout repository
|
||||
2026-02-13T19:33:23.5404646Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T19:33:23.5404779Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T19:33:23.5404873Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T19:33:23.5404977Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:33:23.5405054Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T19:33:23.5405173Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:33:23.5414329Z run post step for 'Checkout repository'
|
||||
2026-02-13T19:33:23.5414678Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T19:33:23.5414778Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T19:33:23.5414840Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T19:33:23.5415001Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:33:23.6310631Z [command]/usr/bin/git version
|
||||
2026-02-13T19:33:23.6331724Z git version 2.34.1
|
||||
2026-02-13T19:33:23.6350074Z ***
|
||||
2026-02-13T19:33:23.6356242Z Copying '/root/.gitconfig' to '/tmp/8d698909-39ad-4750-b3be-314148a4aa23/.gitconfig'
|
||||
2026-02-13T19:33:23.6363154Z Temporarily overriding HOME='/tmp/8d698909-39ad-4750-b3be-314148a4aa23' before making global git config changes
|
||||
2026-02-13T19:33:23.6363740Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T19:33:23.6367434Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:33:23.6390138Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T19:33:23.6407326Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T19:33:23.6572241Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T19:33:23.6584724Z http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T19:33:23.6590389Z [command]/usr/bin/git config --local --unset-all http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T19:33:23.6606735Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T19:33:23.6754982Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T19:33:23.6768951Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T19:33:23.6959873Z ✅ Success - Post Checkout repository
|
||||
2026-02-13T19:33:23.6976704Z Cleaning up container for job build-and-release
|
||||
198
log/log3.txt
198
log/log3.txt
@@ -1,198 +0,0 @@
|
||||
2026-02-13T19:36:21.9777754Z e0b0e1a10f48(version:v0.2.13) received task 45 of job build-and-release, be triggered by event: push
|
||||
2026-02-13T19:36:21.9779832Z workflow prepared
|
||||
2026-02-13T19:36:21.9780279Z evaluating expression 'success()'
|
||||
2026-02-13T19:36:21.9780699Z expression 'success()' evaluated to 'true'
|
||||
2026-02-13T19:36:21.9780795Z 🚀 Start image=moyettes/eb
|
||||
2026-02-13T19:36:21.9819365Z 🐳 docker pull image=moyettes/eb platform= username= forcePull=false
|
||||
2026-02-13T19:36:21.9819476Z 🐳 docker pull moyettes/eb
|
||||
2026-02-13T19:36:21.9823902Z Image exists? true
|
||||
2026-02-13T19:36:21.9901448Z Cleaning up network for job build-and-release, and network name is: GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network
|
||||
2026-02-13T19:36:22.6931880Z 🐳 docker create image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T19:36:22.7012819Z Custom container.Config from options ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image: Volumes:map[] WorkingDir: Entrypoint:[] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T19:36:22.7013111Z Merged container.Config ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image:moyettes/eb Volumes:map[] WorkingDir:/workspace/Moyettes/DiscordClone Entrypoint:[/bin/sleep 10800] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T19:36:22.7013481Z Custom container.HostConfig from options ==> &{Binds:[] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:false VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc0001fa3c8 OomKillDisable:0xc0001fa2c3 PidsLimit:0xc0001fa428 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T19:36:22.7013825Z --network and --net in the options will be ignored.
|
||||
2026-02-13T19:36:22.7014035Z Merged container.HostConfig ==> &{Binds:[/var/run/docker.sock:/var/run/docker.sock] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:true VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc0001fa3c8 OomKillDisable:0xc0001fa2c3 PidsLimit:0xc0001fa428 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[{Type:volume Source:act-toolcache Target:/opt/hostedtoolcache ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release-env Target:/var/run/act ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release Target:/workspace/Moyettes/DiscordClone ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>}] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T19:36:23.8673081Z Created container name=GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release id=d548f196e47e927667dc2c54fadbced669d7e0cedf25618a1d405c92768e9fb1 from image moyettes/eb (platform: )
|
||||
2026-02-13T19:36:23.8673324Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-02-13T19:36:23.8673409Z 🐳 docker run image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-45_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T19:36:23.8673519Z Starting container: d548f196e47e927667dc2c54fadbced669d7e0cedf25618a1d405c92768e9fb1
|
||||
2026-02-13T19:36:26.7000331Z Started container: d548f196e47e927667dc2c54fadbced669d7e0cedf25618a1d405c92768e9fb1
|
||||
2026-02-13T19:36:26.7853339Z Writing entry to tarball workflow/event.json len:4892
|
||||
2026-02-13T19:36:26.7853724Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:36:26.7853844Z Extracting content to '/var/run/act/'
|
||||
2026-02-13T19:36:26.7928425Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
|
||||
2026-02-13T19:36:26.7928632Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:36:27.3331108Z Unable to pull refs/heads/v4: non-fast-forward update
|
||||
2026-02-13T19:36:27.3331493Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:36:27.3466578Z Checked out v4
|
||||
2026-02-13T19:36:27.3511622Z ☁ git clone 'https://github.com/actions/cache' # ref=v4
|
||||
2026-02-13T19:36:27.3511799Z cloning https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T19:36:27.6387088Z Unable to pull refs/heads/v4: worktree contains unstaged changes
|
||||
2026-02-13T19:36:27.6387328Z Cloned https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T19:36:27.6872705Z Checked out v4
|
||||
2026-02-13T19:36:27.6982618Z evaluating expression ''
|
||||
2026-02-13T19:36:27.6982901Z expression '' evaluated to 'true'
|
||||
2026-02-13T19:36:27.6982997Z ⭐ Run Main Checkout repository
|
||||
2026-02-13T19:36:27.6983135Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T19:36:27.6983269Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T19:36:27.6983352Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T19:36:27.6983428Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:36:27.6983499Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T19:36:27.6983583Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:36:27.7024234Z expression '${{ github.repository }}' rewritten to 'format('{0}', github.repository)'
|
||||
2026-02-13T19:36:27.7024368Z evaluating expression 'format('{0}', github.repository)'
|
||||
2026-02-13T19:36:27.7024592Z expression 'format('{0}', github.repository)' evaluated to '%!t(string=Moyettes/DiscordClone)'
|
||||
2026-02-13T19:36:27.7024727Z expression '${{ github.token }}' rewritten to 'format('{0}', github.token)'
|
||||
2026-02-13T19:36:27.7024799Z evaluating expression 'format('{0}', github.token)'
|
||||
2026-02-13T19:36:27.7024937Z expression 'format('{0}', github.token)' evaluated to '%!t(string=***)'
|
||||
2026-02-13T19:36:27.7025098Z type=remote-action actionDir=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab actionPath= workdir=/workspace/Moyettes/DiscordClone actionCacheDir=/root/.cache/act actionName=c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab containerActionDir=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:36:27.7025227Z /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:36:27.7025355Z Removing /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/.gitignore before docker cp
|
||||
2026-02-13T19:36:27.7025861Z 🐳 docker cp src=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ dst=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:36:27.7026458Z Writing tarball /tmp/act1301575211 from /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:36:27.7026563Z Stripping prefix:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ src:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:36:27.7335315Z Extracting content from '/tmp/act1301575211' to '/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/'
|
||||
2026-02-13T19:36:27.7729624Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T19:36:27.7730025Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T19:36:27.7730107Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T19:36:27.7730345Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:36:27.8726078Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T19:36:27.8726199Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T19:36:27.8727713Z Syncing repository: Moyettes/DiscordClone
|
||||
2026-02-13T19:36:27.8728862Z ::group::Getting Git version info
|
||||
2026-02-13T19:36:27.8729003Z Working directory is '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:36:27.8741986Z [command]/usr/bin/git version
|
||||
2026-02-13T19:36:27.8770737Z git version 2.34.1
|
||||
2026-02-13T19:36:27.8779950Z ::endgroup::
|
||||
2026-02-13T19:36:27.8784183Z Copying '/root/.gitconfig' to '/tmp/dec05d8d-0df0-4365-838a-7fca1ce0a633/.gitconfig'
|
||||
2026-02-13T19:36:27.8787854Z Temporarily overriding HOME='/tmp/dec05d8d-0df0-4365-838a-7fca1ce0a633' before making global git config changes
|
||||
2026-02-13T19:36:27.8787943Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T19:36:27.8790187Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:36:27.8806718Z Deleting the contents of '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:36:27.8808457Z ::group::Initializing the repository
|
||||
2026-02-13T19:36:27.8809872Z [command]/usr/bin/git init /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:36:27.8830428Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-02-13T19:36:27.8830601Z hint: is subject to change. To configure the initial branch name to use in all
|
||||
2026-02-13T19:36:27.8830712Z hint: of your new repositories, which will suppress this warning, call:
|
||||
2026-02-13T19:36:27.8830801Z hint:
|
||||
2026-02-13T19:36:27.8830872Z hint: git config --global init.defaultBranch <name>
|
||||
2026-02-13T19:36:27.8830951Z hint:
|
||||
2026-02-13T19:36:27.8831017Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-02-13T19:36:27.8831103Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-02-13T19:36:27.8831175Z hint:
|
||||
2026-02-13T19:36:27.8831236Z hint: git branch -m <name>
|
||||
2026-02-13T19:36:27.8834743Z Initialized empty Git repository in /workspace/Moyettes/DiscordClone/.git/
|
||||
2026-02-13T19:36:27.8841310Z [command]/usr/bin/git remote add origin http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T19:36:27.8860768Z ::endgroup::
|
||||
2026-02-13T19:36:27.8860944Z ::group::Disabling automatic garbage collection
|
||||
2026-02-13T19:36:27.8862545Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-02-13T19:36:27.8880484Z ::endgroup::
|
||||
2026-02-13T19:36:27.8880684Z ::group::Setting up auth
|
||||
2026-02-13T19:36:27.8883759Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T19:36:27.8902433Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T19:36:27.9066940Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T19:36:27.9083692Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T19:36:27.9237344Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T19:36:27.9252826Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T19:36:27.9395597Z [command]/usr/bin/git config --local http.http://192.168.125.15:4000/.extraheader AUTHORIZATION: basic ***
|
||||
2026-02-13T19:36:27.9412863Z ::endgroup::
|
||||
2026-02-13T19:36:27.9412967Z ::group::Fetching the repository
|
||||
2026-02-13T19:36:27.9416326Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +6177ee9a6f3b23aa93a6e77c2705bf6761adef1e:refs/remotes/origin/main
|
||||
2026-02-13T19:36:30.2694276Z From http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T19:36:30.2694572Z * [new ref] 6177ee9a6f3b23aa93a6e77c2705bf6761adef1e -> origin/main
|
||||
2026-02-13T19:36:30.2708466Z ::endgroup::
|
||||
2026-02-13T19:36:30.2708567Z ::group::Determining the checkout info
|
||||
2026-02-13T19:36:30.2709664Z ::endgroup::
|
||||
2026-02-13T19:36:30.2717085Z [command]/usr/bin/git sparse-checkout disable
|
||||
2026-02-13T19:36:30.2740804Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
|
||||
2026-02-13T19:36:30.2756475Z ::group::Checking out the ref
|
||||
2026-02-13T19:36:30.2758337Z [command]/usr/bin/git checkout --progress --force -B main refs/remotes/origin/main
|
||||
2026-02-13T19:36:30.3636484Z Switched to a new branch 'main'
|
||||
2026-02-13T19:36:30.3636833Z Branch 'main' set up to track remote branch 'main' from 'origin'.
|
||||
2026-02-13T19:36:30.3642228Z ::endgroup::
|
||||
2026-02-13T19:36:30.3661996Z [command]/usr/bin/git log -1 --format=%H
|
||||
2026-02-13T19:36:30.3679600Z 6177ee9a6f3b23aa93a6e77c2705bf6761adef1e
|
||||
2026-02-13T19:36:30.3685003Z ::remove-matcher owner=checkout-git::
|
||||
2026-02-13T19:36:31.3571318Z (node:219) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-02-13T19:36:31.3571459Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-02-13T19:36:51.3783300Z ::warning::Failed to restore: getCacheEntry failed: Request timeout: /_apis/artifactcache/cache?keys=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852%252Cnpm-electron-&version=f2531268ab9c19c75ce7b3eb23cc11c7f69fd3cf796834d4881591e430a373ff
|
||||
2026-02-13T19:36:51.3783886Z Cache not found for input keys: npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852, npm-electron-
|
||||
2026-02-13T19:36:52.1516780Z npm warn deprecated tar@6.2.1: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:36:52.3747573Z npm warn deprecated npmlog@6.0.2: This package is no longer supported.
|
||||
2026-02-13T19:36:52.6982265Z npm warn deprecated lodash.isequal@4.5.0: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
|
||||
2026-02-13T19:36:52.7192386Z npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
2026-02-13T19:36:52.9058437Z npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:36:52.9256680Z npm warn deprecated gauge@4.0.4: This package is no longer supported.
|
||||
2026-02-13T19:37:51.9892421Z npm warn deprecated are-we-there-yet@3.0.1: This package is no longer supported.
|
||||
2026-02-13T19:37:51.9917819Z npm warn deprecated @npmcli/move-file@2.0.1: This functionality has been moved to @npmcli/fs
|
||||
2026-02-13T19:37:52.0063818Z npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:37:52.0202588Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T19:37:52.0213733Z npm warn deprecated glob@8.1.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:37:52.0382534Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T19:37:52.0383042Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T19:37:52.0662259Z npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
||||
2026-02-13T19:37:52.3065967Z npm warn deprecated glob@9.3.5: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:37:52.3200651Z npm warn deprecated glob@10.5.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:38:01.1610842Z
|
||||
2026-02-13T19:38:01.1611132Z added 677 packages, and audited 683 packages in 1m
|
||||
2026-02-13T19:38:01.1611198Z
|
||||
2026-02-13T19:38:01.1611239Z 183 packages are looking for funding
|
||||
2026-02-13T19:38:01.1611307Z run `npm fund` for details
|
||||
2026-02-13T19:38:01.1802390Z
|
||||
2026-02-13T19:38:01.1802544Z 11 vulnerabilities (1 moderate, 10 high)
|
||||
2026-02-13T19:38:01.1802633Z
|
||||
2026-02-13T19:38:01.1802695Z To address issues that do not require attention, run:
|
||||
2026-02-13T19:38:01.1802758Z npm audit fix
|
||||
2026-02-13T19:38:01.1802806Z
|
||||
2026-02-13T19:38:01.1802850Z To address all issues (including breaking changes), run:
|
||||
2026-02-13T19:38:01.1802907Z npm audit fix --force
|
||||
2026-02-13T19:38:01.1802959Z
|
||||
2026-02-13T19:38:01.1803057Z Run `npm audit` for details.
|
||||
2026-02-13T19:38:01.1988663Z --- app-builder diagnostics ---
|
||||
2026-02-13T19:38:01.2208484Z -rwxr-xr-x 1 root root 18116608 Feb 13 19:37 node_modules/app-builder-bin/linux/x64/app-builder
|
||||
2026-02-13T19:38:01.2209297Z /var/run/act/workflow/3.sh: 7: file: not found
|
||||
2026-02-13T19:38:01.2215582Z ❌ Failure - Main Install dependencies
|
||||
2026-02-13T19:38:01.2234947Z exitcode '127': command not found, please refer to https://github.com/nektos/act/issues/107 for more information
|
||||
2026-02-13T19:38:01.2731112Z expression 'npm-electron-${{ hashFiles('package-lock.json') }}' rewritten to 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T19:38:01.2731336Z evaluating expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T19:38:01.2731539Z Writing entry to tarball workflow/hashfiles/index.js len:168437
|
||||
2026-02-13T19:38:01.2733153Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:38:01.2744106Z 🐳 docker exec cmd=[node /var/run/act/workflow/hashfiles/index.js] user= workdir=
|
||||
2026-02-13T19:38:01.2744234Z Exec command '[node /var/run/act/workflow/hashfiles/index.js]'
|
||||
2026-02-13T19:38:01.2744324Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:38:01.3474202Z expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))' evaluated to '%!t(string=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852)'
|
||||
2026-02-13T19:38:01.3509253Z evaluating expression 'success()'
|
||||
2026-02-13T19:38:01.3509499Z expression 'success()' evaluated to 'false'
|
||||
2026-02-13T19:38:01.3509585Z Skipping step 'Cache npm and Electron' due to 'success()'
|
||||
2026-02-13T19:38:01.3599133Z evaluating expression 'always()'
|
||||
2026-02-13T19:38:01.3599308Z expression 'always()' evaluated to 'true'
|
||||
2026-02-13T19:38:01.3599374Z ⭐ Run Post Checkout repository
|
||||
2026-02-13T19:38:01.3599464Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T19:38:01.3599559Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T19:38:01.3599605Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T19:38:01.3599656Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:38:01.3599696Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T19:38:01.3599748Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:38:01.3606678Z run post step for 'Checkout repository'
|
||||
2026-02-13T19:38:01.3607073Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T19:38:01.3607202Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T19:38:01.3607317Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T19:38:01.3607538Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:38:01.4605898Z [command]/usr/bin/git version
|
||||
2026-02-13T19:38:01.4797399Z git version 2.34.1
|
||||
2026-02-13T19:38:01.4807817Z ***
|
||||
2026-02-13T19:38:01.4812586Z Copying '/root/.gitconfig' to '/tmp/fea237a6-cf9e-4f7c-a2fa-eeee022d1604/.gitconfig'
|
||||
2026-02-13T19:38:01.4817182Z Temporarily overriding HOME='/tmp/fea237a6-cf9e-4f7c-a2fa-eeee022d1604' before making global git config changes
|
||||
2026-02-13T19:38:01.4817962Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T19:38:01.4820703Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:38:01.4839836Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T19:38:01.4862009Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T19:38:01.5029359Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T19:38:01.5041405Z http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T19:38:01.5048350Z [command]/usr/bin/git config --local --unset-all http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T19:38:01.5067223Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T19:38:01.5281659Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T19:38:01.5304472Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T19:38:01.5503190Z ✅ Success - Post Checkout repository
|
||||
2026-02-13T19:38:01.5523214Z Cleaning up container for job build-and-release
|
||||
610
log/log4.txt
610
log/log4.txt
@@ -1,610 +0,0 @@
|
||||
2026-02-13T19:43:21.8590060Z e0b0e1a10f48(version:v0.2.13) received task 46 of job build-and-release, be triggered by event: push
|
||||
2026-02-13T19:43:21.8591995Z workflow prepared
|
||||
2026-02-13T19:43:21.8592469Z evaluating expression 'success()'
|
||||
2026-02-13T19:43:21.8592844Z expression 'success()' evaluated to 'true'
|
||||
2026-02-13T19:43:21.8592937Z 🚀 Start image=moyettes/eb
|
||||
2026-02-13T19:43:21.8634719Z 🐳 docker pull image=moyettes/eb platform= username= forcePull=false
|
||||
2026-02-13T19:43:21.8634846Z 🐳 docker pull moyettes/eb
|
||||
2026-02-13T19:43:21.8639116Z Image exists? true
|
||||
2026-02-13T19:43:21.8703078Z Cleaning up network for job build-and-release, and network name is: GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network
|
||||
2026-02-13T19:43:22.4682212Z 🐳 docker create image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T19:43:22.4769484Z Custom container.Config from options ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image: Volumes:map[] WorkingDir: Entrypoint:[] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T19:43:22.4769714Z Merged container.Config ==> &{Hostname: Domainname: User: AttachStdin:false AttachStdout:true AttachStderr:true ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8] Cmd:[] Healthcheck:<nil> ArgsEscaped:false Image:moyettes/eb Volumes:map[] WorkingDir:/workspace/Moyettes/DiscordClone Entrypoint:[/bin/sleep 10800] NetworkDisabled:false MacAddress: OnBuild:[] Labels:map[] StopSignal: StopTimeout:<nil> Shell:[]}
|
||||
2026-02-13T19:43:22.4769947Z Custom container.HostConfig from options ==> &{Binds:[] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:false VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc0001fa3c8 OomKillDisable:0xc0001fa2c3 PidsLimit:0xc0001fa428 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T19:43:22.4770164Z --network and --net in the options will be ignored.
|
||||
2026-02-13T19:43:22.4770313Z Merged container.HostConfig ==> &{Binds:[/var/run/docker.sock:/var/run/docker.sock] ContainerIDFile: LogConfig:{Type: Config:map[]} NetworkMode:GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network PortBindings:map[] RestartPolicy:{Name:no MaximumRetryCount:0} AutoRemove:true VolumeDriver: VolumesFrom:[] ConsoleSize:[0 0] Annotations:map[] CapAdd:[] CapDrop:[] CgroupnsMode: DNS:[] DNSOptions:[] DNSSearch:[] ExtraHosts:[] GroupAdd:[] IpcMode: Cgroup: Links:[] OomScoreAdj:0 PidMode: Privileged:false PublishAllPorts:false ReadonlyRootfs:false SecurityOpt:[] StorageOpt:map[] Tmpfs:map[] UTSMode: UsernsMode: ShmSize:0 Sysctls:map[] Runtime: Isolation: Resources:{CPUShares:0 Memory:0 NanoCPUs:0 CgroupParent: BlkioWeight:0 BlkioWeightDevice:[] BlkioDeviceReadBps:[] BlkioDeviceWriteBps:[] BlkioDeviceReadIOps:[] BlkioDeviceWriteIOps:[] CPUPeriod:0 CPUQuota:0 CPURealtimePeriod:0 CPURealtimeRuntime:0 CpusetCpus: CpusetMems: Devices:[] DeviceCgroupRules:[] DeviceRequests:[] KernelMemory:0 KernelMemoryTCP:0 MemoryReservation:0 MemorySwap:0 MemorySwappiness:0xc0001fa3c8 OomKillDisable:0xc0001fa2c3 PidsLimit:0xc0001fa428 Ulimits:[] CPUCount:0 CPUPercent:0 IOMaximumIOps:0 IOMaximumBandwidth:0} Mounts:[{Type:volume Source:act-toolcache Target:/opt/hostedtoolcache ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-env Target:/var/run/act ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>} {Type:volume Source:GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release Target:/workspace/Moyettes/DiscordClone ReadOnly:false Consistency: BindOptions:<nil> VolumeOptions:<nil> TmpfsOptions:<nil> ClusterOptions:<nil>}] MaskedPaths:[] ReadonlyPaths:[] Init:<nil>}
|
||||
2026-02-13T19:43:23.6763541Z Created container name=GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release id=78088ba67b9e9a8de8380d20c89ea44da62bb5887fd0db970d18137ccf97f44f from image moyettes/eb (platform: )
|
||||
2026-02-13T19:43:23.6763911Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-02-13T19:43:23.6764002Z 🐳 docker run image=moyettes/eb platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network"
|
||||
2026-02-13T19:43:23.6764088Z Starting container: 78088ba67b9e9a8de8380d20c89ea44da62bb5887fd0db970d18137ccf97f44f
|
||||
2026-02-13T19:43:26.4422897Z Started container: 78088ba67b9e9a8de8380d20c89ea44da62bb5887fd0db970d18137ccf97f44f
|
||||
2026-02-13T19:43:26.5303431Z Writing entry to tarball workflow/event.json len:5022
|
||||
2026-02-13T19:43:26.5303827Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:43:26.5303940Z Extracting content to '/var/run/act/'
|
||||
2026-02-13T19:43:26.5380347Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
|
||||
2026-02-13T19:43:26.5380532Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:43:26.9946388Z Unable to pull refs/heads/v4: non-fast-forward update
|
||||
2026-02-13T19:43:26.9946600Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:43:27.0057995Z Checked out v4
|
||||
2026-02-13T19:43:27.0093828Z ☁ git clone 'https://github.com/actions/cache' # ref=v4
|
||||
2026-02-13T19:43:27.0093941Z cloning https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T19:43:27.2377963Z Unable to pull refs/heads/v4: worktree contains unstaged changes
|
||||
2026-02-13T19:43:27.2378194Z Cloned https://github.com/actions/cache to /root/.cache/act/6b4e4eb40e21c1bd02cb00a273f4d79af7c42205c1390e4e65c594ecd7a3696e
|
||||
2026-02-13T19:43:27.2850405Z Checked out v4
|
||||
2026-02-13T19:43:27.2948868Z evaluating expression ''
|
||||
2026-02-13T19:43:27.2949102Z expression '' evaluated to 'true'
|
||||
2026-02-13T19:43:27.2949163Z ⭐ Run Main Checkout repository
|
||||
2026-02-13T19:43:27.2949252Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T19:43:27.2949341Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T19:43:27.2949393Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T19:43:27.2949447Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:43:27.2949489Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T19:43:27.2949538Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:43:27.2984878Z expression '${{ github.repository }}' rewritten to 'format('{0}', github.repository)'
|
||||
2026-02-13T19:43:27.2985016Z evaluating expression 'format('{0}', github.repository)'
|
||||
2026-02-13T19:43:27.2985256Z expression 'format('{0}', github.repository)' evaluated to '%!t(string=Moyettes/DiscordClone)'
|
||||
2026-02-13T19:43:27.2985446Z expression '${{ github.token }}' rewritten to 'format('{0}', github.token)'
|
||||
2026-02-13T19:43:27.2985520Z evaluating expression 'format('{0}', github.token)'
|
||||
2026-02-13T19:43:27.2985666Z expression 'format('{0}', github.token)' evaluated to '%!t(string=***)'
|
||||
2026-02-13T19:43:27.2985780Z type=remote-action actionDir=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab actionPath= workdir=/workspace/Moyettes/DiscordClone actionCacheDir=/root/.cache/act actionName=c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab containerActionDir=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:43:27.2985902Z /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-02-13T19:43:27.2986028Z Removing /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/.gitignore before docker cp
|
||||
2026-02-13T19:43:27.2986530Z 🐳 docker cp src=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ dst=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:43:27.2987122Z Writing tarball /tmp/act1670143716 from /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:43:27.2987227Z Stripping prefix:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ src:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
|
||||
2026-02-13T19:43:27.3302843Z Extracting content from '/tmp/act1670143716' to '/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/'
|
||||
2026-02-13T19:43:27.3688387Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T19:43:27.3688745Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T19:43:27.3688828Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T19:43:27.3689035Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:43:27.5273206Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T19:43:27.5273290Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-02-13T19:43:27.5274863Z Syncing repository: Moyettes/DiscordClone
|
||||
2026-02-13T19:43:27.5275939Z ::group::Getting Git version info
|
||||
2026-02-13T19:43:27.5276044Z Working directory is '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:43:27.5288512Z [command]/usr/bin/git version
|
||||
2026-02-13T19:43:27.5317462Z git version 2.34.1
|
||||
2026-02-13T19:43:27.5326809Z ::endgroup::
|
||||
2026-02-13T19:43:27.5331374Z Copying '/root/.gitconfig' to '/tmp/9ec818fd-d5c9-4218-937f-276257579f19/.gitconfig'
|
||||
2026-02-13T19:43:27.5335515Z Temporarily overriding HOME='/tmp/9ec818fd-d5c9-4218-937f-276257579f19' before making global git config changes
|
||||
2026-02-13T19:43:27.5335653Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T19:43:27.5338264Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:43:27.5355876Z Deleting the contents of '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:43:27.5361938Z ::group::Initializing the repository
|
||||
2026-02-13T19:43:27.5363825Z [command]/usr/bin/git init /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:43:27.5379848Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-02-13T19:43:27.5379906Z hint: is subject to change. To configure the initial branch name to use in all
|
||||
2026-02-13T19:43:27.5379951Z hint: of your new repositories, which will suppress this warning, call:
|
||||
2026-02-13T19:43:27.5380005Z hint:
|
||||
2026-02-13T19:43:27.5380048Z hint: git config --global init.defaultBranch <name>
|
||||
2026-02-13T19:43:27.5380091Z hint:
|
||||
2026-02-13T19:43:27.5380128Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-02-13T19:43:27.5380171Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-02-13T19:43:27.5380214Z hint:
|
||||
2026-02-13T19:43:27.5380249Z hint: git branch -m <name>
|
||||
2026-02-13T19:43:27.5381753Z Initialized empty Git repository in /workspace/Moyettes/DiscordClone/.git/
|
||||
2026-02-13T19:43:27.5386979Z [command]/usr/bin/git remote add origin http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T19:43:27.5403050Z ::endgroup::
|
||||
2026-02-13T19:43:27.5403136Z ::group::Disabling automatic garbage collection
|
||||
2026-02-13T19:43:27.5404977Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-02-13T19:43:27.5418736Z ::endgroup::
|
||||
2026-02-13T19:43:27.5418822Z ::group::Setting up auth
|
||||
2026-02-13T19:43:27.5421993Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T19:43:27.5436342Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T19:43:27.5945058Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T19:43:27.5957281Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T19:43:27.6100789Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T19:43:27.6114980Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T19:43:27.6256036Z [command]/usr/bin/git config --local http.http://192.168.125.15:4000/.extraheader AUTHORIZATION: basic ***
|
||||
2026-02-13T19:43:27.6273020Z ::endgroup::
|
||||
2026-02-13T19:43:27.6273114Z ::group::Fetching the repository
|
||||
2026-02-13T19:43:27.6276461Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +6220da85141b9e4c77a30bf0c803645406c1f684:refs/remotes/origin/main
|
||||
2026-02-13T19:43:29.4446658Z From http://192.168.125.15:4000/Moyettes/DiscordClone
|
||||
2026-02-13T19:43:29.4446955Z * [new ref] 6220da85141b9e4c77a30bf0c803645406c1f684 -> origin/main
|
||||
2026-02-13T19:43:29.4460398Z ::endgroup::
|
||||
2026-02-13T19:43:29.4460566Z ::group::Determining the checkout info
|
||||
2026-02-13T19:43:29.4461655Z ::endgroup::
|
||||
2026-02-13T19:43:29.4469324Z [command]/usr/bin/git sparse-checkout disable
|
||||
2026-02-13T19:43:29.4489210Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
|
||||
2026-02-13T19:43:29.4503888Z ::group::Checking out the ref
|
||||
2026-02-13T19:43:29.4505707Z [command]/usr/bin/git checkout --progress --force -B main refs/remotes/origin/main
|
||||
2026-02-13T19:43:29.5371088Z Switched to a new branch 'main'
|
||||
2026-02-13T19:43:29.5371451Z Branch 'main' set up to track remote branch 'main' from 'origin'.
|
||||
2026-02-13T19:43:29.5377492Z ::endgroup::
|
||||
2026-02-13T19:43:29.5399138Z [command]/usr/bin/git log -1 --format=%H
|
||||
2026-02-13T19:43:29.5418768Z 6220da85141b9e4c77a30bf0c803645406c1f684
|
||||
2026-02-13T19:43:29.5426020Z ::remove-matcher owner=checkout-git::
|
||||
2026-02-13T19:43:30.7998481Z (node:218) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-02-13T19:43:30.7998567Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-02-13T19:43:50.8411974Z ::warning::Failed to restore: getCacheEntry failed: Request timeout: /_apis/artifactcache/cache?keys=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852%252Cnpm-electron-&version=f2531268ab9c19c75ce7b3eb23cc11c7f69fd3cf796834d4881591e430a373ff
|
||||
2026-02-13T19:43:50.8412611Z Cache not found for input keys: npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852, npm-electron-
|
||||
2026-02-13T19:43:51.5402006Z npm warn deprecated tar@6.2.1: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:43:51.7975546Z npm warn deprecated npmlog@6.0.2: This package is no longer supported.
|
||||
2026-02-13T19:43:52.0063861Z npm warn deprecated lodash.isequal@4.5.0: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
|
||||
2026-02-13T19:43:52.1082618Z npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
2026-02-13T19:43:52.1773184Z npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:43:52.1900248Z npm warn deprecated gauge@4.0.4: This package is no longer supported.
|
||||
2026-02-13T19:43:52.4885199Z npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
||||
2026-02-13T19:43:52.5222171Z npm warn deprecated are-we-there-yet@3.0.1: This package is no longer supported.
|
||||
2026-02-13T19:43:52.9121508Z npm warn deprecated @npmcli/move-file@2.0.1: This functionality has been moved to @npmcli/fs
|
||||
2026-02-13T19:43:53.4911508Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T19:43:53.5018834Z npm warn deprecated glob@9.3.5: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:43:53.6085654Z npm warn deprecated glob@10.5.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:43:53.6174294Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T19:43:53.6218292Z npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:43:53.6336436Z npm warn deprecated glob@8.1.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
2026-02-13T19:43:53.6577415Z npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
|
||||
2026-02-13T19:45:44.8599059Z
|
||||
2026-02-13T19:45:44.8599517Z added 677 packages, and audited 683 packages in 2m
|
||||
2026-02-13T19:45:44.8599658Z
|
||||
2026-02-13T19:45:44.8599704Z 183 packages are looking for funding
|
||||
2026-02-13T19:45:44.8599752Z run `npm fund` for details
|
||||
2026-02-13T19:45:44.8805402Z
|
||||
2026-02-13T19:45:44.8805571Z 11 vulnerabilities (1 moderate, 10 high)
|
||||
2026-02-13T19:45:44.8805633Z
|
||||
2026-02-13T19:45:44.8805684Z To address all issues (including breaking changes), run:
|
||||
2026-02-13T19:45:44.8805753Z npm audit fix --force
|
||||
2026-02-13T19:45:44.8805804Z
|
||||
2026-02-13T19:45:44.8805856Z Run `npm audit` for details.
|
||||
2026-02-13T19:45:44.8919475Z --- app-builder diagnostics ---
|
||||
2026-02-13T19:45:44.8931305Z -rwxr-xr-x 1 root root 18116608 Feb 13 19:43 node_modules/app-builder-bin/linux/x64/app-builder
|
||||
2026-02-13T19:45:45.0651733Z not a dynamic executable
|
||||
2026-02-13T19:45:45.0701432Z 3.5.10
|
||||
2026-02-13T19:45:45.0835728Z 3.5.10
|
||||
2026-02-13T19:45:45.2948318Z
|
||||
2026-02-13T19:45:45.2948942Z > @discord-clone/electron@1.0.14 build
|
||||
2026-02-13T19:45:45.2949056Z > vite build
|
||||
2026-02-13T19:45:45.2949130Z
|
||||
2026-02-13T19:45:45.4448102Z [36mvite v7.3.1 [32mbuilding client environment for production...[36m[39m
|
||||
2026-02-13T19:45:45.4794190Z transforming...
|
||||
2026-02-13T19:45:45.8322429Z [33mThe glob option "as" has been deprecated in favour of "query". Please update `as: 'url'` to `query: '?url', import: 'default'`.[39m
|
||||
2026-02-13T19:45:45.8404375Z [33m[plugin vite:esbuild] ../../packages/shared/src/components/ScreenShareModal.jsx: [33mDuplicate key "width" in object literal[33m
|
||||
2026-02-13T19:45:45.8404602Z 71 | }}
|
||||
2026-02-13T19:45:45.8404671Z 72 | >
|
||||
2026-02-13T19:45:45.8404717Z 73 | ...le={{ position: 'relative', width: '100%', height: '250px', width: '450px', borderRadius: '8px', overflow: 'hidden...
|
||||
2026-02-13T19:45:45.8404781Z | ^
|
||||
2026-02-13T19:45:45.8404825Z 74 | {/* Thumbnail/Placeholder */}
|
||||
2026-02-13T19:45:45.8404879Z 75 | {item.thumbnail ? (
|
||||
2026-02-13T19:45:45.8404928Z [39m
|
||||
2026-02-13T19:45:48.5194274Z [32m✓[39m 4636 modules transformed.
|
||||
2026-02-13T19:45:49.0494364Z rendering chunks...
|
||||
2026-02-13T19:45:49.0906445Z computing gzip size...
|
||||
2026-02-13T19:45:49.2419084Z [2mdist-react/[22m[32mindex.html [39m[1m[2m 0.63 kB[22m[1m[22m[2m │ gzip: 0.39 kB[22m
|
||||
2026-02-13T19:45:49.2419377Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher-CXwESYU3.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T19:45:49.2419447Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher_tone2-DWJ6rjnf.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T19:45:49.2419502Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher_tone3-BvnMOsM7.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T19:45:49.2419577Z [2mdist-react/[22m[2massets/[22m[32mwoman_teacher_tone4-C9bkU449.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.60 kB[22m
|
||||
2026-02-13T19:45:49.2419642Z [2mdist-react/[22m[2massets/[22m[32mbubble_tea-Cy1d5egt.svg [39m[1m[2m 4.10 kB[22m[1m[22m[2m │ gzip: 1.92 kB[22m
|
||||
2026-02-13T19:45:49.2419709Z [2mdist-react/[22m[2massets/[22m[32mflag_gq-B3TFx5qI.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.68 kB[22m
|
||||
2026-02-13T19:45:49.2419824Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position-9VFgclqE.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2419871Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone1-MK18jaNb.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2419917Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone2-Dzm_xqT2.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2419966Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone4-BtjpxNGo.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T19:45:49.2420016Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone3-DUxbd8tQ.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2420074Z [2mdist-react/[22m[2massets/[22m[32mperson_in_lotus_position_tone5-CinKf4VL.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T19:45:49.2420119Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone5-DOWKsCGX.svg [39m[1m[2m 4.11 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T19:45:49.2420169Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby-BLUtslbF.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T19:45:49.2420217Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone1-lg4dBAV2.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T19:45:49.2420262Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone4-IRS8MZPe.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T19:45:49.2420308Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone2-BuF25R9x.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T19:45:49.2420360Z [2mdist-react/[22m[2massets/[22m[32mman_feeding_baby_tone3-DEYxzqY-.svg [39m[1m[2m 4.13 kB[22m[1m[22m[2m │ gzip: 1.83 kB[22m
|
||||
2026-02-13T19:45:49.2420412Z [2mdist-react/[22m[2massets/[22m[32mwoman_zombie-Cn4gQ0af.svg [39m[1m[2m 4.15 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2420466Z [2mdist-react/[22m[2massets/[22m[32mhiking_boot-CPXD60gE.svg [39m[1m[2m 4.17 kB[22m[1m[22m[2m │ gzip: 1.76 kB[22m
|
||||
2026-02-13T19:45:49.2420513Z [2mdist-react/[22m[2massets/[22m[32mjapanese_ogre-BsqNvmIl.svg [39m[1m[2m 4.22 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T19:45:49.2420557Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone5-CuQMgf5h.svg [39m[1m[2m 4.23 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T19:45:49.2420606Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone1-A8sdhmvt.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2420657Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone3-DXZ2OdUI.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2420703Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer-D6jKOTyC.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2420752Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone2-DaHNn5-D.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.73 kB[22m
|
||||
2026-02-13T19:45:49.2420799Z [2mdist-react/[22m[2massets/[22m[32mwoman_police_officer_tone4--Oe8w2XD.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.72 kB[22m
|
||||
2026-02-13T19:45:49.2420896Z [2mdist-react/[22m[2massets/[22m[32mferris_wheel-DvW0t9g3.svg [39m[1m[2m 4.25 kB[22m[1m[22m[2m │ gzip: 1.19 kB[22m
|
||||
2026-02-13T19:45:49.2420941Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone5-Bk9uZHaS.svg [39m[1m[2m 4.26 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T19:45:49.2421002Z [2mdist-react/[22m[2massets/[22m[32mman_teacher-BRypTuYs.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T19:45:49.2421058Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone2-rQoIFfFz.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T19:45:49.2421117Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone4-C4j4RIq3.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.74 kB[22m
|
||||
2026-02-13T19:45:49.2421172Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone3-BEE8k6p5.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T19:45:49.2421225Z [2mdist-react/[22m[2massets/[22m[32mman_teacher_tone1-jNO2AiRD.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T19:45:49.2421282Z [2mdist-react/[22m[2massets/[22m[32msari-BSRA0_R3.svg [39m[1m[2m 4.27 kB[22m[1m[22m[2m │ gzip: 1.77 kB[22m
|
||||
2026-02-13T19:45:49.2421343Z [2mdist-react/[22m[2massets/[22m[32mgloves-BcY_RgAR.svg [39m[1m[2m 4.31 kB[22m[1m[22m[2m │ gzip: 1.62 kB[22m
|
||||
2026-02-13T19:45:49.2421390Z [2mdist-react/[22m[2massets/[22m[32mmoon_cake-BQr_VKRq.svg [39m[1m[2m 4.31 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2421446Z [2mdist-react/[22m[2massets/[22m[32mman_zombie-D5T1AZ12.svg [39m[1m[2m 4.34 kB[22m[1m[22m[2m │ gzip: 1.78 kB[22m
|
||||
2026-02-13T19:45:49.2421507Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone1-Mj4OdRWf.svg [39m[1m[2m 4.35 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2421563Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing-22i7hQgf.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T19:45:49.2421622Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone2-BcJfdMyX.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T19:45:49.2421681Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone3-D1NUtDA8.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T19:45:49.2421830Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone4-ByYdFJZp.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T19:45:49.2421883Z [2mdist-react/[22m[2massets/[22m[32mwoman_surfing_tone5-CrN9a9WS.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.95 kB[22m
|
||||
2026-02-13T19:45:49.2421940Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane-DRcmbgmz.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T19:45:49.2421994Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone2-CY2wYkQb.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T19:45:49.2422052Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone1-zKjrapc7.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.90 kB[22m
|
||||
2026-02-13T19:45:49.2422097Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone3-CdJAKQXv.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T19:45:49.2422142Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone4-DXYc5Dlc.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T19:45:49.2422195Z [2mdist-react/[22m[2massets/[22m[32mperson_with_probing_cane_tone5-DFAMgo57.svg [39m[1m[2m 4.38 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T19:45:49.2422248Z [2mdist-react/[22m[2massets/[22m[32mskier-BTSq18N5.svg [39m[1m[2m 4.39 kB[22m[1m[22m[2m │ gzip: 1.75 kB[22m
|
||||
2026-02-13T19:45:49.2422303Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone5-Co-5wXNK.svg [39m[1m[2m 4.40 kB[22m[1m[22m[2m │ gzip: 1.84 kB[22m
|
||||
2026-02-13T19:45:49.2422357Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone1-DbW2lM_k.svg [39m[1m[2m 4.42 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T19:45:49.2422404Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone1-D50RTI5B.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2422457Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane-BarJlRlV.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2422511Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone3-GUZ14VpU.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2422564Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone5-BCpRNGU4.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2422619Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone2-CIeZuoUa.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2422673Z [2mdist-react/[22m[2massets/[22m[32mman_with_probing_cane_tone4-Bd6iA8-d.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2422726Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone2-Bc-xqa4S.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T19:45:49.2422782Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer-skPaDBsj.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.84 kB[22m
|
||||
2026-02-13T19:45:49.2422833Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone3-CNRJeino.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m
|
||||
2026-02-13T19:45:49.2422887Z [2mdist-react/[22m[2massets/[22m[32mwoman_singer_tone4-CEfcWjkD.svg [39m[1m[2m 4.45 kB[22m[1m[22m[2m │ gzip: 1.84 kB[22m
|
||||
2026-02-13T19:45:49.2422948Z [2mdist-react/[22m[2massets/[22m[32mflag_sz-CsAySmAn.svg [39m[1m[2m 4.46 kB[22m[1m[22m[2m │ gzip: 1.66 kB[22m
|
||||
2026-02-13T19:45:49.2423005Z [2mdist-react/[22m[2massets/[22m[32msnowman2-CeWFCRvE.svg [39m[1m[2m 4.48 kB[22m[1m[22m[2m │ gzip: 1.15 kB[22m
|
||||
2026-02-13T19:45:49.2423066Z [2mdist-react/[22m[2massets/[22m[32mman_surfing-fqnQ3hm1.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T19:45:49.2423118Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone2-CDUKGUjg.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T19:45:49.2423171Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone1-BeC3CjNB.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.96 kB[22m
|
||||
2026-02-13T19:45:49.2423232Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone4-PI8ASA2j.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T19:45:49.2423280Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone3-Dt-HUBR5.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T19:45:49.2423332Z [2mdist-react/[22m[2massets/[22m[32mman_surfing_tone5-CyvTB2HT.svg [39m[1m[2m 4.49 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T19:45:49.2423387Z [2mdist-react/[22m[2massets/[22m[32mmotorized_wheelchair-DYMoavTH.svg [39m[1m[2m 4.50 kB[22m[1m[22m[2m │ gzip: 1.63 kB[22m
|
||||
2026-02-13T19:45:49.2423440Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone5-B6CmkSrw.svg [39m[1m[2m 4.54 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T19:45:49.2423498Z [2mdist-react/[22m[2massets/[22m[32mflag_ht-nORDdDQL.svg [39m[1m[2m 4.54 kB[22m[1m[22m[2m │ gzip: 1.88 kB[22m
|
||||
2026-02-13T19:45:49.2423557Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone1-BezQI8D_.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T19:45:49.2423613Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone3-DlgTa1f-.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T19:45:49.2423669Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone4-DVay5Top.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T19:45:49.2423813Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby-p-8aPRtV.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T19:45:49.2423868Z [2mdist-react/[22m[2massets/[22m[32mwoman_feeding_baby_tone2-CwnQLRQK.svg [39m[1m[2m 4.56 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T19:45:49.2423924Z [2mdist-react/[22m[2massets/[22m[32mflag_ad-CYOJPtjR.svg [39m[1m[2m 4.59 kB[22m[1m[22m[2m │ gzip: 1.79 kB[22m
|
||||
2026-02-13T19:45:49.2423976Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone1-DuB7wHWP.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2424030Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone3-CeM4gv4f.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2424086Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane-DyJEphms.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2424144Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone2-GEDDmrTN.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2424201Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone4-BaEnBpNy.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2424253Z [2mdist-react/[22m[2massets/[22m[32mwoman_with_probing_cane_tone5-DimRSWot.svg [39m[1m[2m 4.60 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2424315Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing-DEOz-TJs.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T19:45:49.2424368Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone1-B_kCGm1b.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T19:45:49.2424424Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone2-D35jmuhN.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.11 kB[22m
|
||||
2026-02-13T19:45:49.2424468Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone3-C60a5Aj1.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T19:45:49.2424526Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone4-nIpCJUpE.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T19:45:49.2424583Z [2mdist-react/[22m[2massets/[22m[32mperson_surfing_tone5-CXXi5x8s.svg [39m[1m[2m 4.62 kB[22m[1m[22m[2m │ gzip: 2.10 kB[22m
|
||||
2026-02-13T19:45:49.2424634Z [2mdist-react/[22m[2massets/[22m[32mflag_tc-Dn_lC0KY.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 1.94 kB[22m
|
||||
2026-02-13T19:45:49.2424691Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone2-BbPQ5nEE.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2424747Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone1-DhUQwNf7.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2424799Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing-Mc5PuEC7.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2424850Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone3-CKRqu9yJ.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2424911Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone4-DPEV2yNK.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2424963Z [2mdist-react/[22m[2massets/[22m[32mperson_golfing_tone5-Bgq3Ud_d.svg [39m[1m[2m 4.72 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2425009Z [2mdist-react/[22m[2massets/[22m[32mflag_ki-Ccc3Xi24.svg [39m[1m[2m 4.77 kB[22m[1m[22m[2m │ gzip: 1.66 kB[22m
|
||||
2026-02-13T19:45:49.2425059Z [2mdist-react/[22m[2massets/[22m[32mflag_mo-PAf1BQIO.svg [39m[1m[2m 4.77 kB[22m[1m[22m[2m │ gzip: 2.24 kB[22m
|
||||
2026-02-13T19:45:49.2425111Z [2mdist-react/[22m[2massets/[22m[32mflag_sh-CT89bJZi.svg [39m[1m[2m 4.80 kB[22m[1m[22m[2m │ gzip: 1.67 kB[22m
|
||||
2026-02-13T19:45:49.2425167Z [2mdist-react/[22m[2massets/[22m[32mcrab-D6qU1zIW.svg [39m[1m[2m 4.87 kB[22m[1m[22m[2m │ gzip: 1.97 kB[22m
|
||||
2026-02-13T19:45:49.2425220Z [2mdist-react/[22m[2massets/[22m[32mcouple-KSrP6fk0.svg [39m[1m[2m 4.90 kB[22m[1m[22m[2m │ gzip: 2.04 kB[22m
|
||||
2026-02-13T19:45:49.2425264Z [2mdist-react/[22m[2massets/[22m[32mkiwi-BWXr7Vjo.svg [39m[1m[2m 4.91 kB[22m[1m[22m[2m │ gzip: 2.27 kB[22m
|
||||
2026-02-13T19:45:49.2425309Z [2mdist-react/[22m[2massets/[22m[32mhedgehog-CMNxZzfp.svg [39m[1m[2m 4.92 kB[22m[1m[22m[2m │ gzip: 1.82 kB[22m
|
||||
2026-02-13T19:45:49.2425359Z [2mdist-react/[22m[2massets/[22m[32mmoney_with_wings-BnGoAiwj.svg [39m[1m[2m 4.95 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T19:45:49.2425408Z [2mdist-react/[22m[2massets/[22m[32mflag_kg-D_P2G_Do.svg [39m[1m[2m 4.98 kB[22m[1m[22m[2m │ gzip: 2.14 kB[22m
|
||||
2026-02-13T19:45:49.2425451Z [2mdist-react/[22m[2massets/[22m[32mmaracas-kQiWhg0J.svg [39m[1m[2m 4.99 kB[22m[1m[22m[2m │ gzip: 1.76 kB[22m
|
||||
2026-02-13T19:45:49.2425497Z [2mdist-react/[22m[2massets/[22m[32mx_ray-CWrdKTDm.svg [39m[1m[2m 4.99 kB[22m[1m[22m[2m │ gzip: 2.37 kB[22m
|
||||
2026-02-13T19:45:49.2425544Z [2mdist-react/[22m[2massets/[22m[32mpeople_holding_hands-BRZihiu5.svg [39m[1m[2m 5.03 kB[22m[1m[22m[2m │ gzip: 1.68 kB[22m
|
||||
2026-02-13T19:45:49.2425596Z [2mdist-react/[22m[2massets/[22m[32mbutterfly-AxzpD-Pg.svg [39m[1m[2m 5.04 kB[22m[1m[22m[2m │ gzip: 1.86 kB[22m
|
||||
2026-02-13T19:45:49.2425643Z [2mdist-react/[22m[2massets/[22m[32mflag_xk-D1vfCqOd.svg [39m[1m[2m 5.08 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2425688Z [2mdist-react/[22m[2massets/[22m[32mflag_tm-_4vioey7.svg [39m[1m[2m 5.13 kB[22m[1m[22m[2m │ gzip: 1.54 kB[22m
|
||||
2026-02-13T19:45:49.2425731Z [2mdist-react/[22m[2massets/[22m[32mtwo_men_holding_hands-BKJxHZb_.svg [39m[1m[2m 5.15 kB[22m[1m[22m[2m │ gzip: 1.50 kB[22m
|
||||
2026-02-13T19:45:49.2425781Z [2mdist-react/[22m[2massets/[22m[32mseal-Djs0F0U5.svg [39m[1m[2m 5.16 kB[22m[1m[22m[2m │ gzip: 2.18 kB[22m
|
||||
2026-02-13T19:45:49.2425829Z [2mdist-react/[22m[2massets/[22m[32mspeak_no_evil-EoRZCJhS.svg [39m[1m[2m 5.20 kB[22m[1m[22m[2m │ gzip: 2.27 kB[22m
|
||||
2026-02-13T19:45:49.2425885Z [2mdist-react/[22m[2massets/[22m[32mman_dancing-Dg-6O6t7.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2425937Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone1-XI7g5maV.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2426074Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone2-DBvANdsI.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2426128Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone4-D9FZRxV5.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2426176Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone3-BK7ka3J3.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2426227Z [2mdist-react/[22m[2massets/[22m[32mman_dancing_tone5-CQh9niVO.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 2.00 kB[22m
|
||||
2026-02-13T19:45:49.2426287Z [2mdist-react/[22m[2massets/[22m[32mflag_pf-OA_PTTaZ.svg [39m[1m[2m 5.22 kB[22m[1m[22m[2m │ gzip: 1.81 kB[22m
|
||||
2026-02-13T19:45:49.2426339Z [2mdist-react/[22m[2massets/[22m[32mpie-DZ6nmSau.svg [39m[1m[2m 5.24 kB[22m[1m[22m[2m │ gzip: 1.98 kB[22m
|
||||
2026-02-13T19:45:49.2426394Z [2mdist-react/[22m[2massets/[22m[32mtwo_women_holding_hands-CnKtKnaZ.svg [39m[1m[2m 5.29 kB[22m[1m[22m[2m │ gzip: 1.71 kB[22m
|
||||
2026-02-13T19:45:49.2426448Z [2mdist-react/[22m[2massets/[22m[32mbrain-Czvux5Q4.svg [39m[1m[2m 5.32 kB[22m[1m[22m[2m │ gzip: 2.48 kB[22m
|
||||
2026-02-13T19:45:49.2426505Z [2mdist-react/[22m[2massets/[22m[32mlacrosse-DK95k1kF.svg [39m[1m[2m 5.33 kB[22m[1m[22m[2m │ gzip: 2.22 kB[22m
|
||||
2026-02-13T19:45:49.2426549Z [2mdist-react/[22m[2massets/[22m[32msee_no_evil-DnuksPIR.svg [39m[1m[2m 5.42 kB[22m[1m[22m[2m │ gzip: 2.38 kB[22m
|
||||
2026-02-13T19:45:49.2426595Z [2mdist-react/[22m[2massets/[22m[32mflag_vi-vzZjsoBi.svg [39m[1m[2m 5.43 kB[22m[1m[22m[2m │ gzip: 2.34 kB[22m
|
||||
2026-02-13T19:45:49.2426646Z [2mdist-react/[22m[2massets/[22m[32mdodo-CoZFlciJ.svg [39m[1m[2m 5.44 kB[22m[1m[22m[2m │ gzip: 2.31 kB[22m
|
||||
2026-02-13T19:45:49.2426694Z [2mdist-react/[22m[2massets/[22m[32mflag_hk-CzNuCBPg.svg [39m[1m[2m 5.46 kB[22m[1m[22m[2m │ gzip: 2.52 kB[22m
|
||||
2026-02-13T19:45:49.2426816Z [2mdist-react/[22m[2massets/[22m[32mspider_web-DPcv-q20.svg [39m[1m[2m 5.50 kB[22m[1m[22m[2m │ gzip: 2.43 kB[22m
|
||||
2026-02-13T19:45:49.2426866Z [2mdist-react/[22m[2massets/[22m[32mflag_bl-BoaeaHPp.svg [39m[1m[2m 5.58 kB[22m[1m[22m[2m │ gzip: 1.90 kB[22m
|
||||
2026-02-13T19:45:49.2426910Z [2mdist-react/[22m[2massets/[22m[32mflag_as-B43i20pO.svg [39m[1m[2m 5.61 kB[22m[1m[22m[2m │ gzip: 2.39 kB[22m
|
||||
2026-02-13T19:45:49.2426962Z [2mdist-react/[22m[2massets/[22m[32mflag_gp-DW1UVBGw.svg [39m[1m[2m 5.63 kB[22m[1m[22m[2m │ gzip: 2.05 kB[22m
|
||||
2026-02-13T19:45:49.2427008Z [2mdist-react/[22m[2massets/[22m[32mflag_ic-BrB5Xakj.svg [39m[1m[2m 5.63 kB[22m[1m[22m[2m │ gzip: 2.24 kB[22m
|
||||
2026-02-13T19:45:49.2427053Z [2mdist-react/[22m[2massets/[22m[32mman_golfing-DhSLN6KQ.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T19:45:49.2427097Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone3-vxj0o6sI.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T19:45:49.2427147Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone2-CNmTGsfk.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T19:45:49.2427202Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone1-DBE1f7b1.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T19:45:49.2427253Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone4-CsGYmisz.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.61 kB[22m
|
||||
2026-02-13T19:45:49.2427306Z [2mdist-react/[22m[2massets/[22m[32mman_golfing_tone5-Cf_z4uyg.svg [39m[1m[2m 5.64 kB[22m[1m[22m[2m │ gzip: 2.60 kB[22m
|
||||
2026-02-13T19:45:49.2427361Z [2mdist-react/[22m[2massets/[22m[32mflag_bm-CzSakp_Z.svg [39m[1m[2m 5.67 kB[22m[1m[22m[2m │ gzip: 2.37 kB[22m
|
||||
2026-02-13T19:45:49.2427425Z [2mdist-react/[22m[2massets/[22m[32mmap-BGXvLkiw.svg [39m[1m[2m 5.68 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2427478Z [2mdist-react/[22m[2massets/[22m[32mpeople_wrestling-DjCLlDDS.svg [39m[1m[2m 5.71 kB[22m[1m[22m[2m │ gzip: 2.48 kB[22m
|
||||
2026-02-13T19:45:49.2427531Z [2mdist-react/[22m[2massets/[22m[32mtamale-2biJGrAo.svg [39m[1m[2m 5.74 kB[22m[1m[22m[2m │ gzip: 2.53 kB[22m
|
||||
2026-02-13T19:45:49.2427583Z [2mdist-react/[22m[2massets/[22m[32mmen_wrestling-BNuLmHCV.svg [39m[1m[2m 5.74 kB[22m[1m[22m[2m │ gzip: 2.47 kB[22m
|
||||
2026-02-13T19:45:49.2427639Z [2mdist-react/[22m[2massets/[22m[32mempty_nest-DGy7reBo.svg [39m[1m[2m 5.79 kB[22m[1m[22m[2m │ gzip: 2.71 kB[22m
|
||||
2026-02-13T19:45:49.2427791Z [2mdist-react/[22m[2massets/[22m[32mworm-CxRJMG1n.svg [39m[1m[2m 5.86 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T19:45:49.2427843Z [2mdist-react/[22m[2massets/[22m[32mflag_bo-B7hNQ755.svg [39m[1m[2m 5.91 kB[22m[1m[22m[2m │ gzip: 2.22 kB[22m
|
||||
2026-02-13T19:45:49.2427900Z [2mdist-react/[22m[2massets/[22m[32mface_in_clouds-DBzCKo8S.svg [39m[1m[2m 5.94 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T19:45:49.2427948Z [2mdist-react/[22m[2massets/[22m[32mwomen_wrestling-CARP3ZvF.svg [39m[1m[2m 5.96 kB[22m[1m[22m[2m │ gzip: 2.59 kB[22m
|
||||
2026-02-13T19:45:49.2427998Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone5-cnCH-jDP.svg [39m[1m[2m 6.00 kB[22m[1m[22m[2m │ gzip: 2.06 kB[22m
|
||||
2026-02-13T19:45:49.2428053Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights-DkiBT0IO.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T19:45:49.2428110Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone1-DGilOf2d.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.06 kB[22m
|
||||
2026-02-13T19:45:49.2428165Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone4-MLQqpJKZ.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T19:45:49.2428219Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone2-CXUv2fBp.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T19:45:49.2428276Z [2mdist-react/[22m[2massets/[22m[32mman_lifting_weights_tone3-DhF3q93u.svg [39m[1m[2m 6.03 kB[22m[1m[22m[2m │ gzip: 2.07 kB[22m
|
||||
2026-02-13T19:45:49.2428329Z [2mdist-react/[22m[2massets/[22m[32mflag_fj-B2-D6gPQ.svg [39m[1m[2m 6.04 kB[22m[1m[22m[2m │ gzip: 2.64 kB[22m
|
||||
2026-02-13T19:45:49.2428384Z [2mdist-react/[22m[2massets/[22m[32mflag_pn-Bde7vecB.svg [39m[1m[2m 6.05 kB[22m[1m[22m[2m │ gzip: 2.68 kB[22m
|
||||
2026-02-13T19:45:49.2428437Z [2mdist-react/[22m[2massets/[22m[32mflag_bt-COHVTZ6I.svg [39m[1m[2m 6.06 kB[22m[1m[22m[2m │ gzip: 2.52 kB[22m
|
||||
2026-02-13T19:45:49.2428491Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel-B6e7BEW_.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T19:45:49.2428554Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone1-TBt_b-Oj.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T19:45:49.2428609Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone2-BR4ztGzg.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T19:45:49.2428662Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone4-j074vq-9.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T19:45:49.2428850Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone3-BzmNF0vv.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T19:45:49.2428905Z [2mdist-react/[22m[2massets/[22m[32mperson_doing_cartwheel_tone5-BzNEt2oA.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.65 kB[22m
|
||||
2026-02-13T19:45:49.2428957Z [2mdist-react/[22m[2massets/[22m[32maccordion-BPueGNgN.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 1.20 kB[22m
|
||||
2026-02-13T19:45:49.2429012Z [2mdist-react/[22m[2massets/[22m[32mlobster-Cfls8jg_.svg [39m[1m[2m 6.07 kB[22m[1m[22m[2m │ gzip: 2.32 kB[22m
|
||||
2026-02-13T19:45:49.2429067Z [2mdist-react/[22m[2massets/[22m[32mvolcano-Bh_Lqk9r.svg [39m[1m[2m 6.14 kB[22m[1m[22m[2m │ gzip: 2.70 kB[22m
|
||||
2026-02-13T19:45:49.2429121Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling-NFQt9ZB9.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2429179Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone1-B3S_eUE1.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2429223Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone2-CYBBI2iM.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2429270Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone3-D2kqEChS.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2429324Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone5-PFLWmq7Q.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2429380Z [2mdist-react/[22m[2massets/[22m[32mman_cartwheeling_tone4-B96D58fZ.svg [39m[1m[2m 6.17 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2429436Z [2mdist-react/[22m[2massets/[22m[32mflag_lb-DHr4ylgr.svg [39m[1m[2m 6.19 kB[22m[1m[22m[2m │ gzip: 2.75 kB[22m
|
||||
2026-02-13T19:45:49.2429489Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone1-22QBgB92.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2429544Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball-C_yN7fGQ.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2429598Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone2-Bs8PtV12.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2429651Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone3-q-BDso_I.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2429716Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone5-DK-UJ5SH.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2429768Z [2mdist-react/[22m[2massets/[22m[32mman_playing_handball_tone4-BUH96fLA.svg [39m[1m[2m 6.21 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2429820Z [2mdist-react/[22m[2massets/[22m[32mflag_fk-1KKBtSFw.svg [39m[1m[2m 6.22 kB[22m[1m[22m[2m │ gzip: 2.54 kB[22m
|
||||
2026-02-13T19:45:49.2429872Z [2mdist-react/[22m[2massets/[22m[32mflag_rs-CmpxaRIS.svg [39m[1m[2m 6.23 kB[22m[1m[22m[2m │ gzip: 2.35 kB[22m
|
||||
2026-02-13T19:45:49.2429929Z [2mdist-react/[22m[2massets/[22m[32mcucumber-oVkPYVB9.svg [39m[1m[2m 6.24 kB[22m[1m[22m[2m │ gzip: 2.01 kB[22m
|
||||
2026-02-13T19:45:49.2429979Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone5-BJQrRdVE.svg [39m[1m[2m 6.25 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2430038Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights-CsixMYFL.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2430092Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone1-BpRsBk7z.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2430147Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone2-P18Nfbuz.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2430211Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone3-C0gnGp49.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.15 kB[22m
|
||||
2026-02-13T19:45:49.2430265Z [2mdist-react/[22m[2massets/[22m[32mwoman_lifting_weights_tone4-CQZmiYUl.svg [39m[1m[2m 6.28 kB[22m[1m[22m[2m │ gzip: 2.16 kB[22m
|
||||
2026-02-13T19:45:49.2430318Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone5-DEciUSJH.svg [39m[1m[2m 6.29 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T19:45:49.2430371Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights-Cn0dQ6qY.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T19:45:49.2430438Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone1-CXfKAA0L.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.19 kB[22m
|
||||
2026-02-13T19:45:49.2430494Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone2-Dkw3-09P.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T19:45:49.2430548Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone4-C62SuN24.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T19:45:49.2430600Z [2mdist-react/[22m[2massets/[22m[32mperson_lifting_weights_tone3-3OqiHF7e.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 2.20 kB[22m
|
||||
2026-02-13T19:45:49.2430653Z [2mdist-react/[22m[2massets/[22m[32mwomen_with_bunny_ears_partying-CKr9TLic.svg [39m[1m[2m 6.30 kB[22m[1m[22m[2m │ gzip: 1.68 kB[22m
|
||||
2026-02-13T19:45:49.2430712Z [2mdist-react/[22m[2massets/[22m[32mflag_sm-BYO1ASeM.svg [39m[1m[2m 6.31 kB[22m[1m[22m[2m │ gzip: 2.30 kB[22m
|
||||
2026-02-13T19:45:49.2430766Z [2mdist-react/[22m[2massets/[22m[32mcoat-Cbu3wnI6.svg [39m[1m[2m 6.35 kB[22m[1m[22m[2m │ gzip: 2.52 kB[22m
|
||||
2026-02-13T19:45:49.2430823Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone1-fJFXi2hD.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.78 kB[22m
|
||||
2026-02-13T19:45:49.2430876Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling-tGvm940R.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.78 kB[22m
|
||||
2026-02-13T19:45:49.2430929Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone2-C5lE2K9g.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T19:45:49.2430985Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone3-BourpL3A.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T19:45:49.2431045Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone4-CjyM2w54.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T19:45:49.2431098Z [2mdist-react/[22m[2massets/[22m[32mwoman_cartwheeling_tone5-D-eW47Ua.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.77 kB[22m
|
||||
2026-02-13T19:45:49.2431150Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone1-BbRoQah0.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T19:45:49.2431210Z [2mdist-react/[22m[2massets/[22m[32mman_running-Bp7fZpx0.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T19:45:49.2431264Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone2-gBe1A9EP.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T19:45:49.2431316Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone4-CeeXJkX_.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T19:45:49.2431369Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone5-Do-aIXEX.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T19:45:49.2431422Z [2mdist-react/[22m[2massets/[22m[32mman_running_tone3-DfAx9qZO.svg [39m[1m[2m 6.37 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T19:45:49.2431478Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball-fiyPmBDz.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T19:45:49.2431539Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone2-BtTxnxhZ.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T19:45:49.2431591Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone1-B_P42W0r.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T19:45:49.2431644Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone3-C7TXAAWV.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T19:45:49.2431821Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone4-CtCwRGCv.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T19:45:49.2431880Z [2mdist-react/[22m[2massets/[22m[32mwoman_playing_handball_tone5-CmZlugee.svg [39m[1m[2m 6.42 kB[22m[1m[22m[2m │ gzip: 2.73 kB[22m
|
||||
2026-02-13T19:45:49.2432108Z [2mdist-react/[22m[2massets/[22m[32mmicrobe-DHWlm4x3.svg [39m[1m[2m 6.48 kB[22m[1m[22m[2m │ gzip: 2.82 kB[22m
|
||||
2026-02-13T19:45:49.2432162Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone2-kHM6lt0G.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T19:45:49.2432217Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing-Cd5KXigQ.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T19:45:49.2432273Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone1-BPFu29EM.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T19:45:49.2432328Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone3-1prjoMK9.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T19:45:49.2432395Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone4-DZVx5-VD.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T19:45:49.2432449Z [2mdist-react/[22m[2massets/[22m[32mhorse_racing_tone5-DoKtvypB.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.88 kB[22m
|
||||
2026-02-13T19:45:49.2432502Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair-B2ofcHYu.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T19:45:49.2432565Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone2-DmJ1Zffk.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T19:45:49.2432622Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone1-BrR0l2XR.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T19:45:49.2432675Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone4-TZTDWyKD.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T19:45:49.2432728Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone3-Bt_5AaRy.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T19:45:49.2432783Z [2mdist-react/[22m[2massets/[22m[32mperson_in_manual_wheelchair_tone5-DrOKlCDl.svg [39m[1m[2m 6.50 kB[22m[1m[22m[2m │ gzip: 2.57 kB[22m
|
||||
2026-02-13T19:45:49.2432838Z [2mdist-react/[22m[2massets/[22m[32mburrito-B4L0kbwK.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.58 kB[22m
|
||||
2026-02-13T19:45:49.2432890Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone3-Dist2leS.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T19:45:49.2432945Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone2-DNzEDUb0.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T19:45:49.2432996Z [2mdist-react/[22m[2massets/[22m[32mperson_running-DNDUEkxU.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T19:45:49.2433050Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone1-B8sLRwke.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T19:45:49.2433109Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone5-DEOJVy8u.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.93 kB[22m
|
||||
2026-02-13T19:45:49.2433161Z [2mdist-react/[22m[2massets/[22m[32mperson_running_tone4-DVBWC3-p.svg [39m[1m[2m 6.52 kB[22m[1m[22m[2m │ gzip: 2.94 kB[22m
|
||||
2026-02-13T19:45:49.2433213Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone1-Da2hybrT.svg [39m[1m[2m 6.57 kB[22m[1m[22m[2m │ gzip: 2.62 kB[22m
|
||||
2026-02-13T19:45:49.2433270Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair-cGfKOLRc.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T19:45:49.2433326Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone5-YZQTD5Nr.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T19:45:49.2433380Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone2-BPBmkRcs.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T19:45:49.2433433Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone3-H5kpv3q_.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T19:45:49.2433488Z [2mdist-react/[22m[2massets/[22m[32mman_in_manual_wheelchair_tone4-BvKWPBcq.svg [39m[1m[2m 6.60 kB[22m[1m[22m[2m │ gzip: 2.63 kB[22m
|
||||
2026-02-13T19:45:49.2433541Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball-CH3hWpQR.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2433599Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone1-CbOONp_g.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2433655Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone2-jeC51_-P.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2433710Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone3-BGgWTsuS.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2433763Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone4-BsA09Avm.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2433817Z [2mdist-react/[22m[2massets/[22m[32mperson_playing_handball_tone5-D_rmeJiN.svg [39m[1m[2m 6.62 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2433870Z [2mdist-react/[22m[2massets/[22m[32mmen_with_bunny_ears_partying-DabknRQ1.svg [39m[1m[2m 6.64 kB[22m[1m[22m[2m │ gzip: 1.78 kB[22m
|
||||
2026-02-13T19:45:49.2433923Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball-BCtAjpGP.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.04 kB[22m
|
||||
2026-02-13T19:45:49.2433975Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone2-pU3f7Oqo.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T19:45:49.2434029Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone1-BrCW39oq.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T19:45:49.2434076Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone4-BonEB_V5.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T19:45:49.2434121Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone3-CMYhYDFZ.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T19:45:49.2434175Z [2mdist-react/[22m[2massets/[22m[32mman_bouncing_ball_tone5-mVU7qtFm.svg [39m[1m[2m 6.67 kB[22m[1m[22m[2m │ gzip: 3.05 kB[22m
|
||||
2026-02-13T19:45:49.2434228Z [2mdist-react/[22m[2massets/[22m[32mwoman_running-_mwbLWM0.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T19:45:49.2434280Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone1-Dfqdg043.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T19:45:49.2434339Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone2-rXRqTMa0.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T19:45:49.2434392Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone3-BmRDPwCM.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T19:45:49.2434446Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone4-DmFzAsxD.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T19:45:49.2434498Z [2mdist-react/[22m[2massets/[22m[32mwoman_running_tone5-C66GYSAh.svg [39m[1m[2m 6.74 kB[22m[1m[22m[2m │ gzip: 3.03 kB[22m
|
||||
2026-02-13T19:45:49.2434552Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair-Ba72kfnU.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2434606Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone1-Ce9x88Rf.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2434659Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone2-CAKIPnJE.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2434718Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone3-D4YsEoBp.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2434775Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone4-BD3k04p2.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2434888Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_manual_wheelchair_tone5-BmBeJ4-f.svg [39m[1m[2m 6.75 kB[22m[1m[22m[2m │ gzip: 2.66 kB[22m
|
||||
2026-02-13T19:45:49.2434943Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone1-Dcta4qUb.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.68 kB[22m
|
||||
2026-02-13T19:45:49.2434998Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone2-C8UQYonN.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2435052Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair-DxhhvjYe.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2435108Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone4-DLSO0rlF.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2435164Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone5-SnULyxgF.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2435223Z [2mdist-react/[22m[2massets/[22m[32mperson_in_motorized_wheelchair_tone3-BRD_Obbg.svg [39m[1m[2m 6.80 kB[22m[1m[22m[2m │ gzip: 2.69 kB[22m
|
||||
2026-02-13T19:45:49.2435277Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball-H1IsbPT2.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T19:45:49.2435330Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone1-BIhBY2_P.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T19:45:49.2435387Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone2-9V5mlEG0.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T19:45:49.2435438Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone5-C9pS5gcg.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T19:45:49.2435491Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone4-BycyNnMy.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T19:45:49.2435542Z [2mdist-react/[22m[2massets/[22m[32mperson_bouncing_ball_tone3-DSpJYpZ1.svg [39m[1m[2m 6.82 kB[22m[1m[22m[2m │ gzip: 3.10 kB[22m
|
||||
2026-02-13T19:45:49.2435594Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone1-B-J_H3TB.svg [39m[1m[2m 6.85 kB[22m[1m[22m[2m │ gzip: 2.71 kB[22m
|
||||
2026-02-13T19:45:49.2435651Z [2mdist-react/[22m[2massets/[22m[32mflag_ms-BKjfidu-.svg [39m[1m[2m 6.86 kB[22m[1m[22m[2m │ gzip: 3.00 kB[22m
|
||||
2026-02-13T19:45:49.2435702Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair-CiMQlH-Z.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T19:45:49.2435755Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone2-DQy0C3Cx.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T19:45:49.2435810Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone4-CoEn9n-F.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T19:45:49.2435865Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone3-DuduwQoe.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T19:45:49.2435920Z [2mdist-react/[22m[2massets/[22m[32mman_in_motorized_wheelchair_tone5-CgvQDAuT.svg [39m[1m[2m 6.87 kB[22m[1m[22m[2m │ gzip: 2.72 kB[22m
|
||||
2026-02-13T19:45:49.2436132Z [2mdist-react/[22m[2massets/[22m[32mflag_ky-E8sT-Yzf.svg [39m[1m[2m 6.99 kB[22m[1m[22m[2m │ gzip: 2.92 kB[22m
|
||||
2026-02-13T19:45:49.2436186Z [2mdist-react/[22m[2massets/[22m[32manatomical_heart-DbQDqK_8.svg [39m[1m[2m 7.00 kB[22m[1m[22m[2m │ gzip: 3.14 kB[22m
|
||||
2026-02-13T19:45:49.2436238Z [2mdist-react/[22m[2massets/[22m[32mwales-ll0ySOk-.svg [39m[1m[2m 7.01 kB[22m[1m[22m[2m │ gzip: 2.89 kB[22m
|
||||
2026-02-13T19:45:49.2436297Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair-CIaEP3y5.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T19:45:49.2436354Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone2-uhLYilhF.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T19:45:49.2436407Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone1-1BibIgKr.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.78 kB[22m
|
||||
2026-02-13T19:45:49.2436462Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone3-B51r71l0.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T19:45:49.2436515Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone4-oIvpxZcp.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T19:45:49.2436572Z [2mdist-react/[22m[2massets/[22m[32mwoman_in_motorized_wheelchair_tone5-_fFN26h0.svg [39m[1m[2m 7.03 kB[22m[1m[22m[2m │ gzip: 2.79 kB[22m
|
||||
2026-02-13T19:45:49.2436626Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball-B4V8jGG-.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.20 kB[22m
|
||||
2026-02-13T19:45:49.2436680Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone2-BPyPsinZ.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T19:45:49.2436734Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone1-I7gUQpbX.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T19:45:49.2436790Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone3-UqVs8gxM.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T19:45:49.2436846Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone4-CtQI59zT.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T19:45:49.2436899Z [2mdist-react/[22m[2massets/[22m[32mwoman_bouncing_ball_tone5-BgHu12i2.svg [39m[1m[2m 7.09 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T19:45:49.2436950Z [2mdist-react/[22m[2massets/[22m[32mflag_va-BB2uDrB0.svg [39m[1m[2m 7.21 kB[22m[1m[22m[2m │ gzip: 2.49 kB[22m
|
||||
2026-02-13T19:45:49.2437003Z [2mdist-react/[22m[2massets/[22m[32mmammoth-Diaisynz.svg [39m[1m[2m 7.29 kB[22m[1m[22m[2m │ gzip: 3.06 kB[22m
|
||||
2026-02-13T19:45:49.2437061Z [2mdist-react/[22m[2massets/[22m[32mnest_with_eggs-C5ulh3Rz.svg [39m[1m[2m 7.36 kB[22m[1m[22m[2m │ gzip: 3.39 kB[22m
|
||||
2026-02-13T19:45:49.2437115Z [2mdist-react/[22m[2massets/[22m[32mphoenix-QKXqSCuH.svg [39m[1m[2m 7.57 kB[22m[1m[22m[2m │ gzip: 2.91 kB[22m
|
||||
2026-02-13T19:45:49.2437167Z [2mdist-react/[22m[2massets/[22m[32mflag_cy-JKjUtxO9.svg [39m[1m[2m 7.60 kB[22m[1m[22m[2m │ gzip: 3.32 kB[22m
|
||||
2026-02-13T19:45:49.2437220Z [2mdist-react/[22m[2massets/[22m[32mpeople_with_bunny_ears_partying-BVR6SBwD.svg [39m[1m[2m 7.63 kB[22m[1m[22m[2m │ gzip: 1.93 kB[22m
|
||||
2026-02-13T19:45:49.2437276Z [2mdist-react/[22m[2massets/[22m[32mflag_gu-CyZZwWUz.svg [39m[1m[2m 7.68 kB[22m[1m[22m[2m │ gzip: 3.24 kB[22m
|
||||
2026-02-13T19:45:49.2437338Z [2mdist-react/[22m[2massets/[22m[32mmute-CFH4QcSE.mp3 [39m[1m[2m 7.74 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2437391Z [2mdist-react/[22m[2massets/[22m[32mt_rex-BYG-fgI4.svg [39m[1m[2m 8.00 kB[22m[1m[22m[2m │ gzip: 3.35 kB[22m
|
||||
2026-02-13T19:45:49.2437442Z [2mdist-react/[22m[2massets/[22m[32mflag_vg-DWuAWiyw.svg [39m[1m[2m 8.21 kB[22m[1m[22m[2m │ gzip: 1.91 kB[22m
|
||||
2026-02-13T19:45:49.2437497Z [2mdist-react/[22m[2massets/[22m[32mflag_yt-BfOxXbO5.svg [39m[1m[2m 8.42 kB[22m[1m[22m[2m │ gzip: 2.95 kB[22m
|
||||
2026-02-13T19:45:49.2437548Z [2mdist-react/[22m[2massets/[22m[32mpiñata-CQK6iMPe.svg [39m[1m[2m 8.47 kB[22m[1m[22m[2m │ gzip: 3.21 kB[22m
|
||||
2026-02-13T19:45:49.2437605Z [2mdist-react/[22m[2massets/[22m[32mmirror_ball-R_criUm_.svg [39m[1m[2m 8.55 kB[22m[1m[22m[2m │ gzip: 3.31 kB[22m
|
||||
2026-02-13T19:45:49.2437659Z [2mdist-react/[22m[2massets/[22m[32mping-LfakLpwb.mp3 [39m[1m[2m 8.58 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2437733Z [2mdist-react/[22m[2massets/[22m[32mflag_gs-DhFNtBGF.svg [39m[1m[2m 8.86 kB[22m[1m[22m[2m │ gzip: 3.67 kB[22m
|
||||
2026-02-13T19:45:49.2437792Z [2mdist-react/[22m[2massets/[22m[32mknot-CpRGiIMe.svg [39m[1m[2m 8.92 kB[22m[1m[22m[2m │ gzip: 3.89 kB[22m
|
||||
2026-02-13T19:45:49.2437847Z [2mdist-react/[22m[2massets/[22m[32mflag_dg-DwJEN7pv.svg [39m[1m[2m 9.06 kB[22m[1m[22m[2m │ gzip: 2.87 kB[22m
|
||||
2026-02-13T19:45:49.2437902Z [2mdist-react/[22m[2massets/[22m[32mflag_gt-CietPgvg.svg [39m[1m[2m 9.11 kB[22m[1m[22m[2m │ gzip: 3.86 kB[22m
|
||||
2026-02-13T19:45:49.2437956Z [2mdist-react/[22m[2massets/[22m[32mflag_mx-g-aNhK9D.svg [39m[1m[2m 9.66 kB[22m[1m[22m[2m │ gzip: 3.72 kB[22m
|
||||
2026-02-13T19:45:49.2438009Z [2mdist-react/[22m[2massets/[22m[32mflag_ta-Q6DTxsoW.svg [39m[1m[2m 10.30 kB[22m[1m[22m[2m │ gzip: 4.05 kB[22m
|
||||
2026-02-13T19:45:49.2438062Z [2mdist-react/[22m[2massets/[22m[32mflag_je-CGBxZBdT.svg [39m[1m[2m 10.35 kB[22m[1m[22m[2m │ gzip: 4.17 kB[22m
|
||||
2026-02-13T19:45:49.2438119Z [2mdist-react/[22m[2massets/[22m[32munmute-BDrGNM7s.mp3 [39m[1m[2m 10.67 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2438170Z [2mdist-react/[22m[2massets/[22m[32mflag_do-sBcfT32z.svg [39m[1m[2m 11.37 kB[22m[1m[22m[2m │ gzip: 4.67 kB[22m
|
||||
2026-02-13T19:45:49.2438223Z [2mdist-react/[22m[2massets/[22m[32mdeafen-BWE6ozKl.mp3 [39m[1m[2m 11.92 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2438275Z [2mdist-react/[22m[2massets/[22m[32mflag_sa-B3EC8eCD.svg [39m[1m[2m 12.29 kB[22m[1m[22m[2m │ gzip: 5.12 kB[22m
|
||||
2026-02-13T19:45:49.2438337Z [2mdist-react/[22m[2massets/[22m[32mflag_al-D439po3l.svg [39m[1m[2m 12.43 kB[22m[1m[22m[2m │ gzip: 5.32 kB[22m
|
||||
2026-02-13T19:45:49.2438392Z [2mdist-react/[22m[2massets/[22m[32mundeafen-DHeUZCMd.mp3 [39m[1m[2m 13.59 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2438449Z [2mdist-react/[22m[2massets/[22m[32mflag_bz-B34xZjVJ.svg [39m[1m[2m 13.64 kB[22m[1m[22m[2m │ gzip: 5.44 kB[22m
|
||||
2026-02-13T19:45:49.2438503Z [2mdist-react/[22m[2massets/[22m[32mflag_pm-C-C2d-w4.svg [39m[1m[2m 13.65 kB[22m[1m[22m[2m │ gzip: 3.80 kB[22m
|
||||
2026-02-13T19:45:49.2438562Z [2mdist-react/[22m[2massets/[22m[32mleave_call-DtItyZMg.mp3 [39m[1m[2m 14.43 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2438616Z [2mdist-react/[22m[2massets/[22m[32mflag_nf-BjOIhoMF.svg [39m[1m[2m 14.69 kB[22m[1m[22m[2m │ gzip: 5.98 kB[22m
|
||||
2026-02-13T19:45:49.2438669Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_start-B8JctOq8.mp3 [39m[1m[2m 16.13 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2438721Z [2mdist-react/[22m[2massets/[22m[32mflag_ac-Dr8n8VBW.svg [39m[1m[2m 16.66 kB[22m[1m[22m[2m │ gzip: 5.62 kB[22m
|
||||
2026-02-13T19:45:49.2438774Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_stop-DhppajDk.mp3 [39m[1m[2m 18.43 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2438829Z [2mdist-react/[22m[2massets/[22m[32mpotted_plant-BHg6K0D8.svg [39m[1m[2m 21.00 kB[22m[1m[22m[2m │ gzip: 8.96 kB[22m
|
||||
2026-02-13T19:45:49.2438900Z [2mdist-react/[22m[2massets/[22m[32mflag_mp-Bs0Xr_ND.svg [39m[1m[2m 24.06 kB[22m[1m[22m[2m │ gzip: 9.57 kB[22m
|
||||
2026-02-13T19:45:49.2438955Z [2mdist-react/[22m[2massets/[22m[32mflag_af-CN78RMpg.svg [39m[1m[2m 24.13 kB[22m[1m[22m[2m │ gzip: 9.18 kB[22m
|
||||
2026-02-13T19:45:49.2439007Z [2mdist-react/[22m[2massets/[22m[32mflag_kz-D77IkgDL.svg [39m[1m[2m 26.58 kB[22m[1m[22m[2m │ gzip: 9.02 kB[22m
|
||||
2026-02-13T19:45:49.2439061Z [2mdist-react/[22m[2massets/[22m[32munited_nations-BC9awctQ.svg [39m[1m[2m 26.58 kB[22m[1m[22m[2m │ gzip: 10.32 kB[22m
|
||||
2026-02-13T19:45:49.2439118Z [2mdist-react/[22m[2massets/[22m[32mjoin_call-B65a9Ev2.mp3 [39m[1m[2m 30.54 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439173Z [2mdist-react/[22m[2massets/[22m[32mgg sans Regular-Bd8GJPVd.woff [39m[1m[2m 39.09 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439225Z [2mdist-react/[22m[2massets/[22m[32memojies_greyscale-CtRIvx0g.png [39m[1m[2m 39.11 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439276Z [2mdist-react/[22m[2massets/[22m[32mgg sans Bold-BGlwbW8t.woff [39m[1m[2m 40.13 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439331Z [2mdist-react/[22m[2massets/[22m[32mgg sans Medium-BMWm4JFW.woff [39m[1m[2m 40.32 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439384Z [2mdist-react/[22m[2massets/[22m[32mgg sans Semibold-xAGa8zYH.woff [39m[1m[2m 40.57 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439435Z [2mdist-react/[22m[2massets/[22m[32memojies_colored-Cxo2u_zo.png [39m[1m[2m 45.89 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439487Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_viewer_join-BOPrADSV.mp3 [39m[1m[2m 67.54 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439542Z [2mdist-react/[22m[2massets/[22m[32mscreenshare_viewer_leave-BoDMhfvJ.mp3 [39m[1m[2m 67.54 kB[22m[1m[22m
|
||||
2026-02-13T19:45:49.2439594Z [2mdist-react/[22m[2massets/[22m[35mindex-DN2VsIhS.css [39m[1m[2m 72.50 kB[22m[1m[22m[2m │ gzip: 12.96 kB[22m
|
||||
2026-02-13T19:45:49.2439653Z [2mdist-react/[22m[2massets/[22m[36mindex-9riexBjR.js [39m[1m[33m8,425.21 kB[39m[22m[2m │ gzip: 1,537.92 kB[22m
|
||||
2026-02-13T19:45:49.2439708Z [33m
|
||||
2026-02-13T19:45:49.2439745Z (!) Some chunks are larger than 1000 kB after minification. Consider:
|
||||
2026-02-13T19:45:49.2439792Z - Using dynamic import() to code-split the application
|
||||
2026-02-13T19:45:49.2439860Z - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
||||
2026-02-13T19:45:49.2439925Z - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.[39m
|
||||
2026-02-13T19:45:49.2440027Z [32m✓ built in 3.79s[39m
|
||||
2026-02-13T19:45:49.6728276Z [34m•[39m electron-builder [34mversion[39m=25.1.8 [34mos[39m=6.12.54-Unraid
|
||||
2026-02-13T19:45:49.6730876Z [34m•[39m artifacts will be published if draft release exists [34mreason[39m=CI detected
|
||||
2026-02-13T19:45:49.6747756Z [34m•[39m loaded configuration [34mfile[39m=package.json ("build" field)
|
||||
2026-02-13T19:45:49.7516508Z [34m•[39m installing production dependencies [34mplatform[39m=linux [34march[39m=x64 [34mappDir[39m=/workspace/Moyettes/DiscordClone/apps/electron
|
||||
2026-02-13T19:45:50.3522962Z [34m•[39m executing @electron/rebuild [34melectronVersion[39m=33.4.11 [34march[39m=x64 [34mbuildFromSource[39m=false [34mappDir[39m=./
|
||||
2026-02-13T19:45:50.3527333Z [34m•[39m installing native dependencies [34march[39m=x64
|
||||
2026-02-13T19:45:50.3691072Z [31m⨯[39m node:internal/modules/cjs/loader:1228
|
||||
2026-02-13T19:45:50.3691374Z throw err;
|
||||
2026-02-13T19:45:50.3691456Z ^
|
||||
2026-02-13T19:45:50.3691567Z
|
||||
2026-02-13T19:45:50.3691656Z Error: Cannot find module '/workspace/Moyettes/DiscordClone/node_modules/app-builder-lib/out/util/rebuild/remote-rebuild.js'
|
||||
2026-02-13T19:45:50.3691762Z [90m at Function._resolveFilename (node:internal/modules/cjs/loader:1225:15)[39m
|
||||
2026-02-13T19:45:50.3691876Z [90m at Function._load (node:internal/modules/cjs/loader:1055:27)[39m
|
||||
2026-02-13T19:45:50.3691956Z [90m at TracingChannel.traceSync (node:diagnostics_channel:322:14)[39m
|
||||
2026-02-13T19:45:50.3692038Z [90m at wrapModuleLoad (node:internal/modules/cjs/loader:220:24)[39m
|
||||
2026-02-13T19:45:50.3692114Z [90m at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:170:5)[39m
|
||||
2026-02-13T19:45:50.3692210Z [90m at node:internal/main/run_main_module:36:49[39m {
|
||||
2026-02-13T19:45:50.3692298Z code: [32m'MODULE_NOT_FOUND'[39m,
|
||||
2026-02-13T19:45:50.3692367Z requireStack: []
|
||||
2026-02-13T19:45:50.3692434Z }
|
||||
2026-02-13T19:45:50.3692493Z
|
||||
2026-02-13T19:45:50.3692566Z Node.js v22.14.0
|
||||
2026-02-13T19:45:50.3692636Z
|
||||
2026-02-13T19:45:50.3716284Z [31m⨯[39m [31mRebuilder failed with exit code: 1[39m [31mfailedTask[39m=build [31mstackTrace[39m=Error: Rebuilder failed with exit code: 1
|
||||
2026-02-13T19:45:50.3716453Z at ChildProcess.<anonymous> (/workspace/Moyettes/DiscordClone/node_modules/app-builder-lib/out/util/rebuild/rebuild.js:54:40)
|
||||
2026-02-13T19:45:50.3716554Z at ChildProcess.emit (node:events:518:28)
|
||||
2026-02-13T19:45:50.3716632Z at Process.ChildProcess._handle.onexit (node:internal/child_process:293:12)
|
||||
2026-02-13T19:45:50.3919709Z ❌ Failure - Main Build Electron app
|
||||
2026-02-13T19:45:50.3947186Z exitcode '1': failure
|
||||
2026-02-13T19:45:50.4343747Z expression 'npm-electron-${{ hashFiles('package-lock.json') }}' rewritten to 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T19:45:50.4344005Z evaluating expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))'
|
||||
2026-02-13T19:45:50.4344271Z Writing entry to tarball workflow/hashfiles/index.js len:168437
|
||||
2026-02-13T19:45:50.4346221Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:45:50.4357201Z 🐳 docker exec cmd=[node /var/run/act/workflow/hashfiles/index.js] user= workdir=
|
||||
2026-02-13T19:45:50.4357547Z Exec command '[node /var/run/act/workflow/hashfiles/index.js]'
|
||||
2026-02-13T19:45:50.4357806Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:45:50.5032272Z expression 'format('npm-electron-{0}', hashFiles('package-lock.json'))' evaluated to '%!t(string=npm-electron-3bfeb88fe20d0eab8d1e9deb0c16af4cc320445b15ebe7f6fa7d2e30a7316852)'
|
||||
2026-02-13T19:45:50.5074002Z evaluating expression 'success()'
|
||||
2026-02-13T19:45:50.5074389Z expression 'success()' evaluated to 'false'
|
||||
2026-02-13T19:45:50.5074538Z Skipping step 'Cache npm and Electron' due to 'success()'
|
||||
2026-02-13T19:45:50.5182082Z evaluating expression 'always()'
|
||||
2026-02-13T19:45:50.5182485Z expression 'always()' evaluated to 'true'
|
||||
2026-02-13T19:45:50.5182588Z ⭐ Run Post Checkout repository
|
||||
2026-02-13T19:45:50.5182776Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-02-13T19:45:50.5182920Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-02-13T19:45:50.5183011Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-02-13T19:45:50.5183097Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-02-13T19:45:50.5183183Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-02-13T19:45:50.5183271Z Extracting content to '/var/run/act'
|
||||
2026-02-13T19:45:50.5193581Z run post step for 'Checkout repository'
|
||||
2026-02-13T19:45:50.5194070Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-02-13T19:45:50.5194221Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-02-13T19:45:50.5194328Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-02-13T19:45:50.5194571Z Working directory '/workspace/Moyettes/DiscordClone'
|
||||
2026-02-13T19:45:50.5980034Z [command]/usr/bin/git version
|
||||
2026-02-13T19:45:50.6004447Z git version 2.34.1
|
||||
2026-02-13T19:45:50.6023930Z ***
|
||||
2026-02-13T19:45:50.6030965Z Copying '/root/.gitconfig' to '/tmp/60a666f6-d59c-408a-a1cb-056ca0b4c502/.gitconfig'
|
||||
2026-02-13T19:45:50.6037156Z Temporarily overriding HOME='/tmp/60a666f6-d59c-408a-a1cb-056ca0b4c502' before making global git config changes
|
||||
2026-02-13T19:45:50.6038350Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-02-13T19:45:50.6042170Z [command]/usr/bin/git config --global --add safe.directory /workspace/Moyettes/DiscordClone
|
||||
2026-02-13T19:45:50.6065501Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-02-13T19:45:50.6093861Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-02-13T19:45:50.6267924Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader
|
||||
2026-02-13T19:45:50.6283475Z http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T19:45:50.6291176Z [command]/usr/bin/git config --local --unset-all http.http://192.168.125.15:4000/.extraheader
|
||||
2026-02-13T19:45:50.6310983Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/192\.168\.125\.15\:4000\/\.extraheader' && git config --local --unset-all 'http.http://192.168.125.15:4000/.extraheader' || :"
|
||||
2026-02-13T19:45:50.6473927Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-02-13T19:45:50.6494369Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-02-13T19:45:50.6719307Z ✅ Success - Post Checkout repository
|
||||
2026-02-13T19:45:50.6746343Z Cleaning up container for job build-and-release
|
||||
2026-02-13T19:46:19.4915273Z Removed container: 78088ba67b9e9a8de8380d20c89ea44da62bb5887fd0db970d18137ccf97f44f
|
||||
2026-02-13T19:46:19.4936249Z 🐳 docker volume rm GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release
|
||||
2026-02-13T19:46:19.9625914Z 🐳 docker volume rm GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-env
|
||||
2026-02-13T19:46:20.2859684Z Cleaning up network for job build-and-release, and network name is: GITEA-ACTIONS-TASK-46_WORKFLOW-Build-and-Release_JOB-build-and-release-build-and-release-network
|
||||
2026-02-13T19:46:21.1942946Z 🏁 Job failed
|
||||
2026-02-13T19:46:21.1982622Z Job 'build-and-release' failed
|
||||
@@ -174,9 +174,27 @@ async function decryptBatch(items) {
|
||||
}));
|
||||
}
|
||||
|
||||
// --- Ed25519 Support Detection ---
|
||||
|
||||
let ed25519Supported = null;
|
||||
async function checkEd25519Support() {
|
||||
if (ed25519Supported !== null) return ed25519Supported;
|
||||
try {
|
||||
await crypto.subtle.generateKey('Ed25519', false, ['sign', 'verify']);
|
||||
ed25519Supported = true;
|
||||
} catch {
|
||||
ed25519Supported = false;
|
||||
}
|
||||
return ed25519Supported;
|
||||
}
|
||||
|
||||
// --- Batch Verify ---
|
||||
|
||||
async function verifyBatch(items) {
|
||||
const supported = await checkEd25519Support();
|
||||
if (!supported) {
|
||||
return items.map(() => ({ success: true, verified: null }));
|
||||
}
|
||||
return Promise.all(items.map(async ({ publicKey, message, signature }) => {
|
||||
try {
|
||||
const verified = await verifySignature(publicKey, message, signature);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@discord-clone/shared",
|
||||
"private": true,
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.16",
|
||||
"type": "module",
|
||||
"main": "src/App.jsx",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
|
||||
import Login from './pages/Login';
|
||||
import Register from './pages/Register';
|
||||
@@ -62,21 +62,18 @@ function AuthGuard({ children }) {
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
// Redirect once after auth state is determined (not on every route change)
|
||||
const hasRedirected = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (authState === 'loading' || hasRedirected.current) return;
|
||||
hasRedirected.current = true;
|
||||
if (authState === 'loading') return;
|
||||
|
||||
const isAuthPage = location.pathname === '/' || location.pathname === '/register';
|
||||
const hasSession = sessionStorage.getItem('privateKey') && sessionStorage.getItem('signingKey');
|
||||
|
||||
if (authState === 'authenticated' && isAuthPage) {
|
||||
if (hasSession && isAuthPage) {
|
||||
navigate('/chat', { replace: true });
|
||||
} else if (authState === 'unauthenticated' && !isAuthPage) {
|
||||
} else if (!hasSession && !isAuthPage) {
|
||||
navigate('/', { replace: true });
|
||||
}
|
||||
}, [authState]);
|
||||
}, [authState, location.pathname]);
|
||||
|
||||
if (authState === 'loading') {
|
||||
return (
|
||||
|
||||
@@ -24,7 +24,9 @@ import UserProfilePopup from './UserProfilePopup';
|
||||
import Avatar from './Avatar';
|
||||
import MentionMenu from './MentionMenu';
|
||||
import MessageItem, { getUserColor } from './MessageItem';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
import { usePlatform } from '../platform';
|
||||
import { useVoice } from '../contexts/VoiceContext';
|
||||
|
||||
const metadataCache = new Map();
|
||||
const attachmentCache = new Map();
|
||||
@@ -59,8 +61,8 @@ export function clearMessageDecryptionCache() {
|
||||
messageDecryptionCache.clear();
|
||||
}
|
||||
|
||||
const ICON_COLOR_DEFAULT = 'color-mix(in oklab, hsl(240 4.294% 68.039% / 1) 100%, #000 0%)';
|
||||
const ICON_COLOR_DANGER = 'color-mix(in oklab, hsl(1.353 calc(1*82.609%) 68.431% /1) 100%, #000 0%)';
|
||||
const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)';
|
||||
const ICON_COLOR_DANGER = 'hsl(1.353, 82.609%, 68.431%)';
|
||||
|
||||
const fromHexString = (hexString) =>
|
||||
new Uint8Array(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
||||
@@ -463,14 +465,33 @@ const MessageContextMenu = ({ x, y, onInteract, onClose, isOwner, canDelete }) =
|
||||
);
|
||||
};
|
||||
|
||||
const ColoredIcon = ({ src, color, size = '24px', style = {} }) => (
|
||||
<div style={{ width: size, height: size, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', ...style }}>
|
||||
<img src={src} alt="" style={color ? { width: size, height: size, transform: 'translateX(-1000px)', filter: `drop-shadow(1000px 0 0 ${color})` } : { width: size, height: size, objectFit: 'contain' }} />
|
||||
</div>
|
||||
);
|
||||
const InputContextMenu = ({ x, y, onClose, onPaste }) => {
|
||||
const menuRef = useRef(null);
|
||||
const [pos, setPos] = useState({ top: y, left: x });
|
||||
useEffect(() => { const h = () => onClose(); window.addEventListener('click', h); return () => window.removeEventListener('click', h); }, [onClose]);
|
||||
React.useLayoutEffect(() => {
|
||||
if (!menuRef.current) return;
|
||||
const rect = menuRef.current.getBoundingClientRect();
|
||||
let newTop = y, newLeft = x;
|
||||
if (x + rect.width > window.innerWidth) newLeft = x - rect.width;
|
||||
if (y + rect.height > window.innerHeight) newTop = y - rect.height;
|
||||
if (newLeft < 0) newLeft = 10;
|
||||
if (newTop < 0) newTop = 10;
|
||||
setPos({ top: newTop, left: newLeft });
|
||||
}, [x, y]);
|
||||
|
||||
return (
|
||||
<div ref={menuRef} className="context-menu" style={{ top: pos.top, left: pos.left }} onClick={(e) => e.stopPropagation()}>
|
||||
<div className="context-menu-item" onClick={(e) => { e.stopPropagation(); onPaste(); onClose(); }}>
|
||||
<span>Paste</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ChatArea = ({ channelId, channelName, channelType, username, channelKey, userId: currentUserId, showMembers, onToggleMembers, onOpenDM, showPinned, onTogglePinned }) => {
|
||||
const { crypto } = usePlatform();
|
||||
const { isReceivingScreenShareAudio } = useVoice();
|
||||
const [decryptedMessages, setDecryptedMessages] = useState([]);
|
||||
const [input, setInput] = useState('');
|
||||
const [zoomedImage, setZoomedImage] = useState(null);
|
||||
@@ -481,6 +502,7 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
const [isMultiline, setIsMultiline] = useState(false);
|
||||
const [hoveredMessageId, setHoveredMessageId] = useState(null);
|
||||
const [contextMenu, setContextMenu] = useState(null);
|
||||
const [inputContextMenu, setInputContextMenu] = useState(null);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [replyingTo, setReplyingTo] = useState(null);
|
||||
const [editingMessage, setEditingMessage] = useState(null);
|
||||
@@ -563,7 +585,7 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
return {
|
||||
...msg,
|
||||
content: cached?.content ?? '[Decrypting...]',
|
||||
isVerified: cached?.isVerified ?? false,
|
||||
isVerified: cached?.isVerified ?? null,
|
||||
decryptedReply: cached?.decryptedReply ?? null,
|
||||
};
|
||||
});
|
||||
@@ -667,14 +689,15 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
const verifyMap = new Map();
|
||||
for (let i = 0; i < verifyResults.length; i++) {
|
||||
const msg = verifyMsgMap[i];
|
||||
verifyMap.set(msg.id, verifyResults[i].success && verifyResults[i].verified);
|
||||
const verified = verifyResults[i].verified;
|
||||
verifyMap.set(msg.id, verified === null ? null : (verifyResults[i].success && verified));
|
||||
}
|
||||
|
||||
// Populate cache
|
||||
for (const msg of needsDecryption) {
|
||||
const content = decryptedMap.get(msg.id) ??
|
||||
(msg.ciphertext && msg.ciphertext.length < TAG_LENGTH ? '[Invalid Encrypted Message]' : '[Encrypted Message - Key Missing]');
|
||||
const isVerified = verifyMap.get(msg.id) ?? false;
|
||||
const isVerified = verifyMap.has(msg.id) ? verifyMap.get(msg.id) : null;
|
||||
const decryptedReply = replyMap.get(msg.id) ?? null;
|
||||
messageDecryptionCache.set(msg.id, { content, isVerified, decryptedReply });
|
||||
}
|
||||
@@ -715,13 +738,14 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
}, [username, myRoleNames]);
|
||||
|
||||
const playPingSound = useCallback(() => {
|
||||
if (isReceivingScreenShareAudio) return;
|
||||
const now = Date.now();
|
||||
if (now - lastPingTimeRef.current < 1000) return;
|
||||
lastPingTimeRef.current = now;
|
||||
const audio = new Audio(PingSound);
|
||||
audio.volume = 0.5;
|
||||
audio.play().catch(() => {});
|
||||
}, []);
|
||||
}, [isReceivingScreenShareAudio]);
|
||||
|
||||
// Play ping sound when a new message mentions us (by username or role)
|
||||
useEffect(() => {
|
||||
@@ -1341,6 +1365,38 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
</div>
|
||||
</div>
|
||||
{contextMenu && <MessageContextMenu x={contextMenu.x} y={contextMenu.y} isOwner={contextMenu.isOwner} canDelete={contextMenu.canDelete} onClose={() => setContextMenu(null)} onInteract={(action) => handleContextInteract(action, contextMenu.messageId)} />}
|
||||
{inputContextMenu && <InputContextMenu x={inputContextMenu.x} y={inputContextMenu.y} onClose={() => setInputContextMenu(null)} onPaste={async () => {
|
||||
try {
|
||||
if (inputDivRef.current) inputDivRef.current.focus();
|
||||
// Try reading clipboard items for images first
|
||||
if (navigator.clipboard.read) {
|
||||
try {
|
||||
const items = await navigator.clipboard.read();
|
||||
for (const item of items) {
|
||||
const imageType = item.types.find(t => t.startsWith('image/'));
|
||||
if (imageType) {
|
||||
const blob = await item.getType(imageType);
|
||||
const file = new File([blob], `pasted-image.${imageType.split('/')[1] || 'png'}`, { type: imageType });
|
||||
processFile(file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
// Fall back to plain text
|
||||
const text = await navigator.clipboard.readText();
|
||||
if (text) {
|
||||
document.execCommand('insertText', false, text);
|
||||
// Sync state — onInput may not fire from async execCommand
|
||||
const el = inputDivRef.current;
|
||||
if (el) {
|
||||
setInput(el.textContent);
|
||||
const inner = el.innerText;
|
||||
setIsMultiline(inner.includes('\n') || el.scrollHeight > 50);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}} />}
|
||||
|
||||
<form className="chat-input-form" onSubmit={handleSend} style={{ position: 'relative' }}>
|
||||
{mentionQuery !== null && mentionItems.length > 0 && (
|
||||
@@ -1382,6 +1438,10 @@ const ChatArea = ({ channelId, channelName, channelType, username, channelKey, u
|
||||
onBlur={saveSelection}
|
||||
onMouseUp={saveSelection}
|
||||
onKeyUp={saveSelection}
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
setInputContextMenu({ x: e.clientX, y: e.clientY });
|
||||
}}
|
||||
onPaste={(e) => {
|
||||
const items = e.clipboardData?.items;
|
||||
if (items) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import Tooltip from './Tooltip';
|
||||
|
||||
const ChatHeader = ({ channelName, channelType, channelTopic, onToggleMembers, showMembers, onTogglePinned, serverName }) => {
|
||||
const ChatHeader = ({ channelName, channelType, channelTopic, onToggleMembers, showMembers, onTogglePinned, serverName, isMobile, onMobileBack }) => {
|
||||
const [searchFocused, setSearchFocused] = useState(false);
|
||||
|
||||
const isDM = channelType === 'dm';
|
||||
@@ -10,9 +10,14 @@ const ChatHeader = ({ channelName, channelType, channelTopic, onToggleMembers, s
|
||||
return (
|
||||
<div className="chat-header">
|
||||
<div className="chat-header-left">
|
||||
{isMobile && onMobileBack && (
|
||||
<button className="mobile-back-btn" onClick={onMobileBack}>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
)}
|
||||
<span className="chat-header-icon">{isDM ? '@' : '#'}</span>
|
||||
<span className="chat-header-name">{channelName}</span>
|
||||
{channelTopic && !isDM && (
|
||||
{channelTopic && !isDM && !isMobile && (
|
||||
<>
|
||||
<div className="chat-header-divider" />
|
||||
<span className="chat-header-topic" title={channelTopic}>{channelTopic}</span>
|
||||
@@ -21,7 +26,7 @@ const ChatHeader = ({ channelName, channelType, channelTopic, onToggleMembers, s
|
||||
{isDM && <span className="chat-header-status-text"></span>}
|
||||
</div>
|
||||
<div className="chat-header-right">
|
||||
{!isDM && (
|
||||
{!isDM && !isMobile && (
|
||||
<Tooltip text="Threads" position="bottom">
|
||||
<button className="chat-header-btn">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
@@ -37,7 +42,7 @@ const ChatHeader = ({ channelName, channelType, channelTopic, onToggleMembers, s
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{!isDM && (
|
||||
{!isDM && !isMobile && (
|
||||
<Tooltip text={showMembers ? "Hide Members" : "Show Members"} position="bottom">
|
||||
<button
|
||||
className={`chat-header-btn ${showMembers ? 'active' : ''}`}
|
||||
@@ -50,22 +55,26 @@ const ChatHeader = ({ channelName, channelType, channelTopic, onToggleMembers, s
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip text="Notification Settings" position="bottom">
|
||||
<button className="chat-header-btn">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18 9V14C18 15.657 19.344 17 21 17V18H3V17C4.656 17 6 15.657 6 14V9C6 5.686 8.686 3 12 3C15.314 3 18 5.686 18 9ZM11.9999 22C10.5239 22 9.24993 20.955 8.99993 19.5H14.9999C14.7499 20.955 13.4759 22 11.9999 22Z" />
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div className="chat-header-search-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={searchPlaceholder}
|
||||
className={`chat-header-search ${searchFocused ? 'focused' : ''}`}
|
||||
onFocus={() => setSearchFocused(true)}
|
||||
onBlur={() => setSearchFocused(false)}
|
||||
/>
|
||||
</div>
|
||||
{!isMobile && (
|
||||
<Tooltip text="Notification Settings" position="bottom">
|
||||
<button className="chat-header-btn">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18 9V14C18 15.657 19.344 17 21 17V18H3V17C4.656 17 6 15.657 6 14V9C6 5.686 8.686 3 12 3C15.314 3 18 5.686 18 9ZM11.9999 22C10.5239 22 9.24993 20.955 8.99993 19.5H14.9999C14.7499 20.955 13.4759 22 11.9999 22Z" />
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isMobile && (
|
||||
<div className="chat-header-search-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={searchPlaceholder}
|
||||
className={`chat-header-search ${searchFocused ? 'focused' : ''}`}
|
||||
onFocus={() => setSearchFocused(true)}
|
||||
onBlur={() => setSearchFocused(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
31
packages/shared/src/components/ColoredIcon.jsx
Normal file
31
packages/shared/src/components/ColoredIcon.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
const ColoredIcon = React.memo(({ src, color, size = '20px', style = {} }) => {
|
||||
if (!color) {
|
||||
return (
|
||||
<div style={{ width: size, height: size, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, ...style }}>
|
||||
<img src={src} alt="" style={{ width: size, height: size, objectFit: 'contain' }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div style={{
|
||||
width: size, height: size, flexShrink: 0,
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}>
|
||||
<img
|
||||
src={src}
|
||||
alt=""
|
||||
style={{
|
||||
width: size, height: size,
|
||||
objectFit: 'contain',
|
||||
filter: `drop-shadow(${size} 0 0 ${color})`,
|
||||
transform: `translateX(-${size})`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default ColoredIcon;
|
||||
@@ -3,6 +3,7 @@ import { useConvex } from 'convex/react';
|
||||
import { api } from '../../../../convex/_generated/api';
|
||||
import Tooltip from './Tooltip';
|
||||
import Avatar from './Avatar';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
import { useOnlineUsers } from '../contexts/PresenceContext';
|
||||
import friendsIcon from '../assets/icons/friends.svg';
|
||||
|
||||
@@ -181,26 +182,7 @@ const DMList = ({ dmChannels, activeDMChannel, onSelectDM, onOpenDM }) => {
|
||||
onClick={() => onSelectDM('friends')}
|
||||
>
|
||||
<div style={{ marginRight: '12px' }}>
|
||||
<div style={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<img
|
||||
src={friendsIcon}
|
||||
alt=""
|
||||
style={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
transform: 'translateX(-1000px)',
|
||||
filter: `drop-shadow(1000px 0 0 var(--interactive-normal))`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ColoredIcon src={friendsIcon} color="var(--interactive-normal)" size="24px" />
|
||||
</div>
|
||||
<span style={{ fontWeight: 500 }}>Friends</span>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
||||
import { useQuery } from 'convex/react';
|
||||
import { api } from '../../../../convex/_generated/api';
|
||||
import Avatar from './Avatar';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
import { useOnlineUsers } from '../contexts/PresenceContext';
|
||||
import friendsIcon from '../assets/icons/friends.svg';
|
||||
|
||||
@@ -54,26 +55,7 @@ const FriendsView = ({ onOpenDM }) => {
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', marginRight: '16px', paddingRight: '16px', borderRight: '1px solid var(--border-subtle)' }}>
|
||||
<div style={{ marginRight: '12px' }}>
|
||||
<div style={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<img
|
||||
src={friendsIcon}
|
||||
alt=""
|
||||
style={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
transform: 'translateX(-1000px)',
|
||||
filter: `drop-shadow(1000px 0 0 var(--interactive-normal))`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ColoredIcon src={friendsIcon} color="var(--interactive-normal)" size="24px" />
|
||||
</div>
|
||||
Friends
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { api } from '../../../../convex/_generated/api';
|
||||
import { useOnlineUsers } from '../contexts/PresenceContext';
|
||||
import { useVoice } from '../contexts/VoiceContext';
|
||||
import { CrownIcon, SharingIcon } from '../assets/icons';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
|
||||
const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245'];
|
||||
|
||||
@@ -15,12 +16,6 @@ function getUserColor(name) {
|
||||
return USER_COLORS[Math.abs(hash) % USER_COLORS.length];
|
||||
}
|
||||
|
||||
const ColoredIcon = ({ src, color, size = '24px', style = {} }) => (
|
||||
<div style={{ width: size, height: size, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', ...style }}>
|
||||
<img src={src} alt="" style={color ? { width: size, height: size, transform: 'translateX(-1000px)', filter: `drop-shadow(1000px 0 0 ${color})` } : { width: size, height: size, objectFit: 'contain' }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const STATUS_COLORS = {
|
||||
online: '#3ba55c',
|
||||
idle: '#faa61a',
|
||||
|
||||
@@ -14,13 +14,14 @@ import {
|
||||
import { getEmojiUrl, AllEmojis } from '../assets/emojis';
|
||||
import Tooltip from './Tooltip';
|
||||
import Avatar from './Avatar';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
import { usePlatform } from '../platform';
|
||||
|
||||
const fireIcon = getEmojiUrl('nature', 'fire');
|
||||
const heartIcon = getEmojiUrl('symbols', 'heart');
|
||||
const thumbsupIcon = getEmojiUrl('people', 'thumbsup');
|
||||
|
||||
const ICON_COLOR_DEFAULT = 'color-mix(in oklab, hsl(240 4.294% 68.039% / 1) 100%, #000 0%)';
|
||||
const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)';
|
||||
const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245'];
|
||||
|
||||
export const getUserColor = (name) => {
|
||||
@@ -99,12 +100,6 @@ const getReactionIcon = (name) => {
|
||||
}
|
||||
};
|
||||
|
||||
const ColoredIcon = ({ src, color, size = '24px', style = {} }) => (
|
||||
<div style={{ width: size, height: size, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', ...style }}>
|
||||
<img src={src} alt="" style={color ? { width: size, height: size, transform: 'translateX(-1000px)', filter: `drop-shadow(1000px 0 0 ${color})` } : { width: size, height: size, objectFit: 'contain' }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const isNewDay = (current, previous) => {
|
||||
if (!previous) return true;
|
||||
return current.getDate() !== previous.getDate()
|
||||
@@ -123,7 +118,7 @@ const createMarkdownComponents = (openExternal) => ({
|
||||
return <span>{props.children}</span>;
|
||||
}
|
||||
}
|
||||
if (props.href && props.href.startsWith('mention://')) return <span style={{ background: 'color-mix(in oklab,hsl(234.935 calc(1*85.556%) 64.706% /0.23921568627450981) 100%,hsl(0 0% 0% /0.23921568627450981) 0%)', borderRadius: '3px', color: 'color-mix(in oklab, hsl(228.14 calc(1*100%) 83.137% /1) 100%, #000 0%)', fontWeight: 500, padding: '0 2px', cursor: 'default' }} title={props.children}>{props.children}</span>;
|
||||
if (props.href && props.href.startsWith('mention://')) return <span style={{ background: 'hsla(234.935, 85.556%, 64.706%, 0.239)', borderRadius: '3px', color: 'hsl(228.14, 100%, 83.137%)', fontWeight: 500, padding: '0 2px', cursor: 'default' }} title={props.children}>{props.children}</span>;
|
||||
return <a {...props} onClick={(e) => { e.preventDefault(); openExternal(props.href); }} style={{ color: '#00b0f4', cursor: 'pointer', textDecoration: 'none' }} onMouseOver={(e) => e.target.style.textDecoration = 'underline'} onMouseOut={(e) => e.target.style.textDecoration = 'none'} />;
|
||||
},
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
@@ -163,7 +158,7 @@ const MessageToolbar = ({ onAddReaction, onEdit, onReply, onMore, isOwner }) =>
|
||||
<Tooltip text="Fire" position="top">
|
||||
<IconButton onClick={() => onAddReaction('fire')} emoji={<ColoredIcon src={fireIcon} size="20px" />} />
|
||||
</Tooltip>
|
||||
<div style={{ width: '1px', height: '24px', margin: '2px 4px', backgroundColor: 'color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.12156862745098039) 100%,hsl(0 0% 0% /0.12156862745098039) 0%)' }}></div>
|
||||
<div style={{ width: '1px', height: '24px', margin: '2px 4px', backgroundColor: 'hsla(240, 4%, 60.784%, 0.122)' }}></div>
|
||||
<Tooltip text="Add Reaction" position="top">
|
||||
<IconButton onClick={() => onAddReaction(null)} emoji={<ColoredIcon src={EmojieIcon} color={ICON_COLOR_DEFAULT} size="20px" />} />
|
||||
</Tooltip>
|
||||
@@ -262,7 +257,7 @@ const MessageItem = React.memo(({
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: '4px', marginTop: '4px', flexWrap: 'wrap' }}>
|
||||
{Object.entries(msg.reactions).map(([emojiName, data]) => (
|
||||
<div key={emojiName} onClick={() => onReactionClick(msg.id, emojiName, data.me)} style={{ display: 'flex', alignItems: 'center', backgroundColor: data.me ? 'rgba(88, 101, 242, 0.15)' : 'hsl(240 calc(1*4%) 60.784% / 0.0784313725490196)', border: data.me ? '1px solid var(--brand-experiment)' : '1px solid transparent', borderRadius: '8px', padding: '2px 6px', cursor: 'pointer', gap: '4px' }}>
|
||||
<div key={emojiName} onClick={() => onReactionClick(msg.id, emojiName, data.me)} style={{ display: 'flex', alignItems: 'center', backgroundColor: data.me ? 'rgba(88, 101, 242, 0.15)' : 'hsla(240, 4%, 60.784%, 0.078)', border: data.me ? '1px solid var(--brand-experiment)' : '1px solid transparent', borderRadius: '8px', padding: '2px 6px', cursor: 'pointer', gap: '4px' }}>
|
||||
<ColoredIcon src={getReactionIcon(emojiName)} size="16px" color={null} />
|
||||
<span style={{ fontSize: '12px', color: data.me ? '#dee0fc' : 'var(--header-secondary)', fontWeight: 600 }}>{data.count}</span>
|
||||
</div>
|
||||
@@ -287,12 +282,12 @@ const MessageItem = React.memo(({
|
||||
<div
|
||||
id={`msg-${msg.id}`}
|
||||
className={`message-item${isGrouped ? ' message-grouped' : ''}`}
|
||||
style={isMentioned ? { background: 'color-mix(in oklab,hsl(36.894 calc(1*100%) 31.569% /0.0784313725490196) 100%,hsl(0 0% 0% /0.0784313725490196) 0%)', position: 'relative' } : { position: 'relative' }}
|
||||
style={isMentioned ? { background: 'hsla(36.894, 100%, 31.569%, 0.078)', position: 'relative' } : { position: 'relative' }}
|
||||
onMouseEnter={onHover}
|
||||
onMouseLeave={onLeave}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
{isMentioned && <div style={{ background: 'color-mix(in oklab, hsl(34 calc(1*50.847%) 53.725% /1) 100%, #000 0%)', bottom: 0, display: 'block', left: 0, pointerEvents: 'none', position: 'absolute', top: 0, width: '2px' }} />}
|
||||
{isMentioned && <div style={{ background: 'hsl(34, 50.847%, 53.725%)', bottom: 0, display: 'block', left: 0, pointerEvents: 'none', position: 'absolute', top: 0, width: '2px' }} />}
|
||||
|
||||
{msg.replyToId && msg.replyToUsername && (
|
||||
<div className="message-reply-context" onClick={() => onScrollToMessage(msg.replyToId)}>
|
||||
@@ -330,7 +325,7 @@ const MessageItem = React.memo(({
|
||||
>
|
||||
{msg.username || 'Unknown'}
|
||||
</span>
|
||||
{!msg.isVerified && <span className="verification-failed" title="Signature Verification Failed!"><svg aria-hidden="true" role="img" width="16" height="16" viewBox="0 0 24 24" fill="var(--danger)"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg></span>}
|
||||
{msg.isVerified === false && <span className="verification-failed" title="Signature Verification Failed!"><svg aria-hidden="true" role="img" width="16" height="16" viewBox="0 0 24 24" fill="var(--danger)"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg></span>}
|
||||
<span className="timestamp">{currentDate.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useRef, useEffect, useLayoutEffect } from 'react';
|
||||
import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useConvex, useMutation, useQuery } from 'convex/react';
|
||||
import { api } from '../../../../convex/_generated/api';
|
||||
@@ -32,12 +32,13 @@ import screenShareStartSound from '../assets/sounds/screenshare_start.mp3';
|
||||
import screenShareStopSound from '../assets/sounds/screenshare_stop.mp3';
|
||||
import { getUserPref, setUserPref } from '../utils/userPreferences';
|
||||
import { usePlatform } from '../platform';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
|
||||
const USER_COLORS = ['#5865F2', '#EBA7CD', '#57F287', '#FEE75C', '#EB459E', '#ED4245'];
|
||||
|
||||
const ICON_COLOR_DEFAULT = 'color-mix(in oklab, hsl(240 4.294% 68.039% / 1) 100%, #000 0%)';
|
||||
const ICON_COLOR_ACTIVE = 'hsl(357.692 67.826% 54.902% / 1)';
|
||||
const SERVER_MUTE_RED = 'color-mix(in oklab, hsl(1.343 calc(1*84.81%) 69.02% /1) 100%, #000 0%)';
|
||||
const ICON_COLOR_DEFAULT = 'hsl(240, 4.294%, 68.039%)';
|
||||
const ICON_COLOR_ACTIVE = 'hsl(357.692, 67.826%, 54.902%)';
|
||||
const SERVER_MUTE_RED = 'hsl(1.343, 84.81%, 69.02%)';
|
||||
|
||||
const controlButtonStyle = {
|
||||
background: 'transparent',
|
||||
@@ -68,29 +69,6 @@ function randomHex(length) {
|
||||
return bytesToHex(bytes);
|
||||
}
|
||||
|
||||
const ColoredIcon = ({ src, color, size = '20px' }) => (
|
||||
<div style={{
|
||||
width: size,
|
||||
height: size,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<img
|
||||
src={src}
|
||||
alt=""
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
transform: 'translateX(-1000px)',
|
||||
filter: `drop-shadow(1000px 0 0 ${color})`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const VoiceTimer = () => {
|
||||
const [elapsed, setElapsed] = React.useState(0);
|
||||
React.useEffect(() => {
|
||||
@@ -114,7 +92,7 @@ const STATUS_OPTIONS = [
|
||||
{ value: 'invisible', label: 'Invisible', color: '#747f8d' },
|
||||
];
|
||||
|
||||
const UserControlPanel = ({ username, userId }) => {
|
||||
const UserControlPanel = React.memo(({ username, userId }) => {
|
||||
const { session, idle } = usePlatform();
|
||||
const { isMuted, isDeafened, toggleMute, toggleDeafen, connectionState, disconnectVoice } = useVoice();
|
||||
const [showStatusMenu, setShowStatusMenu] = useState(false);
|
||||
@@ -124,23 +102,32 @@ const UserControlPanel = ({ username, userId }) => {
|
||||
const navigate = useNavigate();
|
||||
const manualStatusRef = useRef(false);
|
||||
const preIdleStatusRef = useRef('online');
|
||||
const hasInitializedRef = useRef(false);
|
||||
const currentStatusRef = useRef(currentStatus);
|
||||
currentStatusRef.current = currentStatus;
|
||||
|
||||
// Fetch stored status preference from server and sync local state
|
||||
const allUsers = useQuery(api.auth.getPublicKeys) || [];
|
||||
const myUser = allUsers.find(u => u.id === userId);
|
||||
React.useEffect(() => {
|
||||
if (myUser) {
|
||||
if (myUser.status && myUser.status !== 'offline') {
|
||||
setCurrentStatus(myUser.status);
|
||||
// dnd/invisible are manual overrides; idle is auto-set so don't count it
|
||||
manualStatusRef.current = (myUser.status === 'dnd' || myUser.status === 'invisible');
|
||||
} else if (!myUser.status || myUser.status === 'offline') {
|
||||
// First login or no preference set yet — default to "online"
|
||||
const isInitial = !hasInitializedRef.current;
|
||||
if (isInitial) hasInitializedRef.current = true;
|
||||
|
||||
// 'idle' is auto-set by the idle detector, not a user preference —
|
||||
// on a fresh app launch, reset it to 'online' just like 'offline'
|
||||
const shouldReset = !myUser.status || myUser.status === 'offline'
|
||||
|| (isInitial && myUser.status === 'idle');
|
||||
|
||||
if (shouldReset) {
|
||||
setCurrentStatus('online');
|
||||
manualStatusRef.current = false;
|
||||
if (userId) {
|
||||
updateStatusMutation({ userId, status: 'online' }).catch(() => {});
|
||||
}
|
||||
} else if (myUser.status) {
|
||||
setCurrentStatus(myUser.status);
|
||||
manualStatusRef.current = (myUser.status === 'dnd' || myUser.status === 'invisible');
|
||||
}
|
||||
}
|
||||
}, [myUser?.status]);
|
||||
@@ -188,13 +175,13 @@ const UserControlPanel = ({ username, userId }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Auto-idle detection via Electron powerMonitor
|
||||
// Auto-idle detection via platform idle API
|
||||
useEffect(() => {
|
||||
if (!idle || !userId) return;
|
||||
const handleIdleChange = (data) => {
|
||||
if (manualStatusRef.current) return;
|
||||
if (data.isIdle) {
|
||||
preIdleStatusRef.current = currentStatus;
|
||||
preIdleStatusRef.current = currentStatusRef.current;
|
||||
setCurrentStatus('idle');
|
||||
updateStatusMutation({ userId, status: 'idle' }).catch(() => {});
|
||||
} else {
|
||||
@@ -294,7 +281,7 @@ const UserControlPanel = ({ username, userId }) => {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -311,9 +298,9 @@ const voicePanelButtonStyle = {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
minHeight: '32px',
|
||||
background: 'hsl(240 calc(1*4%) 60.784% /0.0784313725490196)',
|
||||
border: 'hsl(0 calc(1*0%) 100% /0.0784313725490196)',
|
||||
borderColor: 'color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.0392156862745098) 100%,hsl(0 0% 0% /0.0392156862745098) 0%)',
|
||||
background: 'hsla(240, 4%, 60.784%, 0.078)',
|
||||
border: 'hsla(0, 0%, 100%, 0.078)',
|
||||
borderColor: 'hsla(240, 4%, 60.784%, 0.039)',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
padding: '4px',
|
||||
@@ -332,7 +319,7 @@ const liveBadgeStyle = {
|
||||
height: '16px',
|
||||
minHeight: '16px',
|
||||
minWidth: '16px',
|
||||
color: 'hsl(0 calc(1*0%) 100% /1)',
|
||||
color: 'hsl(0, 0%, 100%)',
|
||||
fontSize: '12px',
|
||||
fontWeight: '700',
|
||||
letterSpacing: '.02em',
|
||||
@@ -343,8 +330,8 @@ const liveBadgeStyle = {
|
||||
marginRight: '4px'
|
||||
};
|
||||
|
||||
const ACTIVE_SPEAKER_SHADOW = '0 0 0 0px hsl(134.526 calc(1*41.485%) 44.902% /1), inset 0 0 0 2px hsl(134.526 calc(1*41.485%) 44.902% /1), inset 0 0 0 3px color-mix(in oklab, hsl(240 calc(1*7.143%) 10.98% /1) 100%, #000 0%)';
|
||||
const VOICE_ACTIVE_COLOR = "color-mix(in oklab, hsl(132.809 calc(1*34.902%) 50% /1) 100%, #000 0%)";
|
||||
const ACTIVE_SPEAKER_SHADOW = '0 0 0 0px hsl(134.526, 41.485%, 44.902%), inset 0 0 0 2px hsl(134.526, 41.485%, 44.902%), inset 0 0 0 3px hsl(240, 7.143%, 10.98%)';
|
||||
const VOICE_ACTIVE_COLOR = 'hsl(132.809, 34.902%, 50%)';
|
||||
|
||||
async function encryptKeyForUsers(convex, channelId, keyHex, crypto) {
|
||||
const users = await convex.query(api.auth.getPublicKeys, {});
|
||||
@@ -389,7 +376,7 @@ function getScreenCaptureConstraints(selection) {
|
||||
};
|
||||
}
|
||||
|
||||
const VoiceUserContextMenu = ({ x, y, onClose, user, onMute, isMuted, onServerMute, isServerMuted, hasPermission, onMessage, isSelf, userVolume, onVolumeChange }) => {
|
||||
const VoiceUserContextMenu = ({ x, y, onClose, user, onMute, isMuted, onServerMute, isServerMuted, hasPermission, onDisconnect, hasDisconnectPermission, onMessage, isSelf, userVolume, onVolumeChange }) => {
|
||||
const menuRef = useRef(null);
|
||||
const [pos, setPos] = useState({ top: y, left: x });
|
||||
|
||||
@@ -428,7 +415,7 @@ const VoiceUserContextMenu = ({ x, y, onClose, user, onMute, isMuted, onServerMu
|
||||
value={userVolume}
|
||||
onChange={(e) => onVolumeChange(Number(e.target.value))}
|
||||
className="context-menu-volume-slider"
|
||||
style={{ background: `linear-gradient(to right, hsl(235 86% 65%) ${sliderPercent}%, var(--bg-tertiary) ${sliderPercent}%)` }}
|
||||
style={{ background: `linear-gradient(to right, hsl(235, 86%, 65%) ${sliderPercent}%, var(--bg-tertiary) ${sliderPercent}%)` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="context-menu-separator" />
|
||||
@@ -479,6 +466,14 @@ const VoiceUserContextMenu = ({ x, y, onClose, user, onMute, isMuted, onServerMu
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isSelf && hasDisconnectPermission && (
|
||||
<div
|
||||
className="context-menu-item"
|
||||
onClick={(e) => { e.stopPropagation(); onDisconnect(); onClose(); }}
|
||||
>
|
||||
<span style={{ color: SERVER_MUTE_RED }}>Disconnect</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="context-menu-separator" />
|
||||
<div className="context-menu-item" onClick={(e) => { e.stopPropagation(); onMessage(); onClose(); }}>
|
||||
<span>Message</span>
|
||||
@@ -754,7 +749,7 @@ const DraggableVoiceUser = ({ userId, channelId, disabled, children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, username, channelKeys, view, onViewChange, onOpenDM, activeDMChannel, setActiveDMChannel, dmChannels, userId, serverName = 'Secure Chat', serverIconUrl }) => {
|
||||
const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, username, channelKeys, view, onViewChange, onOpenDM, activeDMChannel, setActiveDMChannel, dmChannels, userId, serverName = 'Secure Chat', serverIconUrl, isMobile }) => {
|
||||
const { crypto, settings } = usePlatform();
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [isServerSettingsOpen, setIsServerSettingsOpen] = useState(false);
|
||||
@@ -829,6 +824,8 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
[dmChannels, unreadChannels, view, activeDMChannel]
|
||||
);
|
||||
|
||||
const { connectToVoice, activeChannelId: voiceChannelId, connectionState, disconnectVoice, activeChannelName: voiceChannelName, voiceStates, room, activeSpeakers, setScreenSharing, isPersonallyMuted, togglePersonalMute, isMuted: selfMuted, toggleMute, serverMute, disconnectUser, isServerMuted, serverSettings, getUserVolume, setUserVolume, isReceivingScreenShareAudio } = useVoice();
|
||||
|
||||
const prevUnreadDMsRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -843,15 +840,17 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
|
||||
for (const id of currentIds) {
|
||||
if (!prevUnreadDMsRef.current.has(id)) {
|
||||
const audio = new Audio(PingSound);
|
||||
audio.volume = 0.5;
|
||||
audio.play().catch(() => {});
|
||||
if (!isReceivingScreenShareAudio) {
|
||||
const audio = new Audio(PingSound);
|
||||
audio.volume = 0.5;
|
||||
audio.play().catch(() => {});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
prevUnreadDMsRef.current = currentIds;
|
||||
}, [dmChannels, unreadChannels]);
|
||||
}, [dmChannels, unreadChannels, isReceivingScreenShareAudio]);
|
||||
|
||||
const onRenameChannel = () => {};
|
||||
|
||||
@@ -859,8 +858,6 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
if (activeChannel === id) onSelectChannel(null);
|
||||
};
|
||||
|
||||
const { connectToVoice, activeChannelId: voiceChannelId, connectionState, disconnectVoice, activeChannelName: voiceChannelName, voiceStates, room, activeSpeakers, setScreenSharing, isPersonallyMuted, togglePersonalMute, isMuted: selfMuted, toggleMute, serverMute, isServerMuted, serverSettings, getUserVolume, setUserVolume } = useVoice();
|
||||
|
||||
const handleStartCreate = () => {
|
||||
setIsCreating(true);
|
||||
setNewChannelName('');
|
||||
@@ -987,7 +984,7 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
});
|
||||
}
|
||||
|
||||
new Audio(screenShareStartSound).play();
|
||||
if (!isReceivingScreenShareAudio) new Audio(screenShareStartSound).play();
|
||||
setScreenSharing(true);
|
||||
|
||||
track.onended = () => {
|
||||
@@ -1017,7 +1014,7 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
}
|
||||
}
|
||||
room.localParticipant.setScreenShareEnabled(false);
|
||||
new Audio(screenShareStopSound).play();
|
||||
if (!isReceivingScreenShareAudio) new Audio(screenShareStopSound).play();
|
||||
setScreenSharing(false);
|
||||
} else {
|
||||
setIsScreenShareModalOpen(true);
|
||||
@@ -1025,8 +1022,11 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
};
|
||||
|
||||
const handleChannelClick = (channel) => {
|
||||
if (channel.type === 'voice' && voiceChannelId !== channel._id) {
|
||||
connectToVoice(channel._id, channel.name, localStorage.getItem('userId'));
|
||||
if (channel.type === 'voice') {
|
||||
if (voiceChannelId !== channel._id) {
|
||||
connectToVoice(channel._id, channel.name, localStorage.getItem('userId'));
|
||||
}
|
||||
onSelectChannel(channel._id);
|
||||
} else {
|
||||
onSelectChannel(channel._id);
|
||||
}
|
||||
@@ -1127,11 +1127,18 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
);
|
||||
};
|
||||
|
||||
const toggleCategory = (cat) => {
|
||||
const next = { ...collapsedCategories, [cat]: !collapsedCategories[cat] };
|
||||
setCollapsedCategories(next);
|
||||
setUserPref(userId, 'collapsedCategories', next, settings);
|
||||
};
|
||||
const toggleCategory = useCallback((cat) => {
|
||||
setCollapsedCategories(prev => {
|
||||
const next = { ...prev, [cat]: !prev[cat] };
|
||||
setUserPref(userId, 'collapsedCategories', next, settings);
|
||||
return next;
|
||||
});
|
||||
}, [userId, settings]);
|
||||
|
||||
const handleAddChannelToCategory = useCallback((groupId) => {
|
||||
setCreateChannelCategoryId(groupId === '__uncategorized__' ? null : groupId);
|
||||
setShowCreateChannelModal(true);
|
||||
}, []);
|
||||
|
||||
// Group channels by categoryId
|
||||
const groupedChannels = React.useMemo(() => {
|
||||
@@ -1377,12 +1384,10 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
<SortableCategory key={group.id} id={`category-${group.id}`}>
|
||||
<CategoryHeader
|
||||
group={group}
|
||||
groupId={group.id}
|
||||
collapsed={collapsedCategories[group.id]}
|
||||
onToggle={() => toggleCategory(group.id)}
|
||||
onAddChannel={() => {
|
||||
setCreateChannelCategoryId(group.id === '__uncategorized__' ? null : group.id);
|
||||
setShowCreateChannelModal(true);
|
||||
}}
|
||||
onToggle={toggleCategory}
|
||||
onAddChannel={handleAddChannelToCategory}
|
||||
/>
|
||||
{(() => {
|
||||
const isCollapsed = collapsedCategories[group.id];
|
||||
@@ -1578,7 +1583,7 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
margin: '8px 8px 0px 8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderBottom: "1px solid color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.0392156862745098) 100%,hsl(0 0% 0% /0.0392156862745098) 0%)"
|
||||
borderBottom: '1px solid hsla(240, 4%, 60.784%, 0.039)'
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
|
||||
<div style={{ color: '#43b581', fontWeight: 'bold', fontSize: 13 }}>Voice Connected</div>
|
||||
@@ -1648,6 +1653,8 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
isServerMuted={isServerMuted(voiceUserMenu.user.userId)}
|
||||
onServerMute={() => serverMute(voiceUserMenu.user.userId, !isServerMuted(voiceUserMenu.user.userId))}
|
||||
hasPermission={!!myPermissions.mute_members}
|
||||
onDisconnect={() => disconnectUser(voiceUserMenu.user.userId)}
|
||||
hasDisconnectPermission={!!myPermissions.move_members}
|
||||
onMessage={() => {
|
||||
onOpenDM(voiceUserMenu.user.userId, voiceUserMenu.user.username);
|
||||
onViewChange('me');
|
||||
@@ -1692,16 +1699,16 @@ const Sidebar = ({ channels, categories, activeChannel, onSelectChannel, usernam
|
||||
};
|
||||
|
||||
// Category header component (extracted for DnD drag handle)
|
||||
const CategoryHeader = ({ group, collapsed, onToggle, onAddChannel, dragListeners }) => (
|
||||
<div className="channel-category-header" onClick={onToggle} {...(dragListeners || {})}>
|
||||
const CategoryHeader = React.memo(({ group, groupId, collapsed, onToggle, onAddChannel, dragListeners }) => (
|
||||
<div className="channel-category-header" onClick={() => onToggle(groupId)} {...(dragListeners || {})}>
|
||||
<span className="category-label">{group.name}</span>
|
||||
<div className={`category-chevron ${collapsed ? 'collapsed' : ''}`}>
|
||||
<ColoredIcon src={categoryCollapsedIcon} color="currentColor" size="12px" />
|
||||
</div>
|
||||
<button className="category-add-btn" onClick={(e) => { e.stopPropagation(); onAddChannel(); }} title="Create Channel">
|
||||
<button className="category-add-btn" onClick={(e) => { e.stopPropagation(); onAddChannel(groupId); }} title="Create Channel">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
));
|
||||
|
||||
export default Sidebar;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect, createContext, useContext } from 'react';
|
||||
import { usePlatform } from '../platform';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
import updateIcon from '../assets/icons/update.svg';
|
||||
|
||||
const RELEASE_URL = 'https://gitea.moyettes.com/Moyettes/DiscordClone/releases/tag/latest';
|
||||
@@ -73,26 +74,7 @@ export function TitleBarUpdateIcon() {
|
||||
style={{ borderRight: '1px solid var(--app-frame-border)' }}
|
||||
>
|
||||
<div style={{ marginRight: '12px' }}>
|
||||
<div style={{
|
||||
width: 20,
|
||||
height: 20,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<img
|
||||
src={updateIcon}
|
||||
alt=""
|
||||
style={{
|
||||
width: 20,
|
||||
height: 20,
|
||||
transform: 'translateX(-1000px)',
|
||||
filter: `drop-shadow(1000px 0 0 #3ba55c)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ColoredIcon src={updateIcon} color="#3ba55c" size="20px" />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -15,8 +15,9 @@ import personalMuteIcon from '../assets/icons/personal_mute.svg';
|
||||
import serverMuteIcon from '../assets/icons/server_mute.svg';
|
||||
import screenShareStartSound from '../assets/sounds/screenshare_start.mp3';
|
||||
import screenShareStopSound from '../assets/sounds/screenshare_stop.mp3';
|
||||
import ColoredIcon from './ColoredIcon';
|
||||
|
||||
const SERVER_MUTE_RED = 'color-mix(in oklab, hsl(1.343 calc(1*84.81%) 69.02% /1) 100%, #000 0%)';
|
||||
const SERVER_MUTE_RED = 'hsl(1.343, 84.81%, 69.02%)';
|
||||
|
||||
const getInitials = (name) => (name || '?').substring(0, 1).toUpperCase();
|
||||
|
||||
@@ -43,30 +44,6 @@ const WATCH_STREAM_BUTTON_STYLE = {
|
||||
const THUMBNAIL_SIZE = { width: 120, height: 68 };
|
||||
const BOTTOM_BAR_HEIGHT = 140;
|
||||
|
||||
// Helper Component for coloring SVGs (Reused from Sidebar)
|
||||
const ColoredIcon = ({ src, color, size = '24px' }) => (
|
||||
<div style={{
|
||||
width: size,
|
||||
height: size,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0
|
||||
}}>
|
||||
<img
|
||||
src={src}
|
||||
alt=""
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
transform: 'translateX(-1000px)',
|
||||
filter: `drop-shadow(1000px 0 0 ${color})`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
// --- Components ---
|
||||
|
||||
const ParticipantTile = ({ participant, username, avatarUrl }) => {
|
||||
@@ -266,6 +243,47 @@ const ParticipantThumbnail = ({ participant, username, avatarUrl, isStreamer, is
|
||||
);
|
||||
};
|
||||
|
||||
// Inline SVG icons for volume control
|
||||
const SpeakerIcon = ({ volume, muted }) => {
|
||||
if (muted || volume === 0) {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M11 5L6 9H2v6h4l5 4V5z" />
|
||||
<line x1="23" y1="9" x2="17" y2="15" stroke="white" strokeWidth="2" strokeLinecap="round" />
|
||||
<line x1="17" y1="9" x2="23" y2="15" stroke="white" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
if (volume < 50) {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M11 5L6 9H2v6h4l5 4V5z" />
|
||||
<path d="M15.54 8.46a5 5 0 010 7.07" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M11 5L6 9H2v6h4l5 4V5z" />
|
||||
<path d="M15.54 8.46a5 5 0 010 7.07" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" />
|
||||
<path d="M19.07 4.93a10 10 0 010 14.14" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
// Inline SVG icons for fullscreen
|
||||
const ExpandIcon = () => (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CompressIcon = () => (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="white">
|
||||
<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const FocusedStreamView = ({
|
||||
streamParticipant,
|
||||
streamerUsername,
|
||||
@@ -278,11 +296,34 @@ const FocusedStreamView = ({
|
||||
streamingIdentities,
|
||||
voiceUsers,
|
||||
isTabVisible,
|
||||
isFullscreen,
|
||||
onToggleFullscreen,
|
||||
localIdentity,
|
||||
}) => {
|
||||
const screenTrack = useParticipantTrack(streamParticipant, 'screenshare');
|
||||
const [barHover, setBarHover] = useState(false);
|
||||
const [bottomEdgeHover, setBottomEdgeHover] = useState(false);
|
||||
|
||||
// Volume control state
|
||||
const { getUserVolume, setUserVolume, togglePersonalMute, isPersonallyMuted } = useVoice();
|
||||
const streamerId = streamParticipant.identity;
|
||||
const isSelf = streamerId === localIdentity;
|
||||
const isMutedByMe = isPersonallyMuted(streamerId);
|
||||
const userVolume = getUserVolume(streamerId);
|
||||
const [volumeExpanded, setVolumeExpanded] = useState(false);
|
||||
const volumeHideTimeout = useRef(null);
|
||||
|
||||
const handleVolumeMouseEnter = () => {
|
||||
if (volumeHideTimeout.current) clearTimeout(volumeHideTimeout.current);
|
||||
setVolumeExpanded(true);
|
||||
};
|
||||
const handleVolumeMouseLeave = () => {
|
||||
volumeHideTimeout.current = setTimeout(() => setVolumeExpanded(false), 1500);
|
||||
};
|
||||
useEffect(() => () => { if (volumeHideTimeout.current) clearTimeout(volumeHideTimeout.current); }, []);
|
||||
|
||||
const sliderPercent = (userVolume / 200) * 100;
|
||||
|
||||
// Auto-exit if stream track disappears
|
||||
useEffect(() => {
|
||||
if (!streamParticipant) {
|
||||
@@ -362,23 +403,90 @@ const FocusedStreamView = ({
|
||||
<span style={LIVE_BADGE_STYLE}>LIVE</span>
|
||||
</div>
|
||||
|
||||
{/* Top-right: close button */}
|
||||
<button
|
||||
onClick={onStopWatching}
|
||||
title="Stop Watching"
|
||||
style={{
|
||||
position: 'absolute', top: '12px', right: '12px',
|
||||
width: '32px', height: '32px', borderRadius: '50%',
|
||||
backgroundColor: 'rgba(0,0,0,0.6)', border: 'none',
|
||||
color: 'white', fontSize: '18px', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
transition: 'background-color 0.15s',
|
||||
}}
|
||||
onMouseEnter={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.8)'}
|
||||
onMouseLeave={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.6)'}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
{/* Top-right: button group (fullscreen + close) */}
|
||||
<div style={{
|
||||
position: 'absolute', top: '12px', right: '12px',
|
||||
display: 'flex', gap: '8px',
|
||||
}}>
|
||||
<button
|
||||
onClick={onToggleFullscreen}
|
||||
title={isFullscreen ? "Exit Fullscreen" : "Fullscreen"}
|
||||
style={{
|
||||
width: '32px', height: '32px', borderRadius: '50%',
|
||||
backgroundColor: 'rgba(0,0,0,0.6)', border: 'none',
|
||||
color: 'white', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
transition: 'background-color 0.15s',
|
||||
}}
|
||||
onMouseEnter={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.8)'}
|
||||
onMouseLeave={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.6)'}
|
||||
>
|
||||
{isFullscreen ? <CompressIcon /> : <ExpandIcon />}
|
||||
</button>
|
||||
<button
|
||||
onClick={onStopWatching}
|
||||
title="Stop Watching"
|
||||
style={{
|
||||
width: '32px', height: '32px', borderRadius: '50%',
|
||||
backgroundColor: 'rgba(0,0,0,0.6)', border: 'none',
|
||||
color: 'white', fontSize: '18px', cursor: 'pointer',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
transition: 'background-color 0.15s',
|
||||
}}
|
||||
onMouseEnter={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.8)'}
|
||||
onMouseLeave={e => e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.6)'}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Bottom-left: volume control (hidden when watching own stream) */}
|
||||
{!isSelf && (
|
||||
<div
|
||||
onMouseEnter={handleVolumeMouseEnter}
|
||||
onMouseLeave={handleVolumeMouseLeave}
|
||||
style={{
|
||||
position: 'absolute', bottom: '12px', left: '12px',
|
||||
display: 'flex', alignItems: 'center', gap: '8px',
|
||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||
padding: '6px 10px',
|
||||
borderRadius: '6px',
|
||||
transition: 'width 0.2s ease',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => togglePersonalMute(streamerId)}
|
||||
title={isMutedByMe ? "Unmute" : "Mute"}
|
||||
style={{
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
padding: 0, display: 'flex', alignItems: 'center',
|
||||
opacity: isMutedByMe ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
<SpeakerIcon volume={userVolume} muted={isMutedByMe} />
|
||||
</button>
|
||||
{volumeExpanded && (
|
||||
<>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="200"
|
||||
value={isMutedByMe ? 0 : userVolume}
|
||||
onChange={(e) => setUserVolume(streamerId, Number(e.target.value))}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
className="context-menu-volume-slider"
|
||||
style={{
|
||||
width: '100px',
|
||||
background: `linear-gradient(to right, hsl(235, 86%, 65%) ${isMutedByMe ? 0 : sliderPercent}%, rgba(255,255,255,0.2) ${isMutedByMe ? 0 : sliderPercent}%)`,
|
||||
}}
|
||||
/>
|
||||
<span style={{ color: 'white', fontSize: '12px', minWidth: '32px', textAlign: 'right' }}>
|
||||
{isMutedByMe ? 0 : userVolume}%
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bottom participants bar */}
|
||||
@@ -473,13 +581,37 @@ const FocusedStreamView = ({
|
||||
|
||||
const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
const [participants, setParticipants] = useState([]);
|
||||
const { isMuted, toggleMute, disconnectVoice, setScreenSharing, connectToVoice, watchingStreamOf, setWatchingStreamOf } = useVoice();
|
||||
const { isMuted, toggleMute, disconnectVoice, setScreenSharing, connectToVoice, watchingStreamOf, setWatchingStreamOf, isReceivingScreenShareAudio } = useVoice();
|
||||
const [isScreenShareModalOpen, setIsScreenShareModalOpen] = useState(false);
|
||||
const [isScreenShareActive, setIsScreenShareActive] = useState(false);
|
||||
const screenShareAudioTrackRef = useRef(null);
|
||||
|
||||
const [participantsCollapsed, setParticipantsCollapsed] = useState(false);
|
||||
|
||||
// Fullscreen support
|
||||
const stageContainerRef = useRef(null);
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleFullscreenChange = () => {
|
||||
setIsFullscreen(!!document.fullscreenElement);
|
||||
};
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||
return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||
}, []);
|
||||
|
||||
const toggleFullscreen = useCallback(() => {
|
||||
if (!stageContainerRef.current) return;
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen().catch(console.error);
|
||||
} else {
|
||||
stageContainerRef.current.requestFullscreen().catch(console.error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const isReceivingScreenShareAudioRef = useRef(false);
|
||||
useEffect(() => { isReceivingScreenShareAudioRef.current = isReceivingScreenShareAudio; }, [isReceivingScreenShareAudio]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!room) return;
|
||||
|
||||
@@ -496,7 +628,7 @@ const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
room.on(RoomEvent.ParticipantDisconnected, updateParticipants);
|
||||
room.localParticipant.on('localTrackPublished', updateParticipants);
|
||||
room.localParticipant.on('localTrackUnpublished', (pub) => {
|
||||
if (pub.source === Track.Source.ScreenShare || pub.source === 'screen_share') {
|
||||
if ((pub.source === Track.Source.ScreenShare || pub.source === 'screen_share') && !isReceivingScreenShareAudioRef.current) {
|
||||
new Audio(screenShareStopSound).play();
|
||||
}
|
||||
updateParticipants();
|
||||
@@ -510,10 +642,11 @@ const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
};
|
||||
}, [room]);
|
||||
|
||||
// Reset collapsed state when room disconnects
|
||||
// Reset collapsed state and exit fullscreen when room disconnects
|
||||
useEffect(() => {
|
||||
if (!room) {
|
||||
setParticipantsCollapsed(false);
|
||||
if (document.fullscreenElement) document.exitFullscreen().catch(console.error);
|
||||
}
|
||||
}, [room]);
|
||||
|
||||
@@ -524,6 +657,7 @@ const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
);
|
||||
|
||||
const handleStopWatching = useCallback(() => {
|
||||
if (document.fullscreenElement) document.exitFullscreen().catch(console.error);
|
||||
setWatchingStreamOf(null);
|
||||
setParticipantsCollapsed(false);
|
||||
}, []);
|
||||
@@ -589,7 +723,7 @@ const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
}
|
||||
const track = stream.getVideoTracks()[0];
|
||||
if (track) {
|
||||
new Audio(screenShareStartSound).play();
|
||||
if (!isReceivingScreenShareAudio) new Audio(screenShareStartSound).play();
|
||||
await room.localParticipant.publishTrack(track, {
|
||||
name: 'screen_share',
|
||||
source: Track.Source.ScreenShare
|
||||
@@ -657,6 +791,21 @@ const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
return () => document.removeEventListener('visibilitychange', handler);
|
||||
}, []);
|
||||
|
||||
// F key shortcut to toggle fullscreen when watching a stream
|
||||
useEffect(() => {
|
||||
if (!watchingStreamOf) return;
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'f' || e.key === 'F') {
|
||||
const tag = e.target.tagName;
|
||||
if (tag === 'INPUT' || tag === 'TEXTAREA' || e.target.isContentEditable) return;
|
||||
e.preventDefault();
|
||||
toggleFullscreen();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}, [watchingStreamOf, toggleFullscreen]);
|
||||
|
||||
if (!room) {
|
||||
return (
|
||||
<div style={{
|
||||
@@ -732,7 +881,7 @@ const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%', flex: 1, backgroundColor: 'black', width: '100%' }}>
|
||||
<div ref={stageContainerRef} style={{ display: 'flex', flexDirection: 'column', height: '100%', flex: 1, backgroundColor: 'black', width: '100%' }}>
|
||||
{watchingStreamOf && watchedParticipant ? (
|
||||
/* Focused/Fullscreen View */
|
||||
<FocusedStreamView
|
||||
@@ -747,6 +896,9 @@ const VoiceStage = ({ room, channelId, voiceStates, channelName }) => {
|
||||
streamingIdentities={streamingIdentities}
|
||||
voiceUsers={voiceUsers}
|
||||
isTabVisible={isTabVisible}
|
||||
isFullscreen={isFullscreen}
|
||||
onToggleFullscreen={toggleFullscreen}
|
||||
localIdentity={room.localParticipant.identity}
|
||||
/>
|
||||
) : (
|
||||
/* Grid View */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
import usePresence from '@convex-dev/presence/react';
|
||||
import usePresence from '../hooks/usePresence.js';
|
||||
import { api } from '../../../../convex/_generated/api';
|
||||
|
||||
const PresenceContext = createContext({
|
||||
|
||||
@@ -31,7 +31,10 @@ const VoiceContext = createContext();
|
||||
|
||||
export const useVoice = () => useContext(VoiceContext);
|
||||
|
||||
let _suppressAppSounds = false;
|
||||
|
||||
function playSound(type) {
|
||||
if (_suppressAppSounds) return;
|
||||
const src = soundMap[type];
|
||||
if (!src) return;
|
||||
const audio = new Audio(src);
|
||||
@@ -40,6 +43,7 @@ function playSound(type) {
|
||||
}
|
||||
|
||||
function playSoundUrl(url) {
|
||||
if (_suppressAppSounds) return;
|
||||
const audio = new Audio(url);
|
||||
audio.volume = 0.5;
|
||||
audio.play().catch(e => console.error("Sound play failed", e));
|
||||
@@ -60,6 +64,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
parseInt(localStorage.getItem('voiceOutputVolume') || '100')
|
||||
);
|
||||
const isMovingRef = useRef(false);
|
||||
const [isReceivingScreenShareAudio, setIsReceivingScreenShareAudio] = useState(false);
|
||||
|
||||
const convex = useConvex();
|
||||
|
||||
@@ -113,7 +118,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
// Apply volume to LiveKit participant (factoring in global output volume)
|
||||
const participant = room?.remoteParticipants?.get(userId);
|
||||
const globalVol = globalOutputVolume / 100;
|
||||
if (participant) participant.setVolume((volume / 100) * globalVol);
|
||||
if (participant) participant.setVolume(Math.min(1, (volume / 100) * globalVol));
|
||||
// Sync personal mute state
|
||||
if (volume === 0) {
|
||||
setPersonallyMutedUsers(prev => {
|
||||
@@ -147,7 +152,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
const vol = userVolumes[userId] ?? 100;
|
||||
const restoreVol = vol === 0 ? 100 : vol;
|
||||
const participant = room?.remoteParticipants?.get(userId);
|
||||
if (participant) participant.setVolume((restoreVol / 100) * globalVol);
|
||||
if (participant) participant.setVolume(Math.min(1, (restoreVol / 100) * globalVol));
|
||||
// Update stored volume if it was 0
|
||||
if (vol === 0) {
|
||||
setUserVolumes(p => {
|
||||
@@ -178,6 +183,16 @@ export const VoiceProvider = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const disconnectUser = async (targetUserId) => {
|
||||
const actorUserId = localStorage.getItem('userId');
|
||||
if (!actorUserId) return;
|
||||
try {
|
||||
await convex.mutation(api.voiceState.disconnectUser, { actorUserId, targetUserId });
|
||||
} catch (e) {
|
||||
console.error('Failed to disconnect user:', e);
|
||||
}
|
||||
};
|
||||
|
||||
const isServerMuted = (userId) => {
|
||||
for (const users of Object.values(voiceStates)) {
|
||||
const user = users.find(u => u.userId === userId);
|
||||
@@ -197,7 +212,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
);
|
||||
|
||||
// Refs for detecting other-user joins via voiceStates changes
|
||||
const prevChannelUsersRef = useRef(new Map());
|
||||
const prevChannelUsersRef = useRef(new Set());
|
||||
const otherJoinInitRef = useRef(false);
|
||||
const isInAfkChannel = !!(activeChannelId && serverSettings?.afkChannelId === activeChannelId);
|
||||
|
||||
@@ -378,7 +393,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
participant.setVolume(0);
|
||||
} else {
|
||||
const userVol = (userVolumes[identity] ?? 100) / 100;
|
||||
participant.setVolume(userVol * globalVol);
|
||||
participant.setVolume(Math.min(1, userVol * globalVol));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -418,31 +433,34 @@ export const VoiceProvider = ({ children }) => {
|
||||
// Detect other users joining the same voice channel and play their join sound
|
||||
useEffect(() => {
|
||||
if (!activeChannelId) {
|
||||
prevChannelUsersRef.current = new Map();
|
||||
prevChannelUsersRef.current = new Set();
|
||||
otherJoinInitRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const selfId = localStorage.getItem('userId');
|
||||
const channelUsers = voiceStates[activeChannelId] || [];
|
||||
const currentUsers = new Map();
|
||||
for (const u of channelUsers) {
|
||||
currentUsers.set(u.userId, u);
|
||||
const currentUserIds = new Set(channelUsers.map(u => u.userId));
|
||||
|
||||
// Guard: ignore transient empty states when we previously had users
|
||||
if (currentUserIds.size === 0 && prevChannelUsersRef.current.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the first render after joining to avoid playing sounds for users already in the channel
|
||||
if (!otherJoinInitRef.current) {
|
||||
otherJoinInitRef.current = true;
|
||||
prevChannelUsersRef.current = currentUsers;
|
||||
prevChannelUsersRef.current = currentUserIds;
|
||||
return;
|
||||
}
|
||||
|
||||
const prev = prevChannelUsersRef.current;
|
||||
const prevIds = prevChannelUsersRef.current;
|
||||
|
||||
// Detect new users (not self)
|
||||
for (const [uid, userData] of currentUsers) {
|
||||
if (uid !== selfId && !prev.has(uid)) {
|
||||
if (userData.joinSoundUrl) {
|
||||
for (const uid of currentUserIds) {
|
||||
if (uid !== selfId && !prevIds.has(uid)) {
|
||||
const userData = channelUsers.find(u => u.userId === uid);
|
||||
if (userData?.joinSoundUrl) {
|
||||
playSoundUrl(userData.joinSoundUrl);
|
||||
} else {
|
||||
playSound('join');
|
||||
@@ -451,7 +469,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
}
|
||||
}
|
||||
|
||||
prevChannelUsersRef.current = currentUsers;
|
||||
prevChannelUsersRef.current = currentUserIds;
|
||||
}, [voiceStates, activeChannelId]);
|
||||
|
||||
// Manage screen share subscriptions — only subscribe when actively watching
|
||||
@@ -459,6 +477,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
if (!room) return;
|
||||
|
||||
const manageSubscriptions = () => {
|
||||
let receivingAudio = false;
|
||||
for (const p of room.remoteParticipants.values()) {
|
||||
const { screenSharePub, screenShareAudioPub } = findTrackPubs(p);
|
||||
|
||||
@@ -470,7 +489,13 @@ export const VoiceProvider = ({ children }) => {
|
||||
if (screenShareAudioPub && screenShareAudioPub.isSubscribed !== shouldSubscribe) {
|
||||
screenShareAudioPub.setSubscribed(shouldSubscribe);
|
||||
}
|
||||
|
||||
if (shouldSubscribe && screenShareAudioPub && screenShareAudioPub.isSubscribed) {
|
||||
receivingAudio = true;
|
||||
}
|
||||
}
|
||||
_suppressAppSounds = receivingAudio;
|
||||
setIsReceivingScreenShareAudio(receivingAudio);
|
||||
};
|
||||
|
||||
manageSubscriptions();
|
||||
@@ -478,10 +503,14 @@ export const VoiceProvider = ({ children }) => {
|
||||
const onTrackChange = () => manageSubscriptions();
|
||||
room.on(RoomEvent.TrackPublished, onTrackChange);
|
||||
room.on(RoomEvent.TrackSubscribed, onTrackChange);
|
||||
room.on(RoomEvent.TrackUnsubscribed, onTrackChange);
|
||||
|
||||
return () => {
|
||||
room.off(RoomEvent.TrackPublished, onTrackChange);
|
||||
room.off(RoomEvent.TrackSubscribed, onTrackChange);
|
||||
room.off(RoomEvent.TrackUnsubscribed, onTrackChange);
|
||||
_suppressAppSounds = false;
|
||||
setIsReceivingScreenShareAudio(false);
|
||||
};
|
||||
}, [room, watchingStreamOf]);
|
||||
|
||||
@@ -639,6 +668,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
setUserVolume,
|
||||
getUserVolume,
|
||||
serverMute,
|
||||
disconnectUser,
|
||||
isServerMuted,
|
||||
isInAfkChannel,
|
||||
serverSettings,
|
||||
@@ -647,6 +677,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
switchDevice,
|
||||
globalOutputVolume,
|
||||
setGlobalOutputVolume,
|
||||
isReceivingScreenShareAudio,
|
||||
}}>
|
||||
{children}
|
||||
{room && (
|
||||
|
||||
18
packages/shared/src/hooks/useIsMobile.js
Normal file
18
packages/shared/src/hooks/useIsMobile.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const MOBILE_BREAKPOINT = '(max-width: 768px)';
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = useState(() =>
|
||||
typeof window !== 'undefined' && window.matchMedia(MOBILE_BREAKPOINT).matches
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const mql = window.matchMedia(MOBILE_BREAKPOINT);
|
||||
const handler = (e) => setIsMobile(e.matches);
|
||||
mql.addEventListener('change', handler);
|
||||
return () => mql.removeEventListener('change', handler);
|
||||
}, []);
|
||||
|
||||
return isMobile;
|
||||
}
|
||||
123
packages/shared/src/hooks/usePresence.js
Normal file
123
packages/shared/src/hooks/usePresence.js
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Custom usePresence hook based on @convex-dev/presence/react.
|
||||
*
|
||||
* Fix: The upstream hook disconnects on `visibilitychange` (document.hidden),
|
||||
* which marks the user offline when the Electron window is minimized.
|
||||
* This version keeps heartbeats running when hidden and only sends an
|
||||
* immediate heartbeat + restarts the interval when becoming visible again
|
||||
* (in case the browser had throttled timers).
|
||||
*/
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useQuery, useMutation, useConvex } from "convex/react";
|
||||
import useSingleFlight from "./useSingleFlight.js";
|
||||
|
||||
export default function usePresence(presence, roomId, userId, interval = 10000, convexUrl) {
|
||||
const hasMounted = useRef(false);
|
||||
const convex = useConvex();
|
||||
const baseUrl = convexUrl ?? convex.url;
|
||||
|
||||
const [sessionId, setSessionId] = useState(() => crypto.randomUUID());
|
||||
const [sessionToken, setSessionToken] = useState(null);
|
||||
const sessionTokenRef = useRef(null);
|
||||
const [roomToken, setRoomToken] = useState(null);
|
||||
const roomTokenRef = useRef(null);
|
||||
const intervalRef = useRef(null);
|
||||
|
||||
const heartbeat = useSingleFlight(useMutation(presence.heartbeat));
|
||||
const disconnect = useSingleFlight(useMutation(presence.disconnect));
|
||||
|
||||
useEffect(() => {
|
||||
// Reset session state when roomId or userId changes.
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
if (sessionTokenRef.current) {
|
||||
void disconnect({ sessionToken: sessionTokenRef.current });
|
||||
}
|
||||
setSessionId(crypto.randomUUID());
|
||||
setSessionToken(null);
|
||||
setRoomToken(null);
|
||||
}, [roomId, userId, disconnect]);
|
||||
|
||||
useEffect(() => {
|
||||
sessionTokenRef.current = sessionToken;
|
||||
roomTokenRef.current = roomToken;
|
||||
}, [sessionToken, roomToken]);
|
||||
|
||||
useEffect(() => {
|
||||
const sendHeartbeat = async () => {
|
||||
const result = await heartbeat({ roomId, userId, sessionId, interval });
|
||||
setRoomToken(result.roomToken);
|
||||
setSessionToken(result.sessionToken);
|
||||
};
|
||||
|
||||
// Send initial heartbeat
|
||||
void sendHeartbeat();
|
||||
|
||||
// Clear any existing interval before setting a new one
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
intervalRef.current = setInterval(sendHeartbeat, interval);
|
||||
|
||||
// Handle page unload.
|
||||
const handleUnload = () => {
|
||||
if (sessionTokenRef.current) {
|
||||
const blob = new Blob([
|
||||
JSON.stringify({
|
||||
path: "presence:disconnect",
|
||||
args: { sessionToken: sessionTokenRef.current },
|
||||
}),
|
||||
], { type: "application/json" });
|
||||
navigator.sendBeacon(`${baseUrl}/api/mutation`, blob);
|
||||
}
|
||||
};
|
||||
window.addEventListener("beforeunload", handleUnload);
|
||||
|
||||
// Handle visibility changes.
|
||||
// FIX: Do NOT disconnect when hidden. Electron timers keep running
|
||||
// when minimized, so heartbeats continue normally. Only send an
|
||||
// immediate heartbeat when becoming visible again to recover quickly
|
||||
// in case the browser had throttled the interval.
|
||||
const handleVisibility = async () => {
|
||||
if (!document.hidden) {
|
||||
void sendHeartbeat();
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
intervalRef.current = setInterval(sendHeartbeat, interval);
|
||||
}
|
||||
};
|
||||
const wrappedHandleVisibility = () => {
|
||||
handleVisibility().catch(console.error);
|
||||
};
|
||||
document.addEventListener("visibilitychange", wrappedHandleVisibility);
|
||||
|
||||
// Cleanup.
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
document.removeEventListener("visibilitychange", wrappedHandleVisibility);
|
||||
window.removeEventListener("beforeunload", handleUnload);
|
||||
// Don't disconnect on first render in strict mode.
|
||||
if (hasMounted.current) {
|
||||
if (sessionTokenRef.current) {
|
||||
void disconnect({ sessionToken: sessionTokenRef.current });
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [heartbeat, disconnect, roomId, userId, baseUrl, interval, sessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
hasMounted.current = true;
|
||||
}, []);
|
||||
|
||||
const state = useQuery(presence.list, roomToken ? { roomToken } : "skip");
|
||||
return useMemo(() => state?.slice().sort((a, b) => {
|
||||
if (a.userId === userId) return -1;
|
||||
if (b.userId === userId) return 1;
|
||||
return 0;
|
||||
}), [state, userId]);
|
||||
}
|
||||
40
packages/shared/src/hooks/useSingleFlight.js
Normal file
40
packages/shared/src/hooks/useSingleFlight.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useCallback, useRef } from "react";
|
||||
|
||||
/**
|
||||
* Wraps a function to single-flight invocations, using the latest args.
|
||||
*
|
||||
* Copied from @convex-dev/presence/dist/react/useSingleFlight.js
|
||||
*/
|
||||
export default function useSingleFlight(fn) {
|
||||
const flightStatus = useRef({
|
||||
inFlight: false,
|
||||
upNext: null,
|
||||
});
|
||||
return useCallback((...args) => {
|
||||
if (flightStatus.current.inFlight) {
|
||||
return new Promise((resolve, reject) => {
|
||||
flightStatus.current.upNext = { fn, resolve, reject, args };
|
||||
});
|
||||
}
|
||||
flightStatus.current.inFlight = true;
|
||||
const firstReq = fn(...args);
|
||||
void (async () => {
|
||||
try {
|
||||
await firstReq;
|
||||
}
|
||||
finally {
|
||||
// If it failed, we naively just move on to the next request.
|
||||
}
|
||||
while (flightStatus.current.upNext) {
|
||||
const cur = flightStatus.current.upNext;
|
||||
flightStatus.current.upNext = null;
|
||||
await cur
|
||||
.fn(...cur.args)
|
||||
.then(cur.resolve)
|
||||
.catch(cur.reject);
|
||||
}
|
||||
flightStatus.current.inFlight = false;
|
||||
})();
|
||||
return firstReq;
|
||||
}, [fn]);
|
||||
}
|
||||
@@ -28,9 +28,14 @@
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
font-family: "gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-normal);
|
||||
@@ -1220,15 +1225,15 @@ body {
|
||||
}
|
||||
|
||||
.context-menu-item:hover {
|
||||
background-color: hsl(240 calc(1*4%) 60.784% /0.0784313725490196);
|
||||
background-color: hsla(240, 4%, 60.784%, 0.078);
|
||||
}
|
||||
|
||||
.context-menu-item-danger {
|
||||
color: color-mix(in oklab, hsl(1.353 calc(1*82.609%) 68.431% /1) 100%, #000 0%);
|
||||
color: hsl(1.353, 82.609%, 68.431%);
|
||||
}
|
||||
|
||||
.context-menu-item-danger:hover {
|
||||
background-color: color-mix(in oklab,hsl(355.636 calc(1*64.706%) 50% /0.0784313725490196) 100%,hsl(0 0% 0% /0.0784313725490196) 0%);
|
||||
background-color: hsla(355.636, 64.706%, 50%, 0.078);
|
||||
}
|
||||
|
||||
.context-menu-checkbox-item {
|
||||
@@ -1255,8 +1260,8 @@ body {
|
||||
}
|
||||
|
||||
.context-menu-checkbox-indicator.checked {
|
||||
background-color: hsl(235 86% 65%);
|
||||
border-color: hsl(235 86% 65%);
|
||||
background-color: hsl(235, 86%, 65%);
|
||||
border-color: hsl(235, 86%, 65%);
|
||||
}
|
||||
|
||||
.context-menu-separator {
|
||||
@@ -1465,6 +1470,7 @@ body {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -3086,3 +3092,103 @@ body {
|
||||
outline: 2px dashed var(--brand-experiment);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
MOBILE BACK BUTTON
|
||||
============================================ */
|
||||
.mobile-back-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--header-secondary);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
margin-right: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mobile-back-btn:hover {
|
||||
color: var(--header-primary);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
MOBILE RESPONSIVE (max-width: 768px)
|
||||
============================================ */
|
||||
@media (max-width: 768px) {
|
||||
/* App container: full dynamic viewport, no titlebar gap */
|
||||
.app-container.is-mobile {
|
||||
height: 100dvh;
|
||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Sidebar fills entire screen on mobile */
|
||||
.is-mobile .sidebar {
|
||||
width: 100vw;
|
||||
min-width: 100vw;
|
||||
}
|
||||
|
||||
/* Hide members list on mobile (also enforced in JS) */
|
||||
.is-mobile .members-list {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Auth box responsive */
|
||||
.auth-box {
|
||||
width: calc(100vw - 32px);
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
/* Pinned panel full-width on mobile */
|
||||
.is-mobile .pinned-panel {
|
||||
width: 100vw;
|
||||
right: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Toast container centered on mobile */
|
||||
.is-mobile .toast-container {
|
||||
right: auto;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 16px;
|
||||
}
|
||||
|
||||
.is-mobile .toast {
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
/* Responsive modals */
|
||||
.create-channel-modal {
|
||||
width: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
.theme-selector-modal {
|
||||
width: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
.avatar-crop-dialog {
|
||||
width: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
.forced-update-modal {
|
||||
width: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
/* Chat container takes full width */
|
||||
.is-mobile .chat-container {
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
/* Channel topic - hide on very small screens (also hidden via JS) */
|
||||
.is-mobile .chat-header-topic {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* FriendsView takes full width */
|
||||
.is-mobile .friends-view {
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,11 @@ import { useToasts } from '../components/Toast';
|
||||
import { PresenceProvider } from '../contexts/PresenceContext';
|
||||
import { getUserPref, setUserPref } from '../utils/userPreferences';
|
||||
import { usePlatform } from '../platform';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
|
||||
const Chat = () => {
|
||||
const { crypto, settings } = usePlatform();
|
||||
const isMobile = useIsMobile();
|
||||
const [userId, setUserId] = useState(() => localStorage.getItem('userId'));
|
||||
const [username, setUsername] = useState(() => localStorage.getItem('username') || '');
|
||||
const [view, setView] = useState(() => {
|
||||
@@ -27,6 +29,7 @@ const Chat = () => {
|
||||
const [activeDMChannel, setActiveDMChannel] = useState(null);
|
||||
const [showMembers, setShowMembers] = useState(true);
|
||||
const [showPinned, setShowPinned] = useState(false);
|
||||
const [mobileView, setMobileView] = useState('sidebar');
|
||||
|
||||
const convex = useConvex();
|
||||
const { toasts, addToast, removeToast, ToastContainer } = useToasts();
|
||||
@@ -156,15 +159,21 @@ const Chat = () => {
|
||||
|
||||
setActiveDMChannel({ channel_id: channelId, other_username: targetUsername });
|
||||
setView('me');
|
||||
if (isMobile) setMobileView('chat');
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error opening DM:', err);
|
||||
}
|
||||
}, [convex]);
|
||||
}, [convex, isMobile]);
|
||||
|
||||
const handleSelectChannel = useCallback((channelId) => {
|
||||
setActiveChannel(channelId);
|
||||
setShowPinned(false);
|
||||
if (isMobile) setMobileView('chat');
|
||||
}, [isMobile]);
|
||||
|
||||
const handleMobileBack = useCallback(() => {
|
||||
setMobileView('sidebar');
|
||||
}, []);
|
||||
|
||||
const activeChannelObj = channels.find(c => c._id === activeChannel);
|
||||
@@ -173,6 +182,7 @@ const Chat = () => {
|
||||
const isDMView = view === 'me' && activeDMChannel;
|
||||
const isServerTextChannel = view === 'server' && activeChannel && activeChannelObj?.type !== 'voice';
|
||||
const currentChannelId = isDMView ? activeDMChannel.channel_id : activeChannel;
|
||||
const effectiveShowMembers = isMobile ? false : showMembers;
|
||||
|
||||
// PiP: show when watching a stream and NOT currently viewing the voice channel in VoiceStage
|
||||
const isViewingVoiceStage = view === 'server' && activeChannelObj?.type === 'voice' && activeChannel === voiceActiveChannelId;
|
||||
@@ -196,6 +206,8 @@ const Chat = () => {
|
||||
onToggleMembers={() => {}}
|
||||
showMembers={false}
|
||||
onTogglePinned={() => setShowPinned(p => !p)}
|
||||
isMobile={isMobile}
|
||||
onMobileBack={handleMobileBack}
|
||||
/>
|
||||
<div className="chat-content">
|
||||
<ChatArea
|
||||
@@ -215,12 +227,43 @@ const Chat = () => {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <FriendsView onOpenDM={openDM} />;
|
||||
return (
|
||||
<>
|
||||
{isMobile && (
|
||||
<div className="chat-header" style={{ position: 'sticky', top: 0, zIndex: 10 }}>
|
||||
<div className="chat-header-left">
|
||||
<button className="mobile-back-btn" onClick={handleMobileBack}>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<span className="chat-header-name">Friends</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<FriendsView onOpenDM={openDM} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (activeChannel) {
|
||||
if (activeChannelObj?.type === 'voice') {
|
||||
return <VoiceStage room={room} channelId={activeChannel} voiceStates={voiceStates} channelName={activeChannelObj?.name} />;
|
||||
return (
|
||||
<div className="chat-container">
|
||||
{isMobile && (
|
||||
<div className="chat-header">
|
||||
<div className="chat-header-left">
|
||||
<button className="mobile-back-btn" onClick={handleMobileBack}>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
||||
</button>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" style={{ color: 'var(--text-muted)', marginRight: 4 }}>
|
||||
<path fill="currentColor" d="M11.383 3.07904C11.009 2.92504 10.579 3.01004 10.293 3.29604L6.586 7.00304H3C2.45 7.00304 2 7.45304 2 8.00304V16.003C2 16.553 2.45 17.003 3 17.003H6.586L10.293 20.71C10.579 20.996 11.009 21.082 11.383 20.927C11.757 20.772 12 20.407 12 20.003V4.00304C12 3.59904 11.757 3.23404 11.383 3.07904Z" />
|
||||
</svg>
|
||||
<span className="chat-header-name">{activeChannelObj?.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<VoiceStage room={room} channelId={activeChannel} voiceStates={voiceStates} channelName={activeChannelObj?.name} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="chat-container">
|
||||
@@ -229,9 +272,11 @@ const Chat = () => {
|
||||
channelType="text"
|
||||
channelTopic={activeChannelObj?.topic}
|
||||
onToggleMembers={() => setShowMembers(!showMembers)}
|
||||
showMembers={showMembers}
|
||||
showMembers={effectiveShowMembers}
|
||||
onTogglePinned={() => setShowPinned(p => !p)}
|
||||
serverName={serverName}
|
||||
isMobile={isMobile}
|
||||
onMobileBack={handleMobileBack}
|
||||
/>
|
||||
<div className="chat-content">
|
||||
<ChatArea
|
||||
@@ -241,7 +286,7 @@ const Chat = () => {
|
||||
channelKey={channelKeys[activeChannel]}
|
||||
username={username}
|
||||
userId={userId}
|
||||
showMembers={showMembers}
|
||||
showMembers={effectiveShowMembers}
|
||||
onToggleMembers={() => setShowMembers(!showMembers)}
|
||||
onOpenDM={openDM}
|
||||
showPinned={showPinned}
|
||||
@@ -249,7 +294,7 @@ const Chat = () => {
|
||||
/>
|
||||
<MembersList
|
||||
channelId={activeChannel}
|
||||
visible={showMembers}
|
||||
visible={effectiveShowMembers}
|
||||
onMemberClick={(member) => {}}
|
||||
/>
|
||||
</div>
|
||||
@@ -266,6 +311,16 @@ const Chat = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const handleSetActiveDMChannel = useCallback((dm) => {
|
||||
setActiveDMChannel(dm);
|
||||
if (isMobile && dm) setMobileView('chat');
|
||||
}, [isMobile]);
|
||||
|
||||
const handleViewChange = useCallback((newView) => {
|
||||
setView(newView);
|
||||
if (isMobile) setMobileView('sidebar');
|
||||
}, [isMobile]);
|
||||
|
||||
if (!userId) {
|
||||
return (
|
||||
<div className="app-container">
|
||||
@@ -276,27 +331,33 @@ const Chat = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const showSidebar = !isMobile || mobileView === 'sidebar';
|
||||
const showMainContent = !isMobile || mobileView === 'chat';
|
||||
|
||||
return (
|
||||
<PresenceProvider userId={userId}>
|
||||
<div className="app-container">
|
||||
<Sidebar
|
||||
channels={channels}
|
||||
categories={categories}
|
||||
activeChannel={activeChannel}
|
||||
onSelectChannel={handleSelectChannel}
|
||||
username={username}
|
||||
channelKeys={channelKeys}
|
||||
view={view}
|
||||
onViewChange={setView}
|
||||
onOpenDM={openDM}
|
||||
activeDMChannel={activeDMChannel}
|
||||
setActiveDMChannel={setActiveDMChannel}
|
||||
dmChannels={dmChannels}
|
||||
userId={userId}
|
||||
serverName={serverName}
|
||||
serverIconUrl={serverIconUrl}
|
||||
/>
|
||||
{renderMainContent()}
|
||||
<div className={`app-container${isMobile ? ' is-mobile' : ''}`}>
|
||||
{showSidebar && (
|
||||
<Sidebar
|
||||
channels={channels}
|
||||
categories={categories}
|
||||
activeChannel={activeChannel}
|
||||
onSelectChannel={handleSelectChannel}
|
||||
username={username}
|
||||
channelKeys={channelKeys}
|
||||
view={view}
|
||||
onViewChange={handleViewChange}
|
||||
onOpenDM={openDM}
|
||||
activeDMChannel={activeDMChannel}
|
||||
setActiveDMChannel={handleSetActiveDMChannel}
|
||||
dmChannels={dmChannels}
|
||||
userId={userId}
|
||||
serverName={serverName}
|
||||
serverIconUrl={serverIconUrl}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
)}
|
||||
{showMainContent && renderMainContent()}
|
||||
{showPiP && <FloatingStreamPiP onGoBackToStream={handleGoBackToStream} />}
|
||||
<ToastContainer />
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
--border-muted: rgba(255, 255, 255, 0.04);
|
||||
--border-normal: rgba(255, 255, 255, 0.2);
|
||||
--border-strong: rgba(255, 255, 255, 0.44);
|
||||
--app-frame-border: color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.12156862745098039) 100%,hsl(0 0% 0% /0.12156862745098039) 0%);
|
||||
--app-frame-border: hsla(240, 4%, 60.784%, 0.122);
|
||||
|
||||
/* Icons */
|
||||
--icon-default: #dbdee1;
|
||||
@@ -95,7 +95,7 @@
|
||||
--background-modifier-selected: rgba(78, 80, 88, 0.6);
|
||||
--div-border: #1e1f22;
|
||||
|
||||
--text-feedback-warning: color-mix(in oklab, hsl(38.455 calc(1*100%) 43.137% /1) 100%, #000 0%);
|
||||
--text-feedback-warning: hsl(38.455, 100%, 43.137%);
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
--border-muted: rgba(0, 0, 0, 0.2);
|
||||
--border-normal: rgba(0, 0, 0, 0.36);
|
||||
--border-strong: rgba(0, 0, 0, 0.48);
|
||||
--app-frame-border: color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.12156862745098039) 100%,hsl(0 0% 0% /0.12156862745098039) 0%);
|
||||
--app-frame-border: hsla(240, 4%, 60.784%, 0.122);
|
||||
|
||||
/* Icons */
|
||||
--icon-default: #313338;
|
||||
@@ -186,7 +186,7 @@
|
||||
--background-modifier-selected: rgba(116, 124, 138, 0.30);
|
||||
--div-border: #e1e2e4;
|
||||
|
||||
--text-feedback-warning: color-mix(in oklab, hsl(38.455 calc(1*100%) 43.137% /1) 100%, #000 0%);
|
||||
--text-feedback-warning: hsl(38.455, 100%, 43.137%);
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
--chat-background: #202225;
|
||||
--channeltextarea-background: #252529;
|
||||
--modal-background: #292b2f;
|
||||
--panel-bg: color-mix(in oklab, hsl(240 calc(1*5.882%) 13.333% /1) 100%, #000 0%);
|
||||
--panel-bg: hsl(240, 5.882%, 13.333%);
|
||||
--embed-background: #242529;
|
||||
|
||||
/* Text */
|
||||
@@ -232,7 +232,7 @@
|
||||
--border-muted: rgba(255, 255, 255, 0.04);
|
||||
--border-normal: rgba(255, 255, 255, 0.2);
|
||||
--border-strong: rgba(255, 255, 255, 0.44);
|
||||
--app-frame-border: color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.12156862745098039) 100%,hsl(0 0% 0% /0.12156862745098039) 0%);
|
||||
--app-frame-border: hsla(240, 4%, 60.784%, 0.122);
|
||||
|
||||
/* Icons */
|
||||
--icon-default: #dddfe4;
|
||||
@@ -277,7 +277,7 @@
|
||||
--background-modifier-selected: rgba(78, 80, 88, 0.4);
|
||||
--div-border: #111214;
|
||||
|
||||
--text-feedback-warning: color-mix(in oklab, hsl(38.455 calc(1*100%) 43.137% /1) 100%, #000 0%);
|
||||
--text-feedback-warning: hsl(38.455, 100%, 43.137%);
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@
|
||||
--border-muted: rgba(255, 255, 255, 0.16);
|
||||
--border-normal: rgba(255, 255, 255, 0.24);
|
||||
--border-strong: rgba(255, 255, 255, 0.44);
|
||||
--app-frame-border: color-mix(in oklab,hsl(240 calc(1*4%) 60.784% /0.12156862745098039) 100%,hsl(0 0% 0% /0.12156862745098039) 0%);
|
||||
--app-frame-border: hsla(240, 4%, 60.784%, 0.122);
|
||||
|
||||
/* Icons */
|
||||
--icon-default: #e0def0;
|
||||
@@ -368,5 +368,5 @@
|
||||
--background-modifier-selected: rgba(78, 73, 106, 0.48);
|
||||
--div-border: #080810;
|
||||
|
||||
--text-feedback-warning: color-mix(in oklab, hsl(38.455 calc(1*100%) 43.137% /1) 100%, #000 0%);
|
||||
--text-feedback-warning: hsl(38.455, 100%, 43.137%);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user