mirror of
https://codeberg.org/SafeTwitch/safetwitch.git
synced 2025-01-03 03:10:04 -05:00
Merge branch 'master' into master
This commit is contained in:
commit
744163c182
33 changed files with 431 additions and 119 deletions
|
@ -1,7 +1,11 @@
|
||||||
steps:
|
steps:
|
||||||
build:
|
lint:
|
||||||
image: docker.io/node:16
|
image: docker.io/node:16
|
||||||
commands:
|
commands:
|
||||||
- git clone --recurse-submodules -j8 https://codeberg.org/dragongoose/safetwitch
|
- git clone --recurse-submodules -j8 https://codeberg.org/dragongoose/safetwitch
|
||||||
- npm i
|
- npm i
|
||||||
|
- npm run lint
|
||||||
|
build:
|
||||||
|
image: docker.io/node:16
|
||||||
|
commands:
|
||||||
- npm run build
|
- npm run build
|
23
README.md
23
README.md
|
@ -1,4 +1,7 @@
|
||||||
# SafeTwitch
|
# SafeTwitch
|
||||||
|
<a href="https://translate.codeberg.org/engage/safetwitch/">
|
||||||
|
<img src="https://translate.codeberg.org/widget/safetwitch/frontend/svg-badge.svg" alt="Translation status" />
|
||||||
|
</a>
|
||||||
|
|
||||||
SafeTwitch is a privacy respecting frontend for [twitch.tv](https://twitch.tv/)
|
SafeTwitch is a privacy respecting frontend for [twitch.tv](https://twitch.tv/)
|
||||||
|
|
||||||
|
@ -121,16 +124,16 @@ You can translate here: https://translate.codeberg.org/projects/safetwitch/front
|
||||||
If you host a SafeTwitch instance and would like it to be listed in the readme, please make an issue or a pull request to add it in.
|
If you host a SafeTwitch instance and would like it to be listed in the readme, please make an issue or a pull request to add it in.
|
||||||
|
|
||||||
### Clearnet
|
### Clearnet
|
||||||
| URL | Country | Info |
|
### Clearnet
|
||||||
|---------------------------|---------|---------|
|
| URL | Country | Info | Cloudflare |
|
||||||
| [safetwitch.drgns.space \(Official\)](https://safetwitch.drgns.space/) | 🇺🇸 | Homelab |
|
|---------------------------|---------|---------|------------|
|
||||||
| [safetwitch.projectsegfau.lt](https://safetwitch.projectsegfau.lt/) | 🇺🇸 🇮🇳 🇫🇷 | #2 |
|
| [safetwitch.drgns.space \(Official\)](https://safetwitch.drgns.space/) | 🇺🇸 | Homelab | ❌ |
|
||||||
| [stream.whateveritworks.org](https://stream.whateveritworks.org) | 🇩🇪 | Hosted on Hetzner/Dedicated Server with Encryption at rest |
|
| [safetwitch.projectsegfau.lt](https://safetwitch.projectsegfau.lt/) | 🇺🇸 🇮🇳 🇫🇷 | #2 | ❌ |
|
||||||
| [safetwitch.datura.network](https://safetwitch.datura.network) | 🇩🇪 | #9 |
|
| [stream.whateveritworks.org](https://stream.whateveritworks.org) | 🇩🇪 | Hosted on Hetzner/Dedicated Server with Encryption at rest | ✅ |
|
||||||
| [ttv.vern.cc](https://ttv.vern.cc) | 🇺🇸 | #12 |
|
| [safetwitch.datura.network](https://safetwitch.datura.network) | 🇩🇪 | #9 | ❌ |
|
||||||
| [safetwitch.frontendfriendly.xyz](https://safetwitch.frontendfriendly.xyz/) | 🇺🇸 | #16 |
|
| [ttv.vern.cc](https://ttv.vern.cc) | 🇺🇸 | #12 | ❌ |
|
||||||
| [ttv.femboys.world](https://ttv.femboys.world) | 🇺🇸 | #29 |
|
| [safetwitch.frontendfriendly.xyz](https://safetwitch.frontendfriendly.xyz/) | 🇺🇸 | #16 | ❌ |
|
||||||
| [ttv.asylum.rest](https://ttv.asylum.rest) | 🇺🇸 | #29 |
|
| [ttv.femboy.band](https://ttv.femboy.band) | 🇺🇸 | #29 | ✅ |
|
||||||
|
|
||||||
### Onion
|
### Onion
|
||||||
| URL | Country | Info |
|
| URL | Country | Info |
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<link rel="manifest" href="/site.webmanifest">
|
<link rel="manifest" href="/site.webmanifest">
|
||||||
<title>SafeTwitch</title>
|
<title>SafeTwitch</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-ctp-base flex flex-col">
|
<body class="flex flex-col">
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
131
package-lock.json
generated
131
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "test",
|
"name": "test",
|
||||||
"version": "2.2.1",
|
"version": "2.3.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "test",
|
"name": "test",
|
||||||
"version": "2.2.1",
|
"version": "2.3.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^0.11.0",
|
"@intlify/unplugin-vue-i18n": "^0.11.0",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
|
@ -42,6 +42,7 @@
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
"tailwindcss": "^3.2.7",
|
"tailwindcss": "^3.2.7",
|
||||||
|
"tailwindcss-themer": "^3.1.0",
|
||||||
"typescript": "~4.8.4",
|
"typescript": "~4.8.4",
|
||||||
"vite": "^4.1.4",
|
"vite": "^4.1.4",
|
||||||
"vue-cli-plugin-i18n": "~2.3.2",
|
"vue-cli-plugin-i18n": "~2.3.2",
|
||||||
|
@ -1769,6 +1770,19 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/color": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1",
|
||||||
|
"color-string": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
@ -1786,6 +1800,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/color-string": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
||||||
|
@ -3454,6 +3478,12 @@
|
||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/just-unique": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/just-unique/-/just-unique-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-cxQGGUiit6CGUpuuiezY8N4m1wgF4o7127rXEXDFcxeDUFfdV7gSkwA26Fe2wWBiNQq2SZOgN4gSmMxB/StA8Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/karma-safaritechpreview-launcher": {
|
"node_modules/karma-safaritechpreview-launcher": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/karma-safaritechpreview-launcher/-/karma-safaritechpreview-launcher-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/karma-safaritechpreview-launcher/-/karma-safaritechpreview-launcher-0.0.6.tgz",
|
||||||
|
@ -3567,6 +3597,12 @@
|
||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.mergewith": {
|
||||||
|
"version": "4.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
|
||||||
|
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
|
@ -4758,6 +4794,21 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/slash": {
|
"node_modules/slash": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||||
|
@ -4968,6 +5019,21 @@
|
||||||
"postcss": "^8.0.9"
|
"postcss": "^8.0.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwindcss-themer": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwindcss-themer/-/tailwindcss-themer-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-IfgxpCxWm5rRK3Q7aTvVyhQ/7tyyn8EJl5tFak5tS+/n8oXT7OGfv8praYepR7+IsM92waAuBDZng1HgnstrYA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color": "^4.1.0",
|
||||||
|
"just-unique": "^4.2.0",
|
||||||
|
"lodash.merge": "^4.6.2",
|
||||||
|
"lodash.mergewith": "^4.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/text-table": {
|
"node_modules/text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||||
|
@ -6860,6 +6926,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"color": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^2.0.1",
|
||||||
|
"color-string": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"color-convert": {
|
"color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
@ -6874,6 +6950,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
},
|
},
|
||||||
|
"color-string": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
||||||
|
@ -8094,6 +8180,12 @@
|
||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"just-unique": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/just-unique/-/just-unique-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-cxQGGUiit6CGUpuuiezY8N4m1wgF4o7127rXEXDFcxeDUFfdV7gSkwA26Fe2wWBiNQq2SZOgN4gSmMxB/StA8Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"karma-safaritechpreview-launcher": {
|
"karma-safaritechpreview-launcher": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/karma-safaritechpreview-launcher/-/karma-safaritechpreview-launcher-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/karma-safaritechpreview-launcher/-/karma-safaritechpreview-launcher-0.0.6.tgz",
|
||||||
|
@ -8186,6 +8278,12 @@
|
||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lodash.mergewith": {
|
||||||
|
"version": "4.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
|
||||||
|
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"lru-cache": {
|
"lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
|
@ -8998,6 +9096,23 @@
|
||||||
"object-inspect": "^1.9.0"
|
"object-inspect": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"slash": {
|
"slash": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||||
|
@ -9155,6 +9270,18 @@
|
||||||
"resolve": "^1.22.1"
|
"resolve": "^1.22.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tailwindcss-themer": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwindcss-themer/-/tailwindcss-themer-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-IfgxpCxWm5rRK3Q7aTvVyhQ/7tyyn8EJl5tFak5tS+/n8oXT7OGfv8praYepR7+IsM92waAuBDZng1HgnstrYA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color": "^4.1.0",
|
||||||
|
"just-unique": "^4.2.0",
|
||||||
|
"lodash.merge": "^4.6.2",
|
||||||
|
"lodash.mergewith": "^4.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"text-table": {
|
"text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "test",
|
"name": "test",
|
||||||
"version": "2.3.0",
|
"version": "2.3.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "run-p type-check build-only",
|
"build": "run-p type-check build-only",
|
||||||
|
@ -47,6 +47,7 @@
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
"tailwindcss": "^3.2.7",
|
"tailwindcss": "^3.2.7",
|
||||||
|
"tailwindcss-themer": "^3.1.0",
|
||||||
"typescript": "~4.8.4",
|
"typescript": "~4.8.4",
|
||||||
"vite": "^4.1.4",
|
"vite": "^4.1.4",
|
||||||
"vue-cli-plugin-i18n": "~2.3.2",
|
"vue-cli-plugin-i18n": "~2.3.2",
|
||||||
|
|
16
src/App.vue
16
src/App.vue
|
@ -7,13 +7,15 @@ import DevWarning from './components/DevWarning.vue'
|
||||||
const dev = import.meta.env.DEV
|
const dev = import.meta.env.DEV
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template class="bg-ctp-base h-full flex flex-row">
|
<template class="h-full flex flex-row">
|
||||||
<dev-warning v-if="dev"></dev-warning>
|
<div class="my-theme bg-primary">
|
||||||
<navbar-item></navbar-item>
|
<dev-warning v-if="dev"></dev-warning>
|
||||||
|
<navbar-item></navbar-item>
|
||||||
|
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<RouterView :key="$route.fullPath" />
|
<RouterView :key="$route.fullPath"/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
<footer-item></footer-item>
|
<footer-item></footer-item>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -13,9 +13,7 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
masterManifestUrl: {
|
masterManifestUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
required: true,
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['PlayerTimeUpdate'],
|
emits: ['PlayerTimeUpdate'],
|
||||||
|
|
|
@ -24,8 +24,8 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-ctp-crust w-40 lg:w-[11rem] md:w-[13.5rem] rounded-lg">
|
<div class="bg-crust w-40 lg:w-[11rem] md:w-[13.5rem] rounded-lg">
|
||||||
<router-link :to="`/directory/game/${category.name}`">
|
<router-link :to="`/directory/game/${encodeURIComponent(category.name)}`">
|
||||||
<img :src="category.image" class="rounded-lg rounded-b-none w-full" />
|
<img :src="category.image" class="rounded-lg rounded-b-none w-full" />
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ export default {
|
||||||
<ul class="h-8 overflow-hidden">
|
<ul class="h-8 overflow-hidden">
|
||||||
<li v-for="tag in category.tags" :key="tag" class="inline-flex">
|
<li v-for="tag in category.tags" :key="tag" class="inline-flex">
|
||||||
<span
|
<span
|
||||||
class="p-2.5 py-1.5 bg-ctp-surface0 rounded-md m-0.5 text-xs font-bold text-white"
|
class="p-2.5 py-1.5 bg-surface0 rounded-md m-0.5 text-xs font-bold text-white"
|
||||||
>{{ tag }}</span
|
>{{ tag }}</span
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<router-link v-if="channelData" :to="'/' + channelData.username">
|
<router-link v-if="channelData" :to="'/' + channelData.username">
|
||||||
<div class="p-3 rounded-lg bg-ctp-crust w-max max-w-lg max-h-28">
|
<div class="p-3 rounded-lg bg-crust w-max max-w-lg max-h-28">
|
||||||
<div class="inline-flex space-x-3">
|
<div class="inline-flex space-x-3">
|
||||||
<img :src="channelData.pfp" class="rounded-full w-20" />
|
<img :src="channelData.pfp" class="rounded-full w-20" />
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded-lg z-50 flex m-3 h-20 bg-amber-400 border-4 border-amber-600">
|
<div class="rounded-lg z-50 flex m-3 mt-0 h-20 bg-amber-400 border-4 border-amber-600">
|
||||||
<div class="m-auto">
|
<div class="m-auto">
|
||||||
<h1 class="font-bold text-2xl">SafeTwitch is currently in development mode.</h1>
|
<h1 class="font-bold text-2xl">SafeTwitch is currently in development mode.</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,7 @@ export default {}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col max-w-prose justify-center text-center mx-auto p-6 bg-ctp-crust rounded-lg text-white"
|
class="flex flex-col max-w-prose justify-center text-center mx-auto p-6 bg-crust rounded-lg text-white"
|
||||||
>
|
>
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<h1 class="font-bold text-5xl">{{ $t('error.oops') }}</h1>
|
<h1 class="font-bold text-5xl">{{ $t('error.oops') }}</h1>
|
||||||
|
|
|
@ -56,7 +56,7 @@ export default {
|
||||||
<button
|
<button
|
||||||
ref="followButton"
|
ref="followButton"
|
||||||
@click="followStreamer"
|
@click="followStreamer"
|
||||||
class="text-white text-sm font-bold p-2 py-1 rounded-md bg-purple-600"
|
class="text-white text-sm font-bold p-2 py-1 rounded-md bg-purple"
|
||||||
>
|
>
|
||||||
<v-icon name="bi-heart-fill" scale="0.85"></v-icon>
|
<v-icon name="bi-heart-fill" scale="0.85"></v-icon>
|
||||||
<span v-if="isFollowing"> {{ $t('streamer.unfollow') }} </span>
|
<span v-if="isFollowing"> {{ $t('streamer.unfollow') }} </span>
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { inject } from 'vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex mx-auto justify-center bg-ctp-crust rounded-lg w-2/3 p-2 text-white">
|
<div class="flex mx-auto justify-center bg-crust rounded-lg w-2/3 p-2 text-white">
|
||||||
<div class="flex space-x-3">
|
<div class="flex space-x-3">
|
||||||
<h1 class="text-4xl font-bold">{{ $t('main.searching') }}</h1>
|
<h1 class="text-4xl font-bold">{{ $t('main.searching') }}</h1>
|
||||||
<v-icon name="fa-circle-notch" class="animate-spin w-10 h-10"></v-icon>
|
<v-icon name="fa-circle-notch" class="animate-spin w-10 h-10"></v-icon>
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import SearchBar from './SearchBar.vue'
|
import SearchBar from './SearchBar.vue'
|
||||||
import LanguageSwitcher from './LanguageSwitcher.vue'
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SearchBar,
|
SearchBar
|
||||||
LanguageSwitcher
|
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return {
|
||||||
|
@ -30,7 +28,7 @@ export default {
|
||||||
<nav class="flex items-center justify-between flex-wrap p-4">
|
<nav class="flex items-center justify-between flex-wrap p-4">
|
||||||
<div class="flex items-center flex-no-shrink text-white mr-6">
|
<div class="flex items-center flex-no-shrink text-white mr-6">
|
||||||
<router-link to="/">
|
<router-link to="/">
|
||||||
<h1 class="font-bold text-2xl">Safe<color class="text-purple-500">Twitch</color></h1>
|
<h1 class="font-bold text-2xl">Safe<color class="text-purple">Twitch</color></h1>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -58,7 +56,7 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="open ? 'block' : 'hidden'" class="w-full flex-grow">
|
<div :class="open ? 'block' : 'hidden'" class="w-full flex-grow">
|
||||||
<div class="p-4 flex flex-col items-center space-y-5 bg-ctp-base text-white">
|
<div class="p-4 flex flex-col items-center space-y-5 text-white">
|
||||||
<search-bar></search-bar>
|
<search-bar></search-bar>
|
||||||
<ul class="inline-flex space-x-3 md:space-x-6 font-medium">
|
<ul class="inline-flex space-x-3 md:space-x-6 font-medium">
|
||||||
<a href="https://codeberg.org/dragongoose/safetwitch">{{ $t('nav.code') }}</a>
|
<a href="https://codeberg.org/dragongoose/safetwitch">{{ $t('nav.code') }}</a>
|
||||||
|
|
|
@ -59,7 +59,7 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="data.stream">
|
<div v-if="data.stream">
|
||||||
<div class="bg-ctp-crust rounded-lg w-[23rem] md:w-[27rem]">
|
<div class="bg-crust rounded-lg w-[23rem] md:w-[27rem]">
|
||||||
<RouterLink :to="'/' + data.login">
|
<RouterLink :to="'/' + data.login">
|
||||||
<img :src="data.stream.preview" class="rounded-lg rounded-b-none" />
|
<img :src="data.stream.preview" class="rounded-lg rounded-b-none" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
|
@ -150,7 +150,7 @@ export default {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="p-3 bg-ctp-crust rounded-lg w-[99vw] md:max-w-[15.625rem] lg:max-w-[20rem] flex flex-col" @PlayerTimeUpdate="updateVodComments">
|
<div class="p-3 bg-crust rounded-lg w-[99vw] md:max-w-[15.625rem] lg:max-w-[20rem] flex flex-col" @PlayerTimeUpdate="updateVodComments">
|
||||||
|
|
||||||
<!-- SYSTEM MESSAGES -->
|
<!-- SYSTEM MESSAGES -->
|
||||||
<ul
|
<ul
|
||||||
|
@ -176,7 +176,7 @@ export default {
|
||||||
|
|
||||||
<strong
|
<strong
|
||||||
:style="message.data.color? `color: ${message.data.color};` : ``"
|
:style="message.data.color? `color: ${message.data.color};` : ``"
|
||||||
class="text-ctp-pink font-bold">
|
class="text-purple font-bold">
|
||||||
{{ message.data.username }}</strong>: {{ message.data.message }}
|
{{ message.data.username }}</strong>: {{ message.data.message }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,7 +185,7 @@ export default {
|
||||||
<p class="text-sm text-gray-500 italic"> {{ $t("chat.removed", { username: message.data.username }) }} </p>
|
<p class="text-sm text-gray-500 italic"> {{ $t("chat.removed", { username: message.data.username }) }} </p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="message.type === 'USERNOTICE'" class="text-white inline-flex bg-ctp-pink bg-opacity-50 p-1 rounded-md">
|
<div v-else-if="message.type === 'USERNOTICE'" class="text-white inline-flex bg-pink bg-opacity-50 p-1 rounded-md">
|
||||||
<p> {{ $t("chat.resub", { username: message.data.username, duration : message.data.months }) }} </p>
|
<p> {{ $t("chat.resub", { username: message.data.username, duration : message.data.months }) }} </p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
import qualityLevels from 'videojs-contrib-quality-levels'
|
import qualityLevels from 'videojs-contrib-quality-levels'
|
||||||
import { createQualitySelector } from '@/assets/qualitySelector'
|
import { createQualitySelector } from '@/assets/qualitySelector'
|
||||||
|
import { getTimeFromQuery } from '@/mixins'
|
||||||
import 'video.js/dist/video-js.css'
|
import 'video.js/dist/video-js.css'
|
||||||
|
|
||||||
videojs.registerPlugin('qualityLevels', qualityLevels)
|
videojs.registerPlugin('qualityLevels', qualityLevels)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'VideoJsPlayer',
|
|
||||||
props: {
|
props: {
|
||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -23,7 +23,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['PlayerTimeUpdate'],
|
emits: ['PlayerTimeUpdate'],
|
||||||
data() {
|
setup() {
|
||||||
let player: any
|
let player: any
|
||||||
return {
|
return {
|
||||||
player
|
player
|
||||||
|
@ -36,6 +36,12 @@ export default {
|
||||||
this.player = videojs('video-player', this.options, () => {
|
this.player = videojs('video-player', this.options, () => {
|
||||||
createQualitySelector(this.player)
|
createQualitySelector(this.player)
|
||||||
|
|
||||||
|
if (this.$route.query['t']) {
|
||||||
|
const timeQuery = this.$route.query['t'].toString() || ""
|
||||||
|
const time = getTimeFromQuery(timeQuery)
|
||||||
|
this.player.currentTime(time)
|
||||||
|
}
|
||||||
|
|
||||||
this.player.on('timeupdate', () => {
|
this.player.on('timeupdate', () => {
|
||||||
emit('PlayerTimeUpdate', this.player.currentTime())
|
emit('PlayerTimeUpdate', this.player.currentTime())
|
||||||
})
|
})
|
||||||
|
|
107
src/components/popups/ShareButtonPopup.vue
Normal file
107
src/components/popups/ShareButtonPopup.vue
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const route = useRoute()
|
||||||
|
const instanceUrl = location.protocol + '//' + location.host;
|
||||||
|
let currentUrl = ref(instanceUrl)
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: route.fullPath,
|
||||||
|
usingTwitchUrl: ref(false),
|
||||||
|
usingTime: ref(false),
|
||||||
|
query: ref(""),
|
||||||
|
instanceUrl,
|
||||||
|
currentUrl
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['close'],
|
||||||
|
props: {
|
||||||
|
time: {
|
||||||
|
type: Number,
|
||||||
|
default() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
useTime: {
|
||||||
|
type: Boolean,
|
||||||
|
default() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatSecondsToQuery(sec: number) {
|
||||||
|
const date = new Date(sec * 1000)
|
||||||
|
const hour = date.getHours()
|
||||||
|
const minutes = date.getMinutes()
|
||||||
|
const seconds = date.getSeconds()
|
||||||
|
|
||||||
|
return `${hour}h${minutes}m${seconds}s`
|
||||||
|
},
|
||||||
|
toggleTwitchUrl() {
|
||||||
|
if (this.usingTwitchUrl) {
|
||||||
|
this.currentUrl = "https://twitch.tv"
|
||||||
|
} else {
|
||||||
|
this.currentUrl = this.instanceUrl
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async copyUrl() {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(this.currentUrl + this.path + this.query);
|
||||||
|
console.log('Content copied to clipboard');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy: ', err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleTime() {
|
||||||
|
if (this.usingTime) {
|
||||||
|
const timestamp = this.formatSecondsToQuery(this.$props.time)
|
||||||
|
this.query = "?t=" + timestamp
|
||||||
|
} else {
|
||||||
|
this.query = ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gotoUrl() {
|
||||||
|
location.href = this.currentUrl + this.path + this.query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="fixed top-0 bottom-0 left-0 right-0 flex w-full z-50 h-[100vh] bg-opacity-50 bg-black">
|
||||||
|
<div class="bg-ctp-crust my-auto h-min mx-auto w-[35rem] max-w-[95vw] p-5 rounded-md relative z-50 text-white">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<h1 class="text-3xl font-bold">Share</h1>
|
||||||
|
<button @click="$emit('close')">
|
||||||
|
<v-icon name="io-close-sharp" scale="1.8"></v-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<hr class="my-2" />
|
||||||
|
<div class="flex bg-ctp-surface0 p-3 rounded-md h-12 overflow-x-scroll whitespace-nowrap">
|
||||||
|
<p class="" ref="urlPreview">
|
||||||
|
{{ currentUrl + path + query }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ul class="mt-1">
|
||||||
|
<li>
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input :disabled="!useTime" class="align-middle w-4 h-4 mr-1 disabled:opacity-50" type="checkbox" @change="toggleTime()" v-model="usingTime" /> Add Timestamp
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input class="align-middle w-4 h-4 mr-1" @change="toggleTwitchUrl()" v-model="usingTwitchUrl" type="checkbox" /> Twitch URL
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="space-x-2 mt-1">
|
||||||
|
<button class="p-2 py-1.5 bg-ctp-surface0 rounded-md" @click="copyUrl()">Copy Link</button>
|
||||||
|
<button class="p-2 py-1.5 bg-ctp-surface0 rounded-md" @click="gotoUrl()" >Go to</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -28,7 +28,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="getSetting('streamerAboutSectionVisible')" class="bg-ctp-mantle mt-1 p-5 pt-3 rounded-lg w-full space-y-3">
|
<div v-if="getSetting('streamerAboutSectionVisible')" class="bg-primary mt-1 p-5 pt-3 rounded-lg w-full space-y-3">
|
||||||
<div class="inline-flex w-full">
|
<div class="inline-flex w-full">
|
||||||
<span class="pr-3 font-bold text-3xl">{{ $t('streamer.about') }}</span>
|
<span class="pr-3 font-bold text-3xl">{{ $t('streamer.about') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,14 +37,15 @@ export default {
|
||||||
|
|
||||||
<hr class="my-auto w-full bg-gray-200 rounded-full opacity-40" />
|
<hr class="my-auto w-full bg-gray-200 rounded-full opacity-40" />
|
||||||
|
|
||||||
<ul class="flex font-semibold text-md justify-start flex-wrap flex-row">
|
<ul v-if="socials" class="flex font-semibold text-md justify-start flex-wrap flex-row">
|
||||||
<li v-if="socials" v-for="link in socials" :key="link.url">
|
<li v-for="link in socials" :key="link.url">
|
||||||
<a :href="link.url" class="text-white hover:text-gray-400 mr-4 flex">
|
<a :href="link.url" class="text-white hover:text-gray-400 mr-4 flex">
|
||||||
<v-icon :name="getIconName(link.type)" class="w-6 h-6 mr-1"></v-icon>
|
<v-icon :name="getIconName(link.type)" class="w-6 h-6 mr-1"></v-icon>
|
||||||
<span>{{ link.name }}</span>
|
<span>{{ link.name }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<p v-else> No socials provided </p>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<p v-else> No socials provided </p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -8,12 +8,12 @@
|
||||||
<img :src="videoData.preview" class="rounded-md" width="300" />
|
<img :src="videoData.preview" class="rounded-md" width="300" />
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<p
|
<p
|
||||||
class="absolute bottom-2 right-2 bg-black p-1 py-0.5 rounded-md bg-opacity-70 text-xs font-bold"
|
class="absolute bottom-2 right-2 bg-secondary bg-opacity-70 p-1 py-0.5 rounded-md text-xs font-bold"
|
||||||
>
|
>
|
||||||
{{ new Date(videoData.duration * 1000).toISOString().slice(11, 19) }}
|
{{ new Date(videoData.duration * 1000).toISOString().slice(11, 19) }}
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
class="absolute bottom-2 left-2 bg-black p-1 py-0.5 rounded-md bg-opacity-70 text-xs font-bold"
|
class="absolute bottom-2 left-2 bg-secondary bg-opacity-70 p-1 py-0.5 rounded-md text-xs font-bold"
|
||||||
>
|
>
|
||||||
{{ abbreviate(videoData.views) }} {{ $t('main.views') }}
|
{{ abbreviate(videoData.views) }} {{ $t('main.views') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-ctp-mantle mt-1 p-5 pt-3 rounded-lg w-full space-y-3">
|
<div class="bg-primary mt-1 p-5 pt-3 rounded-lg w-full space-y-3">
|
||||||
<div class="inline-flex w-full">
|
<div class="inline-flex w-full">
|
||||||
<span class="pr-3 font-bold text-3xl">{{ $t('streamer.videos') }}</span>
|
<span class="pr-3 font-bold text-3xl">{{ $t('streamer.videos') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit d5da365783e6c1241a58fa7e8cd7a9b8cb3e60ac
|
Subproject commit b0f6c8230dceb847d2df2eb79cdd272aa2cd85f4
|
|
@ -33,6 +33,8 @@ import {
|
||||||
BiTiktok,
|
BiTiktok,
|
||||||
BiReddit,
|
BiReddit,
|
||||||
BiHeartFill,
|
BiHeartFill,
|
||||||
|
FaShareAlt,
|
||||||
|
IoCloseSharp,
|
||||||
IoPerson
|
IoPerson
|
||||||
} from 'oh-vue-icons/icons'
|
} from 'oh-vue-icons/icons'
|
||||||
|
|
||||||
|
@ -49,6 +51,8 @@ addIcons(
|
||||||
BiTiktok,
|
BiTiktok,
|
||||||
BiReddit,
|
BiReddit,
|
||||||
BiHeartFill,
|
BiHeartFill,
|
||||||
|
FaShareAlt,
|
||||||
|
IoCloseSharp,
|
||||||
IoPerson
|
IoPerson
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -39,4 +39,16 @@ export async function getEndpoint(endpoint: string) {
|
||||||
const data = rawData.data
|
const data = rawData.data
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTimeFromQuery(query: string) {
|
||||||
|
// H, M, S
|
||||||
|
const x = query.split(/[^0-9.]/g);
|
||||||
|
const times = x.map(Number)
|
||||||
|
|
||||||
|
let time = 0
|
||||||
|
time += times[0] * 3600
|
||||||
|
time += times[1] * 60
|
||||||
|
time += times[2]
|
||||||
|
return time
|
||||||
}
|
}
|
|
@ -53,7 +53,7 @@ export function getDefaultSettings() {
|
||||||
|
|
||||||
export function syncUserSettings() {
|
export function syncUserSettings() {
|
||||||
const defaultSettings = getDefaultSettings()
|
const defaultSettings = getDefaultSettings()
|
||||||
let userSettings = localStorage.getItem('settings')
|
const userSettings = localStorage.getItem('settings')
|
||||||
if (!userSettings) return
|
if (!userSettings) return
|
||||||
const parsedUserSettings = JSON.parse(userSettings)
|
const parsedUserSettings = JSON.parse(userSettings)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await getEndpoint('api/discover/' + this.$route.params.game)
|
await getEndpoint('api/discover/' + encodeURIComponent(this.$route.params.game.toString()))
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.status = 'error'
|
this.status = 'error'
|
||||||
})
|
})
|
||||||
|
@ -99,7 +99,7 @@ export default {
|
||||||
<ul class="mb-5">
|
<ul class="mb-5">
|
||||||
<li v-for="tag in data.tags" :key="tag" class="inline-flex">
|
<li v-for="tag in data.tags" :key="tag" class="inline-flex">
|
||||||
<span
|
<span
|
||||||
class="text-white p-1 py-0.5 mr-1 text-sm font-bold bg-ctp-overlay1 rounded-sm"
|
class="text-white p-1 py-0.5 mr-1 text-sm font-bold bg-overlay1 rounded-sm"
|
||||||
>{{ tag }}</span
|
>{{ tag }}</span
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
|
@ -126,7 +126,7 @@ export default {
|
||||||
<ul class="mb-5">
|
<ul class="mb-5">
|
||||||
<li v-for="tag in data.tags" :key="tag" class="inline-flex">
|
<li v-for="tag in data.tags" :key="tag" class="inline-flex">
|
||||||
<span
|
<span
|
||||||
class="text-white p-1 py-0.5 mr-1 text-sm font-bold bg-ctp-overlay1 rounded-sm"
|
class="text-white p-1 py-0.5 mr-1 text-sm font-bold bg-overlay1 rounded-sm"
|
||||||
>{{ tag }}</span
|
>{{ tag }}</span
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, inject } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
import VideoPlayer from '@/components/VideoPlayer.vue'
|
import VideoPlayer from '@/components/VideoPlayer.vue'
|
||||||
import TwitchChat from '@/components/TwitchChat.vue'
|
|
||||||
import ErrorMessage from '@/components/ErrorMessage.vue'
|
import ErrorMessage from '@/components/ErrorMessage.vue'
|
||||||
import FollowButton from '@/components/FollowButton.vue'
|
import FollowButton from '@/components/FollowButton.vue'
|
||||||
import LoadingScreen from '@/components/LoadingScreen.vue'
|
import LoadingScreen from '@/components/LoadingScreen.vue'
|
||||||
import AboutTab from '@/components/user/AboutTab.vue'
|
import AboutTab from '@/components/user/AboutTab.vue'
|
||||||
|
import ShareModal from '@/components/popups/ShareButtonPopup.vue'
|
||||||
|
|
||||||
import type { Video } from '@/types'
|
import type { Video } from '@/types'
|
||||||
import { truncate, abbreviate, getEndpoint } from '@/mixins'
|
import { truncate, abbreviate, getEndpoint } from '@/mixins'
|
||||||
import { chatVisible } from '@/settingsManager'
|
import { chatVisible } from '@/settingsManager'
|
||||||
|
|
||||||
interface ChatComponent {
|
|
||||||
updateVodComments: (time: number) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['rootBackendUrl'],
|
inject: ['rootBackendUrl'],
|
||||||
async setup() {
|
async setup() {
|
||||||
|
@ -24,7 +20,6 @@ export default {
|
||||||
const clipSlug = route.params.slug
|
const clipSlug = route.params.slug
|
||||||
const data = ref<Video>()
|
const data = ref<Video>()
|
||||||
const status = ref<'ok' | 'error'>()
|
const status = ref<'ok' | 'error'>()
|
||||||
const rootBackendUrl = inject('rootBackendUrl')
|
|
||||||
|
|
||||||
let srcUrl
|
let srcUrl
|
||||||
await getEndpoint(`api/clips/cliplink/${clipSlug}`)
|
await getEndpoint(`api/clips/cliplink/${clipSlug}`)
|
||||||
|
@ -52,7 +47,9 @@ export default {
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
status,
|
status,
|
||||||
videoOptions
|
videoOptions,
|
||||||
|
time: ref(0),
|
||||||
|
shareModalVisible: ref(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
@ -69,26 +66,25 @@ export default {
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
VideoPlayer,
|
VideoPlayer,
|
||||||
TwitchChat,
|
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
FollowButton,
|
FollowButton,
|
||||||
LoadingScreen,
|
LoadingScreen,
|
||||||
AboutTab
|
AboutTab,
|
||||||
|
ShareModal
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
truncate,
|
truncate,
|
||||||
abbreviate,
|
abbreviate,
|
||||||
handlePlayerTimeUpdate(time: number) {
|
chatVisible,
|
||||||
if (!chatVisible()) return
|
toggleShareModal() {
|
||||||
const chat = this.$refs.chat as ChatComponent
|
this.shareModalVisible = !this.shareModalVisible
|
||||||
chat.updateVodComments(time)
|
}
|
||||||
},
|
|
||||||
chatVisible
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<share-modal v-if="shareModalVisible" :useTime="false" @close="toggleShareModal"></share-modal>
|
||||||
<loading-screen v-if="!data && status != 'error'"></loading-screen>
|
<loading-screen v-if="!data && status != 'error'"></loading-screen>
|
||||||
<error-message v-else-if="status == 'error'"></error-message>
|
<error-message v-else-if="status == 'error'"></error-message>
|
||||||
|
|
||||||
|
@ -97,10 +93,10 @@ export default {
|
||||||
class="w-full justify-center md:inline-flex space-y-4 md:space-y-0 md:space-x-4 md:p-4"
|
class="w-full justify-center md:inline-flex space-y-4 md:space-y-0 md:space-x-4 md:p-4"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex bg-ctp-crust flex-col p-6 rounded-lg w-[99vw] md:max-w-prose md:min-w-[65ch] lg:max-w-[70rem] text-white"
|
class="flex bg-crust flex-col p-6 rounded-lg w-[99vw] md:max-w-prose md:min-w-[65ch] lg:max-w-[70rem] text-white"
|
||||||
>
|
>
|
||||||
<div class="w-full mx-auto rounded-lg mb-5">
|
<div class="w-full mx-auto rounded-lg mb-5">
|
||||||
<video-player :options="videoOptions" @PlayerTimeUpdate="handlePlayerTimeUpdate">
|
<video-player :options="videoOptions">
|
||||||
</video-player>
|
</video-player>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -124,25 +120,22 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pt-2 inline-flex">
|
<div class="flex justify-between items-center">
|
||||||
|
<div class="pt-2 inline-flex">
|
||||||
<follow-button :username="data.streamer.username"></follow-button>
|
<follow-button :username="data.streamer.username"></follow-button>
|
||||||
<p class="align-baseline font-bold ml-3">
|
<p class="align-baseline font-bold ml-3">
|
||||||
{{ abbreviate(data.streamer.followers) }} {{ $t('main.followers') }}
|
{{ abbreviate(data.streamer.followers) }} {{ $t('main.followers') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button @click="toggleShareModal" class="px-2 py-1.5 rounded-lg bg-purple-600">
|
||||||
|
<v-icon name="fa-share-alt"></v-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ABOUT TAB -->
|
<!-- ABOUT TAB -->
|
||||||
<about-tab :socials="data.streamer.socials" :about="data.streamer.about"></about-tab>
|
<about-tab :socials="data.streamer.socials" :about="data.streamer.about"></about-tab>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--
|
|
||||||
<twitch-chat
|
|
||||||
v-if="chatVisible()"
|
|
||||||
:isVod="true"
|
|
||||||
:channelName="data.streamer.login"
|
|
||||||
ref="chat"
|
|
||||||
></twitch-chat>
|
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,7 +3,7 @@ export default {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<article class="prose prose-invert bg-ctp-crust rounded-lg mx-auto p-8 pt-10 text-white">
|
<article class="prose prose-invert bg-crust rounded-lg mx-auto p-8 pt-10 text-white">
|
||||||
<h1>Privacy Policy</h1>
|
<h1>Privacy Policy</h1>
|
||||||
<p>
|
<p>
|
||||||
It's.... kind of empty here.
|
It's.... kind of empty here.
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mx-auto w-[35rem] max-w-[95vw] p-5 py-3 bg-ctp-crust rounded-md text-white">
|
<div class="mx-auto w-[35rem] max-w-[95vw] p-5 py-3 bg-secondary rounded-md text-white">
|
||||||
<h1 class="font-bold text-3xl">{{ $t("nav.settings") }}</h1>
|
<h1 class="font-bold text-3xl">{{ $t("nav.settings") }}</h1>
|
||||||
<hr class="my-2" />
|
<hr class="my-2" />
|
||||||
<ul class="w-full space-y-1">
|
<ul class="w-full space-y-1">
|
||||||
|
@ -77,10 +77,10 @@ export default {
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="space-x-2 mt-3">
|
<div class="space-x-2 mt-3">
|
||||||
<button @click="save" class="bg-ctp-surface0 p-4 py-2 rounded-md">{{ $t('settings.saveButton') }}</button>
|
<button @click="save" class="bg-surface0 p-4 py-2 rounded-md">{{ $t('settings.saveButton') }}</button>
|
||||||
<!-- <button @click="download" class="bg-ctp-surface0 p-4 py-2 rounded-md">Export</button>
|
<!-- <button @click="download" class="bg-surface0 p-4 py-2 rounded-md">Export</button>
|
||||||
<input type="file" @change="handleImport" name="fileinput" ref="fileinput"
|
<input type="file" @change="handleImport" name="fileinput" ref="fileinput"
|
||||||
class="bg-ctp-surface0 p-4 py-2 rounded-md"> -->
|
class="bg-surface0 p-4 py-2 rounded-md"> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -10,6 +10,8 @@ import LoadingScreen from '@/components/LoadingScreen.vue'
|
||||||
import VideoTab from '@/components/user/VideoTab.vue'
|
import VideoTab from '@/components/user/VideoTab.vue'
|
||||||
import AudioPlayer from '@/components/AudioPlayer.vue'
|
import AudioPlayer from '@/components/AudioPlayer.vue'
|
||||||
import AboutTab from '@/components/user/AboutTab.vue'
|
import AboutTab from '@/components/user/AboutTab.vue'
|
||||||
|
import ShareModal from '@/components/popups/ShareButtonPopup.vue'
|
||||||
|
|
||||||
|
|
||||||
import type { StreamerData } from '@/types'
|
import type { StreamerData } from '@/types'
|
||||||
import { truncate, abbreviate, getEndpoint } from '@/mixins'
|
import { truncate, abbreviate, getEndpoint } from '@/mixins'
|
||||||
|
@ -40,7 +42,8 @@ export default {
|
||||||
data,
|
data,
|
||||||
status,
|
status,
|
||||||
videoOptions,
|
videoOptions,
|
||||||
audioOptions
|
audioOptions,
|
||||||
|
shareModalVisible: ref(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
@ -68,18 +71,23 @@ export default {
|
||||||
LoadingScreen,
|
LoadingScreen,
|
||||||
VideoTab,
|
VideoTab,
|
||||||
AudioPlayer,
|
AudioPlayer,
|
||||||
AboutTab
|
AboutTab,
|
||||||
|
ShareModal
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
truncate,
|
truncate,
|
||||||
abbreviate,
|
abbreviate,
|
||||||
chatVisible,
|
chatVisible,
|
||||||
getSetting
|
getSetting,
|
||||||
|
toggleShareModal() {
|
||||||
|
this.shareModalVisible = !this.shareModalVisible
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<share-modal v-if="shareModalVisible" :time="0" :useTime="false" @close="toggleShareModal"></share-modal>
|
||||||
<loading-screen v-if="!data && status != 'error'"></loading-screen>
|
<loading-screen v-if="!data && status != 'error'"></loading-screen>
|
||||||
<error-message v-else-if="status == 'error'"></error-message>
|
<error-message v-else-if="status == 'error'"></error-message>
|
||||||
|
|
||||||
|
@ -88,7 +96,7 @@ export default {
|
||||||
class="w-full justify-center md:inline-flex space-y-4 md:space-y-0 md:space-x-4 md:p-4"
|
class="w-full justify-center md:inline-flex space-y-4 md:space-y-0 md:space-x-4 md:p-4"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex bg-ctp-crust flex-col p-6 rounded-lg w-[99vw] md:max-w-prose md:min-w-[65ch] lg:max-w-[70rem] text-white"
|
class="flex bg-crust flex-col p-6 rounded-lg w-[99vw] md:max-w-prose md:min-w-[65ch] lg:max-w-[70rem] text-white"
|
||||||
>
|
>
|
||||||
<div v-if="data.isLive" class="w-full mx-auto rounded-lg mb-5">
|
<div v-if="data.isLive" class="w-full mx-auto rounded-lg mb-5">
|
||||||
<video-player v-if="Boolean($route.query['audio-only']) === false" :options="videoOptions"> </video-player>
|
<video-player v-if="Boolean($route.query['audio-only']) === false" :options="videoOptions"> </video-player>
|
||||||
|
@ -113,7 +121,7 @@ export default {
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="data.isLive"
|
v-if="data.isLive"
|
||||||
class="absolute flex left-1/2 translate-x-[-50%] whitespace-nowrap uppercase top-16 bg-ctp-red font-bold text-sm p-1.5 py-0.5 rounded-md"
|
class="absolute flex left-1/2 translate-x-[-50%] whitespace-nowrap uppercase top-16 bg-red font-bold text-sm p-1.5 py-0.5 rounded-md"
|
||||||
>{{ $t('main.live') }}</span
|
>{{ $t('main.live') }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -139,7 +147,7 @@ export default {
|
||||||
<div class="flex-col md:inline-flex md:w-1/5 float-right h-full text-right">
|
<div class="flex-col md:inline-flex md:w-1/5 float-right h-full text-right">
|
||||||
<div v-if="!data.isLive" class="w-full">
|
<div v-if="!data.isLive" class="w-full">
|
||||||
<p
|
<p
|
||||||
class="font-bold bg-ctp-mantle p-3 py-2 rounded-lg w-min float-right border-2 border-ctp-red"
|
class="font-bold bg-overlay0 p-3 py-2 rounded-lg w-min float-right border-2 border-red"
|
||||||
>
|
>
|
||||||
OFFLINE
|
OFFLINE
|
||||||
</p>
|
</p>
|
||||||
|
@ -152,7 +160,7 @@ export default {
|
||||||
<li
|
<li
|
||||||
v-for="tag in data.stream!.tags"
|
v-for="tag in data.stream!.tags"
|
||||||
:key="tag"
|
:key="tag"
|
||||||
class="inline-flex bg-ctp-mantle p-1.5 px-2 rounded-md"
|
class="inline-flex bg-overlay0 p-1.5 px-2 rounded-md"
|
||||||
>
|
>
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
</li>
|
</li>
|
||||||
|
@ -160,11 +168,16 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pt-2 inline-flex">
|
<div class="pt-2 space-x-2 items-center inline-flex">
|
||||||
<follow-button :username="data.login"></follow-button>
|
<follow-button :username="data.login"></follow-button>
|
||||||
|
|
||||||
<p class="align-baseline font-bold ml-3">
|
<p class="align-baseline font-bold ml-3">
|
||||||
{{ abbreviate(data.followers) }} {{ $t('main.followers') }}
|
{{ abbreviate(data.followers) }} {{ $t('main.followers') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<button @click="toggleShareModal" class="px-2 py-1.5 rounded-lg bg-purple-600">
|
||||||
|
<v-icon name="fa-share-alt"></v-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ import ErrorMessage from '@/components/ErrorMessage.vue'
|
||||||
import FollowButton from '@/components/FollowButton.vue'
|
import FollowButton from '@/components/FollowButton.vue'
|
||||||
import LoadingScreen from '@/components/LoadingScreen.vue'
|
import LoadingScreen from '@/components/LoadingScreen.vue'
|
||||||
import AboutTab from '@/components/user/AboutTab.vue'
|
import AboutTab from '@/components/user/AboutTab.vue'
|
||||||
|
import ShareModal from '@/components/popups/ShareButtonPopup.vue'
|
||||||
|
|
||||||
|
|
||||||
import type { Video } from '@/types'
|
import type { Video } from '@/types'
|
||||||
import { truncate, abbreviate, getEndpoint } from '@/mixins'
|
import { truncate, abbreviate, getEndpoint } from '@/mixins'
|
||||||
|
@ -22,11 +24,9 @@ export default {
|
||||||
async setup() {
|
async setup() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const vodID = route.params.vodID
|
const vodID = route.params.vodID
|
||||||
const data = ref<Video>()
|
|
||||||
const status = ref<'ok' | 'error'>()
|
|
||||||
const rootBackendUrl = inject('rootBackendUrl')
|
const rootBackendUrl = inject('rootBackendUrl')
|
||||||
const videoOptions = {
|
const videoOptions = {
|
||||||
autoplay: true,
|
autoplay: getSetting('autoplay'),
|
||||||
controls: true,
|
controls: true,
|
||||||
sources: [
|
sources: [
|
||||||
{
|
{
|
||||||
|
@ -36,11 +36,12 @@ export default {
|
||||||
],
|
],
|
||||||
fluid: true
|
fluid: true
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data: ref<Video>(),
|
||||||
status,
|
status: ref<'ok' | 'error'>(),
|
||||||
videoOptions
|
videoOptions,
|
||||||
|
time: ref(0),
|
||||||
|
shareModalVisible: ref(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
@ -60,7 +61,8 @@ export default {
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
FollowButton,
|
FollowButton,
|
||||||
LoadingScreen,
|
LoadingScreen,
|
||||||
AboutTab
|
AboutTab,
|
||||||
|
ShareModal
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
truncate,
|
truncate,
|
||||||
|
@ -68,15 +70,20 @@ export default {
|
||||||
handlePlayerTimeUpdate(time: number) {
|
handlePlayerTimeUpdate(time: number) {
|
||||||
if (!chatVisible()) return
|
if (!chatVisible()) return
|
||||||
const chat = this.$refs.chat as ChatComponent
|
const chat = this.$refs.chat as ChatComponent
|
||||||
|
this.time = time
|
||||||
chat.updateVodComments(time)
|
chat.updateVodComments(time)
|
||||||
},
|
},
|
||||||
chatVisible,
|
chatVisible,
|
||||||
getSetting
|
getSetting,
|
||||||
|
toggleShareModal() {
|
||||||
|
this.shareModalVisible = !this.shareModalVisible
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<share-modal v-if="shareModalVisible" :time="time" :useTime="true" @close="toggleShareModal"></share-modal>
|
||||||
<loading-screen v-if="!data && status != 'error'"></loading-screen>
|
<loading-screen v-if="!data && status != 'error'"></loading-screen>
|
||||||
<error-message v-else-if="status == 'error'"></error-message>
|
<error-message v-else-if="status == 'error'"></error-message>
|
||||||
|
|
||||||
|
@ -85,7 +92,7 @@ export default {
|
||||||
class="w-full justify-center md:inline-flex space-y-4 md:space-y-0 md:space-x-4 md:p-4"
|
class="w-full justify-center md:inline-flex space-y-4 md:space-y-0 md:space-x-4 md:p-4"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex bg-ctp-crust flex-col p-6 rounded-lg w-[99vw] md:max-w-prose md:min-w-[65ch] lg:max-w-[70rem] text-white"
|
class="flex bg-crust flex-col p-6 rounded-lg w-[99vw] md:max-w-prose md:min-w-[65ch] lg:max-w-[70rem] text-white"
|
||||||
>
|
>
|
||||||
<div class="w-full mx-auto rounded-lg mb-5">
|
<div class="w-full mx-auto rounded-lg mb-5">
|
||||||
<video-player :options="videoOptions" @PlayerTimeUpdate="handlePlayerTimeUpdate">
|
<video-player :options="videoOptions" @PlayerTimeUpdate="handlePlayerTimeUpdate">
|
||||||
|
@ -102,7 +109,7 @@ export default {
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<div class="ml-3 content-between">
|
<div class="ml-3 content-between w-5/6">
|
||||||
<router-link :to="'/' + data.streamer.login">
|
<router-link :to="'/' + data.streamer.login">
|
||||||
<h1 class="text-2xl md:text-4xl font-bold">{{ data.streamer.username }}</h1>
|
<h1 class="text-2xl md:text-4xl font-bold">{{ data.streamer.username }}</h1>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -112,11 +119,18 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pt-2 inline-flex">
|
<div class="flex justify-between items-center">
|
||||||
<follow-button :username="data.streamer.login"></follow-button>
|
<div class="pt-2 inline-flex">
|
||||||
<p class="align-baseline font-bold ml-3">
|
<follow-button :username="data.streamer.login"></follow-button>
|
||||||
{{ abbreviate(data.streamer.followers) }} {{ $t('main.followers') }}
|
<p class="align-baseline font-bold ml-3">
|
||||||
</p>
|
{{ abbreviate(data.streamer.followers) }} {{ $t('main.followers') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<button @click="toggleShareModal" class="px-2 py-1.5 rounded-lg bg-purple-600">
|
||||||
|
<v-icon name="fa-share-alt"></v-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -125,7 +139,7 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<twitch-chat
|
<twitch-chat
|
||||||
v-if="getSetting('chatVisible')"
|
v-if="!getSetting('chatVisible')"
|
||||||
:isVod="true"
|
:isVod="true"
|
||||||
:channelName="data.streamer.login"
|
:channelName="data.streamer.login"
|
||||||
ref="chat"
|
ref="chat"
|
||||||
|
|
|
@ -7,9 +7,40 @@ module.exports = {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
require('@catppuccin/tailwindcss')({
|
require('tailwindcss-themer')({
|
||||||
prefix: 'ctp',
|
defaultTheme: {
|
||||||
defaultFlavour: 'mocha'
|
// put the default values of any config you want themed
|
||||||
|
// just as if you were to extend tailwind's theme like normal https://tailwindcss.com/docs/theme#extending-the-default-theme
|
||||||
|
extend: {
|
||||||
|
// colors is used here for demonstration purposes
|
||||||
|
colors: {
|
||||||
|
primary: 'red'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
themes: [
|
||||||
|
{
|
||||||
|
// name your theme anything that could be a valid css class name
|
||||||
|
// remember what you named your theme because you will use it as a class to enable the theme
|
||||||
|
name: 'my-theme',
|
||||||
|
// put any overrides your theme has here
|
||||||
|
// just as if you were to extend tailwind's theme like normal https://tailwindcss.com/docs/theme#extending-the-default-theme
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
"primary": '#141515',
|
||||||
|
"secondary": '#1e1f1f',
|
||||||
|
"overlay0": '#282a2a',
|
||||||
|
"overlay1": '#323434',
|
||||||
|
"surface0": '#393B3B',
|
||||||
|
"surface1": '#3F4242',
|
||||||
|
"crust": '#0C0C0C',
|
||||||
|
"purple": '#D946EF',
|
||||||
|
"red": "#980C0C"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}),
|
}),
|
||||||
require('@tailwindcss/typography'),
|
require('@tailwindcss/typography'),
|
||||||
require("@tailwindcss/forms")
|
require("@tailwindcss/forms")
|
||||||
|
|
Loading…
Reference in a new issue