mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 06:56:04 +02:00
add img fallback
This commit is contained in:
Generated
+61
-2
@@ -7,7 +7,6 @@
|
|||||||
"name": "VRCX",
|
"name": "VRCX",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/vue-query": "^5.92.9",
|
|
||||||
"hazardous": "^0.3.0",
|
"hazardous": "^0.3.0",
|
||||||
"node-api-dotnet": "^0.9.19"
|
"node-api-dotnet": "^0.9.19"
|
||||||
},
|
},
|
||||||
@@ -28,6 +27,7 @@
|
|||||||
"@sigma/edge-curve": "^3.1.0",
|
"@sigma/edge-curve": "^3.1.0",
|
||||||
"@sigma/node-border": "^3.0.0",
|
"@sigma/node-border": "^3.0.0",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
|
"@tanstack/vue-query": "^5.92.9",
|
||||||
"@tanstack/vue-table": "^8.21.3",
|
"@tanstack/vue-table": "^8.21.3",
|
||||||
"@tanstack/vue-virtual": "^3.13.22",
|
"@tanstack/vue-virtual": "^3.13.22",
|
||||||
"@types/node": "^24.12.0",
|
"@types/node": "^24.12.0",
|
||||||
@@ -421,6 +421,7 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -430,6 +431,7 @@
|
|||||||
"version": "7.28.5",
|
"version": "7.28.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -463,6 +465,7 @@
|
|||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
|
||||||
"integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
|
"integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.29.0"
|
"@babel/types": "^7.29.0"
|
||||||
@@ -574,6 +577,7 @@
|
|||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.27.1",
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
@@ -2041,6 +2045,7 @@
|
|||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
@@ -2716,6 +2721,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2733,6 +2741,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2750,6 +2761,9 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2767,6 +2781,9 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3453,6 +3470,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3474,6 +3494,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3698,6 +3721,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3715,6 +3741,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3841,6 +3870,7 @@
|
|||||||
"version": "8.19.4",
|
"version": "8.19.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz",
|
||||||
"integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==",
|
"integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"remove-accents": "0.5.0"
|
"remove-accents": "0.5.0"
|
||||||
@@ -3857,6 +3887,7 @@
|
|||||||
"version": "5.90.20",
|
"version": "5.90.20",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz",
|
||||||
"integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==",
|
"integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -3892,6 +3923,7 @@
|
|||||||
"version": "5.92.9",
|
"version": "5.92.9",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/vue-query/-/vue-query-5.92.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/vue-query/-/vue-query-5.92.9.tgz",
|
||||||
"integrity": "sha512-jjAZcqKveyX0C4w/6zUqbnqk/XzuxNWaFsWjGTJWULVFizUNeLGME2gf9vVSDclIyiBhR13oZJPPs6fJgfpIJQ==",
|
"integrity": "sha512-jjAZcqKveyX0C4w/6zUqbnqk/XzuxNWaFsWjGTJWULVFizUNeLGME2gf9vVSDclIyiBhR13oZJPPs6fJgfpIJQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||||
@@ -3917,12 +3949,14 @@
|
|||||||
"version": "6.6.4",
|
"version": "6.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/vue-query/node_modules/vue-demi": {
|
"node_modules/@tanstack/vue-query/node_modules/vue-demi": {
|
||||||
"version": "0.14.10",
|
"version": "0.14.10",
|
||||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||||
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -4465,6 +4499,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz",
|
||||||
"integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==",
|
"integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.0",
|
"@babel/parser": "^7.29.0",
|
||||||
@@ -4478,6 +4513,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz",
|
||||||
"integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==",
|
"integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.5.30",
|
"@vue/compiler-core": "3.5.30",
|
||||||
@@ -4488,6 +4524,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz",
|
||||||
"integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==",
|
"integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.0",
|
"@babel/parser": "^7.29.0",
|
||||||
@@ -4505,6 +4542,7 @@
|
|||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
@@ -4514,6 +4552,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz",
|
||||||
"integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==",
|
"integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.30",
|
"@vue/compiler-dom": "3.5.30",
|
||||||
@@ -4560,6 +4599,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz",
|
||||||
"integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==",
|
"integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.5.30"
|
"@vue/shared": "3.5.30"
|
||||||
@@ -4569,6 +4609,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz",
|
||||||
"integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==",
|
"integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.30",
|
"@vue/reactivity": "3.5.30",
|
||||||
@@ -4579,6 +4620,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz",
|
||||||
"integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==",
|
"integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.30",
|
"@vue/reactivity": "3.5.30",
|
||||||
@@ -4591,6 +4633,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz",
|
||||||
"integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==",
|
"integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.5.30",
|
"@vue/compiler-ssr": "3.5.30",
|
||||||
@@ -4604,6 +4647,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz",
|
||||||
"integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==",
|
"integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vue/test-utils": {
|
"node_modules/@vue/test-utils": {
|
||||||
@@ -5972,6 +6016,7 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/data-urls": {
|
"node_modules/data-urls": {
|
||||||
@@ -6818,6 +6863,7 @@
|
|||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||||
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
@@ -7203,6 +7249,7 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/esutils": {
|
"node_modules/esutils": {
|
||||||
@@ -8812,6 +8859,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -8833,6 +8883,9 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -9402,6 +9455,7 @@
|
|||||||
"version": "3.3.11",
|
"version": "3.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||||
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -10005,6 +10059,7 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
@@ -10061,6 +10116,7 @@
|
|||||||
"version": "8.5.8",
|
"version": "8.5.8",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
|
||||||
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
||||||
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -10305,6 +10361,7 @@
|
|||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
|
||||||
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
|
"integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
@@ -10816,6 +10873,7 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -11460,7 +11518,7 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -11887,6 +11945,7 @@
|
|||||||
"version": "3.5.30",
|
"version": "3.5.30",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz",
|
||||||
"integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==",
|
"integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.30",
|
"@vue/compiler-dom": "3.5.30",
|
||||||
|
|||||||
+1
-1
@@ -47,6 +47,7 @@
|
|||||||
"@sigma/edge-curve": "^3.1.0",
|
"@sigma/edge-curve": "^3.1.0",
|
||||||
"@sigma/node-border": "^3.0.0",
|
"@sigma/node-border": "^3.0.0",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
|
"@tanstack/vue-query": "^5.92.9",
|
||||||
"@tanstack/vue-table": "^8.21.3",
|
"@tanstack/vue-table": "^8.21.3",
|
||||||
"@tanstack/vue-virtual": "^3.13.22",
|
"@tanstack/vue-virtual": "^3.13.22",
|
||||||
"@types/node": "^24.12.0",
|
"@types/node": "^24.12.0",
|
||||||
@@ -184,7 +185,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/vue-query": "^5.92.9",
|
|
||||||
"hazardous": "^0.3.0",
|
"hazardous": "^0.3.0",
|
||||||
"node-api-dotnet": "^0.9.19"
|
"node-api-dotnet": "^0.9.19"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<Avatar v-else class="rounded" :style="{ width: size + 'px', height: size + 'px' }">
|
<Avatar v-else class="rounded" :style="{ width: size + 'px', height: size + 'px' }">
|
||||||
<AvatarImage :src="imageUrl" class="object-cover" />
|
<AvatarImage :src="imageUrl" class="object-cover" />
|
||||||
<AvatarFallback class="rounded">
|
<AvatarFallback class="rounded">
|
||||||
<ImageOff class="size-4 text-muted-foreground" />
|
<Image class="size-4 text-muted-foreground" />
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
import { ImageOff } from 'lucide-vue-next';
|
import { Image } from 'lucide-vue-next';
|
||||||
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
||||||
import { extractFileId, generateEmojiStyle } from '../shared/utils';
|
import { extractFileId, generateEmojiStyle } from '../shared/utils';
|
||||||
|
|||||||
@@ -278,18 +278,26 @@
|
|||||||
<ContextMenuSub>
|
<ContextMenuSub>
|
||||||
<ContextMenuSubTrigger>{{ t('status_bar.clocks') }}</ContextMenuSubTrigger>
|
<ContextMenuSubTrigger>{{ t('status_bar.clocks') }}</ContextMenuSubTrigger>
|
||||||
<ContextMenuSubContent>
|
<ContextMenuSubContent>
|
||||||
<ContextMenuRadioGroup :model-value="String(clockCount)" @update:modelValue="setClockCount">
|
<ContextMenuCheckboxItem
|
||||||
<ContextMenuRadioItem value="0">
|
:model-value="clockCount === 0"
|
||||||
|
@update:model-value="setClockCount('0')">
|
||||||
{{ t('status_bar.clocks_none') }}
|
{{ t('status_bar.clocks_none') }}
|
||||||
</ContextMenuRadioItem>
|
</ContextMenuCheckboxItem>
|
||||||
<ContextMenuRadioItem value="1"> 1 {{ t('status_bar.clock') }} </ContextMenuRadioItem>
|
<ContextMenuCheckboxItem
|
||||||
<ContextMenuRadioItem value="2">
|
:model-value="clockCount === 1"
|
||||||
|
@update:model-value="setClockCount('1')">
|
||||||
|
1 {{ t('status_bar.clock') }}
|
||||||
|
</ContextMenuCheckboxItem>
|
||||||
|
<ContextMenuCheckboxItem
|
||||||
|
:model-value="clockCount === 2"
|
||||||
|
@update:model-value="setClockCount('2')">
|
||||||
2 {{ t('status_bar.clocks_label') }}
|
2 {{ t('status_bar.clocks_label') }}
|
||||||
</ContextMenuRadioItem>
|
</ContextMenuCheckboxItem>
|
||||||
<ContextMenuRadioItem value="3">
|
<ContextMenuCheckboxItem
|
||||||
|
:model-value="clockCount === 3"
|
||||||
|
@update:model-value="setClockCount('3')">
|
||||||
3 {{ t('status_bar.clocks_label') }}
|
3 {{ t('status_bar.clocks_label') }}
|
||||||
</ContextMenuRadioItem>
|
</ContextMenuCheckboxItem>
|
||||||
</ContextMenuRadioGroup>
|
|
||||||
</ContextMenuSubContent>
|
</ContextMenuSubContent>
|
||||||
</ContextMenuSub>
|
</ContextMenuSub>
|
||||||
</ContextMenuContent>
|
</ContextMenuContent>
|
||||||
@@ -302,8 +310,6 @@
|
|||||||
ContextMenu,
|
ContextMenu,
|
||||||
ContextMenuCheckboxItem,
|
ContextMenuCheckboxItem,
|
||||||
ContextMenuContent,
|
ContextMenuContent,
|
||||||
ContextMenuRadioGroup,
|
|
||||||
ContextMenuRadioItem,
|
|
||||||
ContextMenuSeparator,
|
ContextMenuSeparator,
|
||||||
ContextMenuSub,
|
ContextMenuSub,
|
||||||
ContextMenuSubContent,
|
ContextMenuSubContent,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ vi.mock('../ui/avatar', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('lucide-vue-next', () => ({
|
vi.mock('lucide-vue-next', () => ({
|
||||||
ImageOff: { template: '<i data-testid="image-off" />' }
|
Image: { template: '<i data-testid="image" />' }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import Emoji from '../Emoji.vue';
|
import Emoji from '../Emoji.vue';
|
||||||
|
|||||||
@@ -10,11 +10,19 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div style="flex: none; width: 160px; height: 120px">
|
<div style="flex: none; width: 160px; height: 120px">
|
||||||
<img
|
<img
|
||||||
|
v-if="!imageError"
|
||||||
:src="avatarDialog.ref.thumbnailImageUrl"
|
:src="avatarDialog.ref.thumbnailImageUrl"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
@click="showFullscreenImageDialog(avatarDialog.ref.imageUrl)"
|
@click="showFullscreenImageDialog(avatarDialog.ref.imageUrl)"
|
||||||
style="width: 160px; height: 120px; border-radius: var(--radius-xl); object-fit: cover"
|
style="width: 160px; height: 120px; border-radius: var(--radius-xl); object-fit: cover"
|
||||||
|
@error="imageError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="width: 160px; height: 120px; border-radius: var(--radius-xl)">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
||||||
<div style="flex: 1">
|
<div style="flex: 1">
|
||||||
@@ -346,7 +354,16 @@
|
|||||||
:src="imageUrl"
|
:src="imageUrl"
|
||||||
style="width: 100%; height: 100%; object-fit: contain"
|
style="width: 100%; height: 100%; object-fit: contain"
|
||||||
@click="showFullscreenImageDialog(imageUrl)"
|
@click="showFullscreenImageDialog(imageUrl)"
|
||||||
|
@error="
|
||||||
|
$event.target.style.display = 'none';
|
||||||
|
$event.target.nextElementSibling.style.display = 'flex';
|
||||||
|
"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 items-center justify-center bg-muted"
|
||||||
|
style="display: none">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CarouselItem>
|
</CarouselItem>
|
||||||
</CarouselContent>
|
</CarouselContent>
|
||||||
@@ -663,6 +680,14 @@
|
|||||||
|
|
||||||
const treeData = ref({});
|
const treeData = ref({});
|
||||||
const memo = ref('');
|
const memo = ref('');
|
||||||
|
const imageError = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => avatarDialog.value.id,
|
||||||
|
() => {
|
||||||
|
imageError.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
const setAvatarTagsDialog = ref({
|
const setAvatarTagsDialog = ref({
|
||||||
visible: false,
|
visible: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|||||||
@@ -10,12 +10,19 @@
|
|||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
<div style="flex: none; width: 120px; height: 120px">
|
<div style="flex: none; width: 120px; height: 120px">
|
||||||
<img
|
<img
|
||||||
v-if="!groupDialog.loading"
|
v-if="!groupDialog.loading && !imageError"
|
||||||
:src="groupDialog.ref.iconUrl"
|
:src="groupDialog.ref.iconUrl"
|
||||||
style="width: 120px; height: 120px; border-radius: var(--radius-xl)"
|
style="width: 120px; height: 120px; border-radius: var(--radius-xl)"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
@click="showFullscreenImageDialog(groupDialog.ref.iconUrl)"
|
@click="showFullscreenImageDialog(groupDialog.ref.iconUrl)"
|
||||||
|
@error="imageError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else-if="!groupDialog.loading"
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="width: 120px; height: 120px; border-radius: var(--radius-xl)">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
||||||
<div class="group-header" style="flex: 1">
|
<div class="group-header" style="flex: 1">
|
||||||
@@ -374,6 +381,7 @@
|
|||||||
Check,
|
Check,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Eye,
|
Eye,
|
||||||
|
Image,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
@@ -469,6 +477,14 @@
|
|||||||
|
|
||||||
const groupDialogTabCurrentName = ref('0');
|
const groupDialogTabCurrentName = ref('0');
|
||||||
const treeData = ref({});
|
const treeData = ref({});
|
||||||
|
const imageError = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => groupDialog.value.id,
|
||||||
|
() => {
|
||||||
|
imageError.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
const membersTabRef = ref(null);
|
const membersTabRef = ref(null);
|
||||||
const photosTabRef = ref(null);
|
const photosTabRef = ref(null);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<img
|
<img
|
||||||
v-if="!groupDialog.loading"
|
v-if="!groupDialog.loading && !bannerError"
|
||||||
:src="groupDialog.ref.bannerUrl"
|
:src="groupDialog.ref.bannerUrl"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
style="flex: none; width: 100%; aspect-ratio: 6/1; object-fit: cover; border-radius: var(--radius-md)"
|
style="flex: none; width: 100%; aspect-ratio: 6/1; object-fit: cover; border-radius: var(--radius-md)"
|
||||||
@click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)"
|
@click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)"
|
||||||
|
@error="bannerError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else-if="!groupDialog.loading"
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="width: 100%; aspect-ratio: 6/1; border-radius: var(--radius-md)">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-start px-2.5" style="max-height: none">
|
<div class="flex flex-wrap items-start px-2.5" style="max-height: none">
|
||||||
<span v-if="groupDialog.instances.length" class="text-xs font-bold" style="margin: 6px">
|
<span v-if="groupDialog.instances.length" class="text-xs font-bold" style="margin: 6px">
|
||||||
@@ -34,7 +41,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showUserDialog(user.id)">
|
@click="showUserDialog(user.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5" :class="userStatusClass(user)">
|
<div class="relative inline-block flex-none size-9 mr-2.5" :class="userStatusClass(user)">
|
||||||
<img class="size-full rounded-full object-cover" :src="userImage(user)" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="userImage(user)" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -58,6 +70,7 @@
|
|||||||
<span style="display: block" v-text="groupDialog.announcement.title" />
|
<span style="display: block" v-text="groupDialog.announcement.title" />
|
||||||
<div v-if="groupDialog.announcement.imageUrl" style="display: inline-block; margin-right: 6px">
|
<div v-if="groupDialog.announcement.imageUrl" style="display: inline-block; margin-right: 6px">
|
||||||
<img
|
<img
|
||||||
|
v-if="!announcementPhotoError"
|
||||||
:src="groupDialog.announcement.imageUrl"
|
:src="groupDialog.announcement.imageUrl"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
style="
|
style="
|
||||||
@@ -68,7 +81,14 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
"
|
"
|
||||||
@click="showFullscreenImageDialog(groupDialog.announcement.imageUrl)"
|
@click="showFullscreenImageDialog(groupDialog.announcement.imageUrl)"
|
||||||
|
@error="announcementPhotoError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="width: 60px; height: 60px; border-radius: var(--radius-md)">
|
||||||
|
<Image class="size-5 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<pre
|
<pre
|
||||||
class="text-xs font-[inherit]"
|
class="text-xs font-[inherit]"
|
||||||
@@ -340,9 +360,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Copy, Eye, MoreHorizontal } from 'lucide-vue-next';
|
import { Copy, Eye, Image, MoreHorizontal, User } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
@@ -356,14 +378,14 @@
|
|||||||
userStatusClass
|
userStatusClass
|
||||||
} from '../../../shared/utils';
|
} from '../../../shared/utils';
|
||||||
import { refreshInstancePlayerCount } from '../../../coordinators/instanceCoordinator';
|
import { refreshInstancePlayerCount } from '../../../coordinators/instanceCoordinator';
|
||||||
import { useGalleryStore, useGroupStore, useInstanceStore, useLocationStore, useUserStore } from '../../../stores';
|
import { useGalleryStore, useGroupStore, useInstanceStore, useLocationStore } from '../../../stores';
|
||||||
import { useGroupCalendarEvents } from './useGroupCalendarEvents';
|
import { useGroupCalendarEvents } from './useGroupCalendarEvents';
|
||||||
|
|
||||||
import GroupCalendarEventCard from '../../../views/Tools/components/GroupCalendarEventCard.vue';
|
import GroupCalendarEventCard from '../../../views/Tools/components/GroupCalendarEventCard.vue';
|
||||||
import InstanceActionBar from '../../InstanceActionBar.vue';
|
import InstanceActionBar from '../../InstanceActionBar.vue';
|
||||||
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
import { showUserDialog } from '../../../coordinators/userCoordinator';
|
||||||
|
|
||||||
const props = defineProps({
|
defineProps({
|
||||||
showGroupPostEditDialog: {
|
showGroupPostEditDialog: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true
|
required: true
|
||||||
@@ -383,6 +405,17 @@
|
|||||||
|
|
||||||
const { pastCalenderEvents, upcomingCalenderEvents, updateFollowingCalendarData } = useGroupCalendarEvents(groupDialog);
|
const { pastCalenderEvents, upcomingCalenderEvents, updateFollowingCalendarData } = useGroupCalendarEvents(groupDialog);
|
||||||
|
|
||||||
|
const bannerError = ref(false);
|
||||||
|
const announcementPhotoError = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => groupDialog.value.id,
|
||||||
|
() => {
|
||||||
|
bannerError.value = false;
|
||||||
|
announcementPhotoError.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param groupRef
|
* @param groupRef
|
||||||
|
|||||||
@@ -82,7 +82,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showUserDialog(user.userId)">
|
@click="showUserDialog(user.userId)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="userImage(user.user)" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="userImage(user.user)" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -139,7 +144,12 @@
|
|||||||
class="infinite-list-item box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="infinite-list-item box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showUserDialog(user.userId)">
|
@click="showUserDialog(user.userId)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="userImage(user.user)" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="userImage(user.user)" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -201,7 +211,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Download, Eye, MessageSquare, Pencil, RefreshCcw, Tag } from 'lucide-vue-next';
|
import { Download, Eye, MessageSquare, Pencil, RefreshCcw, Tag, User } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { InputGroupField } from '@/components/ui/input-group';
|
import { InputGroupField } from '@/components/ui/input-group';
|
||||||
|
|||||||
@@ -41,11 +41,21 @@
|
|||||||
v-for="image in groupDialog.galleries[gallery.id]"
|
v-for="image in groupDialog.galleries[gallery.id]"
|
||||||
:key="image.id"
|
:key="image.id"
|
||||||
class="p-0 overflow-hidden transition-shadow hover:shadow-md">
|
class="p-0 overflow-hidden transition-shadow hover:shadow-md">
|
||||||
|
<div class="cursor-pointer" @click="showFullscreenImageDialog(image.imageUrl)">
|
||||||
<img
|
<img
|
||||||
:src="image.imageUrl"
|
:src="image.imageUrl"
|
||||||
:class="[' cursor-pointer', 'max-w-full', 'max-h-full']"
|
:class="['max-w-full', 'max-h-full']"
|
||||||
@click="showFullscreenImageDialog(image.imageUrl)"
|
@error="
|
||||||
|
$event.target.style.display = 'none';
|
||||||
|
$event.target.nextElementSibling.style.display = 'flex';
|
||||||
|
"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
class="hidden h-[200px] w-full items-center justify-center bg-muted"
|
||||||
|
style="display: none">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -55,7 +65,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
import { RefreshCw } from 'lucide-vue-next';
|
import { Image, RefreshCw } from 'lucide-vue-next';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { TabsUnderline } from '@/components/ui/tabs';
|
import { TabsUnderline } from '@/components/ui/tabs';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|||||||
@@ -18,18 +18,29 @@
|
|||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span style="display: block" v-text="post.title" />
|
<span style="display: block" v-text="post.title" />
|
||||||
<div v-if="post.imageUrl" style="display: inline-block; margin-right: 6px">
|
<div v-if="post.imageUrl" style="display: inline-block; margin-right: 6px">
|
||||||
|
<div
|
||||||
|
class="cursor-pointer"
|
||||||
|
style="flex: none; width: 60px; height: 60px"
|
||||||
|
@click="showFullscreenImageDialog(post.imageUrl)">
|
||||||
<img
|
<img
|
||||||
:src="post.imageUrl"
|
:src="post.imageUrl"
|
||||||
class="cursor-pointer"
|
|
||||||
style="
|
style="
|
||||||
flex: none;
|
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
"
|
"
|
||||||
@click="showFullscreenImageDialog(post.imageUrl)"
|
@error="
|
||||||
|
$event.target.style.display = 'none';
|
||||||
|
$event.target.nextElementSibling.style.display = 'flex';
|
||||||
|
"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
class="items-center justify-center bg-muted"
|
||||||
|
style="width: 60px; height: 60px; border-radius: var(--radius-md); display: none">
|
||||||
|
<Image class="size-5 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<pre
|
<pre
|
||||||
class="text-xs font-[inherit]"
|
class="text-xs font-[inherit]"
|
||||||
@@ -101,7 +112,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Eye, Pencil, Trash2 } from 'lucide-vue-next';
|
import { Eye, Image, Pencil, Trash2 } from 'lucide-vue-next';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { InputGroupField } from '@/components/ui/input-group';
|
import { InputGroupField } from '@/components/ui/input-group';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
@@ -110,7 +121,7 @@
|
|||||||
import { formatDateFilter, hasGroupPermission } from '../../../shared/utils';
|
import { formatDateFilter, hasGroupPermission } from '../../../shared/utils';
|
||||||
import { useGalleryStore, useGroupStore } from '../../../stores';
|
import { useGalleryStore, useGroupStore } from '../../../stores';
|
||||||
|
|
||||||
const props = defineProps({
|
defineProps({
|
||||||
showGroupPostEditDialog: {
|
showGroupPostEditDialog: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -68,11 +68,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showAvatarDialog(avatar.id)">
|
@click="showAvatarDialog(avatar.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img
|
<Avatar class="size-9">
|
||||||
v-if="avatar.thumbnailImageUrl"
|
<AvatarImage v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" class="object-cover" />
|
||||||
class="size-full rounded-full object-cover"
|
<AvatarFallback>
|
||||||
:src="avatar.thumbnailImageUrl"
|
<Image class="size-4 text-muted-foreground" />
|
||||||
loading="lazy" />
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]" v-text="avatar.name"></span>
|
<span class="block truncate font-medium leading-[18px]" v-text="avatar.name"></span>
|
||||||
@@ -103,7 +104,8 @@
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
import { RefreshCw } from 'lucide-vue-next';
|
import { Image, RefreshCw } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { DataTableEmpty } from '@/components/ui/data-table';
|
import { DataTableEmpty } from '@/components/ui/data-table';
|
||||||
|
|||||||
@@ -45,10 +45,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showWorldDialog(world.id)">
|
@click="showWorldDialog(world.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img
|
<Avatar class="size-9">
|
||||||
class="size-full rounded-full object-cover"
|
<AvatarImage :src="world.thumbnailImageUrl" class="object-cover" />
|
||||||
:src="world.thumbnailImageUrl"
|
<AvatarFallback>
|
||||||
loading="lazy" />
|
<Image class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]" v-text="world.name"></span>
|
<span class="block truncate font-medium leading-[18px]" v-text="world.name"></span>
|
||||||
@@ -68,6 +70,8 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import { Image } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { DataTableEmpty } from '@/components/ui/data-table';
|
import { DataTableEmpty } from '@/components/ui/data-table';
|
||||||
import { TabsUnderline } from '@/components/ui/tabs';
|
import { TabsUnderline } from '@/components/ui/tabs';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
@@ -75,7 +79,7 @@
|
|||||||
|
|
||||||
import DeprecationAlert from '@/components/DeprecationAlert.vue';
|
import DeprecationAlert from '@/components/DeprecationAlert.vue';
|
||||||
|
|
||||||
import { useFavoriteStore, useUserStore, useWorldStore } from '../../../stores';
|
import { useFavoriteStore, useUserStore } from '../../../stores';
|
||||||
import { showWorldDialog } from '../../../coordinators/worldCoordinator';
|
import { showWorldDialog } from '../../../coordinators/worldCoordinator';
|
||||||
import { handleFavoriteWorldList } from '../../../coordinators/favoriteCoordinator';
|
import { handleFavoriteWorldList } from '../../../coordinators/favoriteCoordinator';
|
||||||
import { favoriteRequest } from '../../../api';
|
import { favoriteRequest } from '../../../api';
|
||||||
|
|||||||
@@ -142,7 +142,12 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="group.iconUrl" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="group.iconUrl" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<Users class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
||||||
@@ -247,7 +252,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showGroupDialog(group.id)">
|
@click="showGroupDialog(group.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="group.iconUrl" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="group.iconUrl" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<Users class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
||||||
@@ -283,7 +293,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showGroupDialog(group.id)">
|
@click="showGroupDialog(group.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="group.iconUrl" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="group.iconUrl" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<Users class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
||||||
@@ -330,7 +345,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showGroupDialog(group.id)">
|
@click="showGroupDialog(group.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="group.iconUrl" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="group.iconUrl" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<Users class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
<span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
|
||||||
@@ -361,7 +381,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ArrowDown, ArrowUp, DownloadIcon, Eye, LogOut, RefreshCw, Tag } from 'lucide-vue-next';
|
import { ArrowDown, ArrowUp, DownloadIcon, Eye, LogOut, RefreshCw, Tag, Users } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { nextTick, ref } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -30,10 +30,12 @@
|
|||||||
<div
|
<div
|
||||||
class="relative inline-block flex-none size-9 mr-2.5"
|
class="relative inline-block flex-none size-9 mr-2.5"
|
||||||
:class="userStatusClass(userDialog.$location.user)">
|
:class="userStatusClass(userDialog.$location.user)">
|
||||||
<img
|
<Avatar class="size-9">
|
||||||
class="size-full rounded-full object-cover"
|
<AvatarImage :src="userImage(userDialog.$location.user, true)" class="object-cover" />
|
||||||
:src="userImage(userDialog.$location.user, true)"
|
<AvatarFallback>
|
||||||
loading="lazy" />
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -51,7 +53,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showUserDialog(user.id)">
|
@click="showUserDialog(user.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5" :class="userStatusClass(user)">
|
<div class="relative inline-block flex-none size-9 mr-2.5" :class="userStatusClass(user)">
|
||||||
<img class="size-full rounded-full object-cover" :src="userImage(user, true)" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="userImage(user, true)" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -145,7 +152,9 @@
|
|||||||
:src="userDialog.representedGroup.$thumbnailUrl"
|
:src="userDialog.representedGroup.$thumbnailUrl"
|
||||||
@load="userDialog.isRepresentedGroupLoading = false"
|
@load="userDialog.isRepresentedGroupLoading = false"
|
||||||
@error="userDialog.isRepresentedGroupLoading = false" />
|
@error="userDialog.isRepresentedGroupLoading = false" />
|
||||||
<AvatarFallback class="rounded-lg!" />
|
<AvatarFallback class="rounded-lg!">
|
||||||
|
<Image class="size-5 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
@@ -461,7 +470,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Copy, Info, Languages, MoreHorizontal, Pencil, Trash2 } from 'lucide-vue-next';
|
import { Copy, Image, Info, Languages, MoreHorizontal, Pencil, Trash2, User } from 'lucide-vue-next';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|||||||
@@ -41,7 +41,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showUserDialog(user.id)">
|
@click="showUserDialog(user.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="userImage(user)" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="userImage(user)" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -54,9 +59,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { RefreshCw } from 'lucide-vue-next';
|
import { RefreshCw, User } from 'lucide-vue-next';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|||||||
@@ -59,7 +59,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showWorldDialog(world.id)">
|
@click="showWorldDialog(world.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img class="size-full rounded-full object-cover" :src="world.thumbnailImageUrl" loading="lazy" />
|
<Avatar class="size-9">
|
||||||
|
<AvatarImage :src="world.thumbnailImageUrl" class="object-cover" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<Image class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]" v-text="world.name"></span>
|
<span class="block truncate font-medium leading-[18px]" v-text="world.name"></span>
|
||||||
@@ -79,7 +84,8 @@
|
|||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { DataTableEmpty } from '@/components/ui/data-table';
|
import { DataTableEmpty } from '@/components/ui/data-table';
|
||||||
import { RefreshCw } from 'lucide-vue-next';
|
import { Image, RefreshCw } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|||||||
@@ -4,20 +4,29 @@
|
|||||||
<img
|
<img
|
||||||
v-if="
|
v-if="
|
||||||
!userDialog.loading &&
|
!userDialog.loading &&
|
||||||
|
!profileImageError &&
|
||||||
(userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride)
|
(userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride)
|
||||||
"
|
"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
:src="userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride"
|
:src="userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride"
|
||||||
style="height: 120px; width: 213.33px; border-radius: var(--radius-xl); object-fit: cover"
|
style="height: 120px; width: 213.33px; border-radius: var(--radius-xl); object-fit: cover"
|
||||||
@click="showFullscreenImageDialog(userDialog.ref.profilePicOverride)"
|
@click="showFullscreenImageDialog(userDialog.ref.profilePicOverride)"
|
||||||
|
@error="profileImageError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
<img
|
<img
|
||||||
v-else-if="!userDialog.loading"
|
v-else-if="!userDialog.loading && !profileImageError && userDialog.ref.currentAvatarThumbnailImageUrl"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
:src="userDialog.ref.currentAvatarThumbnailImageUrl"
|
:src="userDialog.ref.currentAvatarThumbnailImageUrl"
|
||||||
style="height: 120px; width: 160px; border-radius: var(--radius-xl); object-fit: cover"
|
style="height: 120px; width: 160px; border-radius: var(--radius-xl); object-fit: cover"
|
||||||
@click="showFullscreenImageDialog(userDialog.ref.currentAvatarImageUrl)"
|
@click="showFullscreenImageDialog(userDialog.ref.currentAvatarImageUrl)"
|
||||||
|
@error="profileImageError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else-if="!userDialog.loading"
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="height: 120px; width: 160px; border-radius: var(--radius-xl)">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
||||||
<div style="flex: 1">
|
<div style="flex: 1">
|
||||||
@@ -232,11 +241,19 @@
|
|||||||
|
|
||||||
<div v-if="userDialog.ref.userIcon" style="flex: none; margin-right: 8px">
|
<div v-if="userDialog.ref.userIcon" style="flex: none; margin-right: 8px">
|
||||||
<img
|
<img
|
||||||
|
v-if="!userIconError"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
:src="userImage(userDialog.ref, true, '256', true)"
|
:src="userImage(userDialog.ref, true, '256', true)"
|
||||||
style="flex: none; width: 120px; height: 120px; border-radius: var(--radius-xl); object-fit: cover"
|
style="flex: none; width: 120px; height: 120px; border-radius: var(--radius-xl); object-fit: cover"
|
||||||
@click="showFullscreenImageDialog(userDialog.ref.userIcon)"
|
@click="showFullscreenImageDialog(userDialog.ref.userIcon)"
|
||||||
|
@error="userIconError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="width: 120px; height: 120px; border-radius: var(--radius-xl)">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UserActionDropdown class="ml-2 mt-12" :user-dialog-command="userDialogCommand" />
|
<UserActionDropdown class="ml-2 mt-12" :user-dialog-command="userDialogCommand" />
|
||||||
@@ -245,7 +262,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Apple, ChevronDown, IdCard, Monitor, Shield, Smartphone, UserPlus, Users } from 'lucide-vue-next';
|
import { Apple, ChevronDown, IdCard, Image, Monitor, Shield, Smartphone, UserPlus, Users } from 'lucide-vue-next';
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
@@ -287,6 +305,17 @@
|
|||||||
|
|
||||||
const { showFullscreenImageDialog } = useGalleryStore();
|
const { showFullscreenImageDialog } = useGalleryStore();
|
||||||
|
|
||||||
|
const profileImageError = ref(false);
|
||||||
|
const userIconError = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => userDialog.value.id,
|
||||||
|
() => {
|
||||||
|
profileImageError.value = false;
|
||||||
|
userIconError.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const getUserStateText = props.getUserStateText;
|
const getUserStateText = props.getUserStateText;
|
||||||
const copyUserDisplayName = props.copyUserDisplayName;
|
const copyUserDisplayName = props.copyUserDisplayName;
|
||||||
const toggleBadgeVisibility = props.toggleBadgeVisibility;
|
const toggleBadgeVisibility = props.toggleBadgeVisibility;
|
||||||
|
|||||||
@@ -10,12 +10,19 @@
|
|||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
<div style="flex: none; width: 160px; height: 120px">
|
<div style="flex: none; width: 160px; height: 120px">
|
||||||
<img
|
<img
|
||||||
v-if="!worldDialog.loading"
|
v-if="!worldDialog.loading && !imageError"
|
||||||
:src="worldDialog.ref.thumbnailImageUrl"
|
:src="worldDialog.ref.thumbnailImageUrl"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
style="width: 160px; height: 120px; border-radius: var(--radius-xl)"
|
style="width: 160px; height: 120px; border-radius: var(--radius-xl)"
|
||||||
@click="showFullscreenImageDialog(worldDialog.ref.imageUrl)"
|
@click="showFullscreenImageDialog(worldDialog.ref.imageUrl)"
|
||||||
|
@error="imageError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else-if="!worldDialog.loading"
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="width: 160px; height: 120px; border-radius: var(--radius-xl)">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
<div class="ml-4" style="flex: 1; display: flex; align-items: flex-start">
|
||||||
<div style="flex: 1">
|
<div style="flex: 1">
|
||||||
@@ -480,6 +487,14 @@
|
|||||||
const treeData = ref({});
|
const treeData = ref({});
|
||||||
const translatedDescription = ref('');
|
const translatedDescription = ref('');
|
||||||
const isTranslating = ref(false);
|
const isTranslating = ref(false);
|
||||||
|
const imageError = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => worldDialog.value.id,
|
||||||
|
() => {
|
||||||
|
imageError.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const isDialogVisible = computed({
|
const isDialogVisible = computed({
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
@@ -53,10 +53,12 @@
|
|||||||
<div
|
<div
|
||||||
class="relative inline-block flex-none size-9 mr-2.5"
|
class="relative inline-block flex-none size-9 mr-2.5"
|
||||||
:class="userStatusClass(room.$location.user)">
|
:class="userStatusClass(room.$location.user)">
|
||||||
<img
|
<Avatar class="size-9">
|
||||||
class="size-full rounded-full object-cover"
|
<AvatarImage :src="userImage(room.$location.user, true)" class="object-cover" />
|
||||||
:src="userImage(room.$location.user, true)"
|
<AvatarFallback>
|
||||||
loading="lazy" />
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -76,10 +78,12 @@
|
|||||||
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
|
||||||
@click="showUserDialog(user.id)">
|
@click="showUserDialog(user.id)">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5" :class="userStatusClass(user)">
|
<div class="relative inline-block flex-none size-9 mr-2.5" :class="userStatusClass(user)">
|
||||||
<img
|
<Avatar class="size-9">
|
||||||
class="size-full rounded-full object-cover"
|
<AvatarImage :src="userImage(user, true)" class="object-cover" />
|
||||||
:src="userImage(user, true)"
|
<AvatarFallback>
|
||||||
loading="lazy" />
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
@@ -104,6 +108,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Check, User } from 'lucide-vue-next';
|
import { Check, User } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|||||||
@@ -291,7 +291,12 @@
|
|||||||
@click="showUserDialog(favorite.id)">
|
@click="showUserDialog(favorite.id)">
|
||||||
<div class="favorites-search-card__content">
|
<div class="favorites-search-card__content">
|
||||||
<div class="favorites-search-card__avatar">
|
<div class="favorites-search-card__avatar">
|
||||||
<img :src="userImage(favorite, true)" loading="lazy" />
|
<Avatar class="size-full">
|
||||||
|
<AvatarImage :src="userImage(favorite, true)" class="object-cover" loading="lazy" />
|
||||||
|
<AvatarFallback>
|
||||||
|
<User class="size-5 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="favorites-search-card__detail">
|
<div class="favorites-search-card__detail">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -325,8 +330,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Ellipsis, MoreHorizontal, Plus, RefreshCcw, RefreshCw } from 'lucide-vue-next';
|
import { Ellipsis, MoreHorizontal, Plus, RefreshCcw, RefreshCw, User } from 'lucide-vue-next';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { DataTableEmpty } from '@/components/ui/data-table';
|
import { DataTableEmpty } from '@/components/ui/data-table';
|
||||||
import { InputGroupField } from '@/components/ui/input-group';
|
import { InputGroupField } from '@/components/ui/input-group';
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
decoding="async"
|
decoding="async"
|
||||||
fetchpriority="low"
|
fetchpriority="low"
|
||||||
class="rounded-sm object-cover" />
|
class="rounded-sm object-cover" />
|
||||||
<AvatarFallback class="rounded-sm">{{ avatarFallback }}</AvatarFallback>
|
<AvatarFallback class="rounded-sm">
|
||||||
|
<Image class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent class="min-w-0">
|
<ItemContent class="min-w-0">
|
||||||
@@ -98,7 +100,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { AlertTriangle, Lock, MoreHorizontal, Trash2 } from 'lucide-vue-next';
|
import { AlertTriangle, Image, Lock, MoreHorizontal, Trash2 } from 'lucide-vue-next';
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
@@ -146,10 +148,6 @@
|
|||||||
|
|
||||||
const localFavFakeRef = computed(() => (props.isLocalFavorite ? props.favorite : props.favorite?.ref));
|
const localFavFakeRef = computed(() => (props.isLocalFavorite ? props.favorite : props.favorite?.ref));
|
||||||
|
|
||||||
const displayName = computed(() => localFavFakeRef.value?.name || props.favorite?.name || props.favorite?.id);
|
|
||||||
|
|
||||||
const avatarFallback = computed(() => displayName.value?.charAt(0)?.toUpperCase() || '?');
|
|
||||||
|
|
||||||
const showUnavailable = computed(() => !props.isLocalFavorite && props.favorite?.deleted);
|
const showUnavailable = computed(() => !props.isLocalFavorite && props.favorite?.deleted);
|
||||||
|
|
||||||
const isPrivateAvatar = computed(() => !props.isLocalFavorite && props.favorite?.ref?.releaseStatus === 'private');
|
const isPrivateAvatar = computed(() => !props.isLocalFavorite && props.favorite?.ref?.releaseStatus === 'private');
|
||||||
|
|||||||
@@ -15,7 +15,9 @@
|
|||||||
decoding="async"
|
decoding="async"
|
||||||
fetchpriority="low"
|
fetchpriority="low"
|
||||||
class="rounded-sm object-cover" />
|
class="rounded-sm object-cover" />
|
||||||
<AvatarFallback class="rounded-sm">{{ avatarFallback }}</AvatarFallback>
|
<AvatarFallback class="rounded-sm">
|
||||||
|
<Image class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent class="min-w-0">
|
<ItemContent class="min-w-0">
|
||||||
@@ -65,7 +67,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { MoreHorizontal } from 'lucide-vue-next';
|
import { Image, MoreHorizontal } from 'lucide-vue-next';
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
@@ -102,8 +104,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const avatarFallback = computed(() => props.favorite.name?.charAt(0)?.toUpperCase() || '?');
|
|
||||||
|
|
||||||
const itemStyle = computed(() => ({
|
const itemStyle = computed(() => ({
|
||||||
padding: 'var(--favorites-card-padding-y, 8px) var(--favorites-card-padding-x, 10px)',
|
padding: 'var(--favorites-card-padding-y, 8px) var(--favorites-card-padding-x, 10px)',
|
||||||
gap: 'var(--favorites-card-content-gap, 10px)',
|
gap: 'var(--favorites-card-content-gap, 10px)',
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
<ItemMedia variant="image">
|
<ItemMedia variant="image">
|
||||||
<Avatar>
|
<Avatar>
|
||||||
<AvatarImage :src="userImage(favorite.ref, true)" loading="lazy" />
|
<AvatarImage :src="userImage(favorite.ref, true)" loading="lazy" />
|
||||||
<AvatarFallback>{{ avatarFallback }}</AvatarFallback>
|
<AvatarFallback>
|
||||||
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent class="min-w-0">
|
<ItemContent class="min-w-0">
|
||||||
@@ -114,7 +116,9 @@
|
|||||||
<Item variant="outline" class="favorites-item hover:bg-muted x-hover-list" :style="itemStyle">
|
<Item variant="outline" class="favorites-item hover:bg-muted x-hover-list" :style="itemStyle">
|
||||||
<ItemMedia variant="image">
|
<ItemMedia variant="image">
|
||||||
<Avatar>
|
<Avatar>
|
||||||
<AvatarFallback>{{ avatarFallback }}</AvatarFallback>
|
<AvatarFallback>
|
||||||
|
<User class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent class="min-w-0">
|
<ItemContent class="min-w-0">
|
||||||
@@ -135,7 +139,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { MoreHorizontal, Trash2 } from 'lucide-vue-next';
|
import { MoreHorizontal, Trash2, User } from 'lucide-vue-next';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import {
|
import {
|
||||||
@@ -195,8 +199,6 @@
|
|||||||
|
|
||||||
const displayName = computed(() => props.favorite?.ref?.displayName || props.favorite?.name || props.favorite?.id);
|
const displayName = computed(() => props.favorite?.ref?.displayName || props.favorite?.name || props.favorite?.id);
|
||||||
|
|
||||||
const avatarFallback = computed(() => displayName.value?.charAt(0)?.toUpperCase() || '?');
|
|
||||||
|
|
||||||
const displayNameStyle = computed(() => {
|
const displayNameStyle = computed(() => {
|
||||||
if (props.favorite?.ref?.$userColour) {
|
if (props.favorite?.ref?.$userColour) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -15,7 +15,9 @@
|
|||||||
decoding="async"
|
decoding="async"
|
||||||
fetchpriority="low"
|
fetchpriority="low"
|
||||||
class="rounded-sm object-cover" />
|
class="rounded-sm object-cover" />
|
||||||
<AvatarFallback class="rounded-sm">{{ avatarFallback }}</AvatarFallback>
|
<AvatarFallback class="rounded-sm">
|
||||||
|
<Image class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent class="min-w-0">
|
<ItemContent class="min-w-0">
|
||||||
@@ -77,7 +79,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { AlertTriangle, Lock, MoreHorizontal } from 'lucide-vue-next';
|
import { AlertTriangle, Image, Lock, MoreHorizontal } from 'lucide-vue-next';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import {
|
import {
|
||||||
@@ -129,8 +131,6 @@
|
|||||||
|
|
||||||
const displayName = computed(() => localFavRef.value?.name || props.favorite?.name || props.favorite?.id);
|
const displayName = computed(() => localFavRef.value?.name || props.favorite?.name || props.favorite?.id);
|
||||||
|
|
||||||
const avatarFallback = computed(() => displayName.value?.charAt(0)?.toUpperCase() || '?');
|
|
||||||
|
|
||||||
const showUnavailable = computed(() => !props.isLocalFavorite && props.favorite?.deleted);
|
const showUnavailable = computed(() => !props.isLocalFavorite && props.favorite?.deleted);
|
||||||
|
|
||||||
const isPrivateWorld = computed(() => localFavRef.value?.releaseStatus === 'private');
|
const isPrivateWorld = computed(() => localFavRef.value?.releaseStatus === 'private');
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<Avatar :style="{ width: `${avatarSize}px`, height: `${avatarSize}px` }">
|
<Avatar :style="{ width: `${avatarSize}px`, height: `${avatarSize}px` }">
|
||||||
<AvatarImage :src="userImage(friend.ref, true)" />
|
<AvatarImage :src="userImage(friend.ref, true)" />
|
||||||
<AvatarFallback>{{ avatarFallback }}</AvatarFallback>
|
<AvatarFallback>
|
||||||
|
<User class="text-muted-foreground" :size="Math.max(16, 20 * cardScale)" />
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
@@ -79,7 +81,7 @@
|
|||||||
} from '@/components/ui/context-menu';
|
} from '@/components/ui/context-menu';
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
import { Pencil } from 'lucide-vue-next';
|
import { Pencil, User } from 'lucide-vue-next';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
@@ -132,7 +134,6 @@
|
|||||||
paddingBottom: `${36 * props.cardScale * props.cardSpacing}px !important`
|
paddingBottom: `${36 * props.cardScale * props.cardSpacing}px !important`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const avatarFallback = computed(() => props.friend?.name?.charAt(0) ?? '?');
|
|
||||||
|
|
||||||
const statusDotClass = computed(() => {
|
const statusDotClass = computed(() => {
|
||||||
const status = userStatusClass(props.friend.ref, props.friend.pendingOffline);
|
const status = userStatusClass(props.friend.ref, props.friend.pendingOffline);
|
||||||
|
|||||||
@@ -92,13 +92,26 @@ export function getColumns({
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const ref = row.original;
|
const ref = row.original;
|
||||||
return (
|
return (
|
||||||
|
<div class="flex items-center">
|
||||||
<img
|
<img
|
||||||
src={ref.thumbnailImageUrl}
|
src={ref.thumbnailImageUrl}
|
||||||
class="avatar-table-thumbnail cursor-pointer rounded-sm object-cover"
|
class="avatar-table-thumbnail cursor-pointer rounded-sm object-cover"
|
||||||
style="width: 34px; height: 22px;"
|
style="width: 34px; height: 22px;"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
onClick={() => onShowAvatarDialog(ref.id)}
|
onClick={() => onShowAvatarDialog(ref.id)}
|
||||||
|
onError={(e) => {
|
||||||
|
e.target.style.display = 'none';
|
||||||
|
e.target.nextElementSibling.style.display = '';
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
class="rounded-sm bg-muted flex items-center justify-center cursor-pointer"
|
||||||
|
style="width: 34px; height: 22px; display: none"
|
||||||
|
onClick={() => onShowAvatarDialog(ref.id)}
|
||||||
|
>
|
||||||
|
<Image class="h-3 w-3 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,13 +9,14 @@
|
|||||||
:class="isActive ? 'border-2 border-primary' : 'border border-border/50'">
|
:class="isActive ? 'border-2 border-primary' : 'border border-border/50'">
|
||||||
<div class="w-full aspect-5/2 overflow-hidden bg-muted relative">
|
<div class="w-full aspect-5/2 overflow-hidden bg-muted relative">
|
||||||
<img
|
<img
|
||||||
v-if="avatar.thumbnailImageUrl"
|
v-if="avatar.thumbnailImageUrl && !imageLoadError"
|
||||||
:src="avatar.thumbnailImageUrl"
|
:src="avatar.thumbnailImageUrl"
|
||||||
:alt="avatar.name"
|
:alt="avatar.name"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
fetchpriority="low"
|
fetchpriority="low"
|
||||||
class="w-full h-full object-cover block" />
|
class="w-full h-full object-cover block"
|
||||||
|
@error="imageLoadError = true" />
|
||||||
<div v-else class="w-full h-full grid place-items-center">
|
<div v-else class="w-full h-full grid place-items-center">
|
||||||
<ImageIcon class="size-6 text-muted-foreground" />
|
<ImageIcon class="size-6 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
@@ -252,6 +253,7 @@
|
|||||||
|
|
||||||
const hoverOpen = ref(false);
|
const hoverOpen = ref(false);
|
||||||
const contextMenuOpen = ref(false);
|
const contextMenuOpen = ref(false);
|
||||||
|
const imageLoadError = ref(false);
|
||||||
|
|
||||||
const handleContextMenuOpen = (open) => {
|
const handleContextMenuOpen = (open) => {
|
||||||
contextMenuOpen.value = open;
|
contextMenuOpen.value = open;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
Ban,
|
Ban,
|
||||||
BellOff,
|
BellOff,
|
||||||
Check,
|
Check,
|
||||||
ImageOff,
|
Image,
|
||||||
Link,
|
Link,
|
||||||
MessageCircle,
|
MessageCircle,
|
||||||
Reply,
|
Reply,
|
||||||
@@ -439,7 +439,7 @@ export const createColumns = ({
|
|||||||
class="object-cover"
|
class="object-cover"
|
||||||
/>
|
/>
|
||||||
<AvatarFallback class="rounded">
|
<AvatarFallback class="rounded">
|
||||||
<ImageOff class="size-4 text-muted-foreground" />
|
<Image class="size-4 text-muted-foreground" />
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
);
|
);
|
||||||
@@ -456,7 +456,7 @@ export const createColumns = ({
|
|||||||
>
|
>
|
||||||
<AvatarImage src={imgUrl} class="object-cover" />
|
<AvatarImage src={imgUrl} class="object-cover" />
|
||||||
<AvatarFallback class="rounded">
|
<AvatarFallback class="rounded">
|
||||||
<ImageOff class="size-4 text-muted-foreground" />
|
<Image class="size-4 text-muted-foreground" />
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,11 +7,19 @@
|
|||||||
style="display: flex; min-height: 120px"
|
style="display: flex; min-height: 120px"
|
||||||
class="mb-7">
|
class="mb-7">
|
||||||
<img
|
<img
|
||||||
|
v-if="!worldImageError"
|
||||||
:src="currentInstanceWorld.ref.thumbnailImageUrl"
|
:src="currentInstanceWorld.ref.thumbnailImageUrl"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
style="flex: none; width: 160px; height: 120px; border-radius: var(--radius-md)"
|
style="flex: none; width: 160px; height: 120px; border-radius: var(--radius-md)"
|
||||||
@click="showFullscreenImageDialog(currentInstanceWorld.ref.imageUrl)"
|
@click="showFullscreenImageDialog(currentInstanceWorld.ref.imageUrl)"
|
||||||
|
@error="worldImageError = true"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex items-center justify-center bg-muted"
|
||||||
|
style="flex: none; width: 160px; height: 120px; border-radius: var(--radius-md)">
|
||||||
|
<Image class="size-8 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
<div class="ml-2" style="display: flex; flex-direction: column; min-width: 320px; width: 100%">
|
<div class="ml-2" style="display: flex; flex-direction: column; min-width: 320px; width: 100%">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span
|
<span
|
||||||
@@ -174,7 +182,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, defineAsyncComponent, onActivated, onMounted, ref, watch } from 'vue';
|
import { computed, defineAsyncComponent, onActivated, onMounted, ref, watch } from 'vue';
|
||||||
import { Apple, Home, Monitor, Smartphone } from 'lucide-vue-next';
|
import { Apple, Home, Image, Monitor, Smartphone } from 'lucide-vue-next';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
@@ -207,6 +215,15 @@
|
|||||||
|
|
||||||
const { lastLocation } = storeToRefs(useLocationStore());
|
const { lastLocation } = storeToRefs(useLocationStore());
|
||||||
const { currentInstanceLocation, currentInstanceWorld, currentInstanceUsersData } = storeToRefs(useInstanceStore());
|
const { currentInstanceLocation, currentInstanceWorld, currentInstanceUsersData } = storeToRefs(useInstanceStore());
|
||||||
|
|
||||||
|
const worldImageError = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => currentInstanceWorld.value?.ref?.id,
|
||||||
|
() => {
|
||||||
|
worldImageError.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
const { getCurrentInstanceUserList } = useInstanceStore();
|
const { getCurrentInstanceUserList } = useInstanceStore();
|
||||||
const { showFullscreenImageDialog } = useGalleryStore();
|
const { showFullscreenImageDialog } = useGalleryStore();
|
||||||
const { currentUser } = storeToRefs(useUserStore());
|
const { currentUser } = storeToRefs(useUserStore());
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Apple,
|
Apple,
|
||||||
ArrowUpDown,
|
ArrowUpDown,
|
||||||
IdCard,
|
IdCard,
|
||||||
|
User,
|
||||||
Monitor,
|
Monitor,
|
||||||
Smartphone
|
Smartphone
|
||||||
} from 'lucide-vue-next';
|
} from 'lucide-vue-next';
|
||||||
@@ -84,7 +85,17 @@ export const createColumns = ({
|
|||||||
src={src}
|
src={src}
|
||||||
class="h-4 w-4 rounded-sm object-cover"
|
class="h-4 w-4 rounded-sm object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
onError={(e) => {
|
||||||
|
e.target.style.display = 'none';
|
||||||
|
e.target.nextElementSibling.style.display = '';
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
class="h-4 w-4 rounded-sm bg-muted flex items-center justify-center"
|
||||||
|
style="display: none"
|
||||||
|
>
|
||||||
|
<User class="h-3 w-3 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
import Location from '@/components/Location.vue';
|
import Location from '@/components/Location.vue';
|
||||||
import Timer from '@/components/Timer.vue';
|
import Timer from '@/components/Timer.vue';
|
||||||
|
|
||||||
import { useAppearanceSettingsStore, useFriendStore, useUserStore } from '../../../stores';
|
import { useAppearanceSettingsStore, useFriendStore } from '../../../stores';
|
||||||
import { useUserDisplay } from '../../../composables/useUserDisplay';
|
import { useUserDisplay } from '../../../composables/useUserDisplay';
|
||||||
|
|
||||||
import '@/styles/status-icon.css';
|
import '@/styles/status-icon.css';
|
||||||
|
|||||||
@@ -36,10 +36,12 @@
|
|||||||
<div
|
<div
|
||||||
class="relative inline-block flex-none size-9 mr-2.5"
|
class="relative inline-block flex-none size-9 mr-2.5"
|
||||||
:class="userStatusClass(currentUser)">
|
:class="userStatusClass(currentUser)">
|
||||||
<img
|
<Avatar class="size-full rounded-full">
|
||||||
class="size-full rounded-full object-cover"
|
<AvatarImage :src="userImage(currentUser)" class="object-cover" />
|
||||||
:src="userImage(currentUser)"
|
<AvatarFallback>
|
||||||
loading="lazy" />
|
<User class="size-5 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden h-9 flex flex-col justify-between">
|
<div class="flex-1 overflow-hidden h-9 flex flex-col justify-between">
|
||||||
<span
|
<span
|
||||||
@@ -176,7 +178,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
||||||
import { ChevronDown } from 'lucide-vue-next';
|
import { ChevronDown, User } from 'lucide-vue-next';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@@ -193,6 +195,7 @@
|
|||||||
ContextMenuSubTrigger,
|
ContextMenuSubTrigger,
|
||||||
ContextMenuTrigger
|
ContextMenuTrigger
|
||||||
} from '../../../components/ui/context-menu';
|
} from '../../../components/ui/context-menu';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '../../../components/ui/avatar';
|
||||||
import {
|
import {
|
||||||
useAdvancedSettingsStore,
|
useAdvancedSettingsStore,
|
||||||
useAppearanceSettingsStore,
|
useAppearanceSettingsStore,
|
||||||
|
|||||||
@@ -37,10 +37,12 @@
|
|||||||
@click="showGroupDialog(item.row.ownerId)">
|
@click="showGroupDialog(item.row.ownerId)">
|
||||||
<template v-if="item.row.isVisible">
|
<template v-if="item.row.isVisible">
|
||||||
<div class="relative inline-block flex-none size-9 mr-2.5">
|
<div class="relative inline-block flex-none size-9 mr-2.5">
|
||||||
<img
|
<Avatar class="size-9">
|
||||||
class="size-full rounded-full object-cover"
|
<AvatarImage :src="getSmallGroupIconUrl(item.row.iconUrl)" class="object-cover" />
|
||||||
:src="getSmallGroupIconUrl(item.row.iconUrl)"
|
<AvatarFallback>
|
||||||
loading="lazy" />
|
<Users class="size-4 text-muted-foreground" />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<span class="block truncate font-medium leading-[18px]">
|
<span class="block truncate font-medium leading-[18px]">
|
||||||
@@ -82,7 +84,8 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||||
import { ChevronDown } from 'lucide-vue-next';
|
import { ChevronDown, Users } from 'lucide-vue-next';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|||||||
@@ -206,7 +206,8 @@ vi.mock('../FriendItem.vue', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('lucide-vue-next', () => ({
|
vi.mock('lucide-vue-next', () => ({
|
||||||
ChevronDown: { template: '<span data-testid="chevron" />' }
|
ChevronDown: { template: '<span data-testid="chevron" />' },
|
||||||
|
User: { template: '<i />' }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import FriendsSidebar from '../FriendsSidebar.vue';
|
import FriendsSidebar from '../FriendsSidebar.vue';
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ vi.mock('../../../../components/Location.vue', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('lucide-vue-next', () => ({
|
vi.mock('lucide-vue-next', () => ({
|
||||||
ChevronDown: { template: '<i />' }
|
ChevronDown: { template: '<i />' },
|
||||||
|
Users: { template: '<i />' }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import GroupsSidebar from '../GroupsSidebar.vue';
|
import GroupsSidebar from '../GroupsSidebar.vue';
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
:class="cardClass"
|
:class="cardClass"
|
||||||
@mouseenter="openEventPopover"
|
@mouseenter="openEventPopover"
|
||||||
@mouseleave="scheduleCloseEventPopover">
|
@mouseleave="scheduleCloseEventPopover">
|
||||||
<img :src="bannerUrl" @click="showFullscreenImageDialog(bannerUrl)" class="banner" />
|
<img v-if="!bannerError" :src="bannerUrl" @click="showFullscreenImageDialog(bannerUrl)" @error="bannerError = true" class="banner" />
|
||||||
|
<div v-else class="banner flex items-center justify-center bg-muted">
|
||||||
|
<Image class="size-6 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
<div class="event-content">
|
<div class="event-content">
|
||||||
<div class="event-title">
|
<div class="event-title">
|
||||||
<div v-if="showGroupName" class="event-group-name" @click="onGroupClick">
|
<div v-if="showGroupName" class="event-group-name" @click="onGroupClick">
|
||||||
@@ -106,7 +109,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Calendar, Download, Share2, Star } from 'lucide-vue-next';
|
import { Calendar, Download, Image, Share2, Star } from 'lucide-vue-next';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -162,6 +165,8 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const bannerError = ref(false);
|
||||||
|
|
||||||
const groupName = computed(() => {
|
const groupName = computed(() => {
|
||||||
if (!props.event) return '';
|
if (!props.event) return '';
|
||||||
return cachedGroups.get(props.event.ownerId)?.name || '';
|
return cachedGroups.get(props.event.ownerId)?.name || '';
|
||||||
|
|||||||
Reference in New Issue
Block a user