From 0407853837c323428d122a027fbdb574025824db Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Wed, 14 Dec 2022 12:50:50 +0100 Subject: [PATCH] add result VoD links --- app/Http/Controllers/ResultController.php | 6 +- .../2022_12_14_093503_add_result_vod.php | 32 ++++++++++ resources/js/components/common/Icon.js | 3 + resources/js/components/results/Item.js | 64 +++++++++++++++---- resources/js/components/results/ReportForm.js | 54 ++++++++++++---- .../js/components/tournament/Scoreboard.js | 31 ++++++++- resources/js/i18n/de.js | 5 ++ resources/js/i18n/en.js | 5 ++ resources/sass/_variables.scss | 4 +- resources/sass/results.scss | 7 ++ 10 files changed, 181 insertions(+), 30 deletions(-) create mode 100644 database/migrations/2022_12_14_093503_add_result_vod.php diff --git a/app/Http/Controllers/ResultController.php b/app/Http/Controllers/ResultController.php index 57f78a1..ba57c4b 100644 --- a/app/Http/Controllers/ResultController.php +++ b/app/Http/Controllers/ResultController.php @@ -20,6 +20,7 @@ class ResultController extends Controller 'round_id' => 'required|exists:App\\Models\\Round,id', 'time' => 'numeric', 'user_id' => 'required|exists:App\\Models\\User,id', + 'vod' => 'string|url', ]); $round = Round::findOrFail($validatedData['round_id']); @@ -36,7 +37,8 @@ class ResultController extends Controller if (isset($validatedData['forfeit'])) $result->forfeit = $validatedData['forfeit']; if (isset($validatedData['time'])) $result->time = $validatedData['time']; } - $result->comment = $validatedData['comment'] ? $validatedData['comment'] : null; + $result->comment = !empty($validatedData['comment']) ? $validatedData['comment'] : null; + $result->vod = !empty($validatedData['vod']) ? $validatedData['vod'] : null; $result->save(); if ($result->wasChanged()) { @@ -50,7 +52,7 @@ class ResultController extends Controller $request->user(), ); DiscordBotCommand::queueResult($result); - } else if ($result->wasChanged('comment')) { + } else if ($result->wasChanged(['comment', 'vod'])) { Protocol::resultCommented( $round->tournament, $result, diff --git a/database/migrations/2022_12_14_093503_add_result_vod.php b/database/migrations/2022_12_14_093503_add_result_vod.php new file mode 100644 index 0000000..5b6c707 --- /dev/null +++ b/database/migrations/2022_12_14_093503_add_result_vod.php @@ -0,0 +1,32 @@ +text('vod')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('results', function(Blueprint $table) { + $table->dropColumn('vod'); + }); + } +}; diff --git a/resources/js/components/common/Icon.js b/resources/js/components/common/Icon.js index 617ca03..581ce6f 100644 --- a/resources/js/components/common/Icon.js +++ b/resources/js/components/common/Icon.js @@ -79,6 +79,9 @@ Icon.SECOND_PLACE = makePreset('SecondPlaceIcon', 'medal'); Icon.SETTINGS = makePreset('SettingsIcon', 'cog'); Icon.STREAM = makePreset('StreamIcon', ['fab', 'twitch']); Icon.THIRD_PLACE = makePreset('ThirdPlaceIcon', 'award'); +Icon.TWITCH = makePreset('TwitchIcon', ['fab', 'twitch']); Icon.UNLOCKED = makePreset('UnlockedIcon', 'lock-open'); +Icon.VIDEO = makePreset('VideoIcon', 'video'); +Icon.YOUTUBE = makePreset('YoutubeIcon', ['fab', 'youtube']); export default Icon; diff --git a/resources/js/components/results/Item.js b/resources/js/components/results/Item.js index 4b243b3..b8a0ec6 100644 --- a/resources/js/components/results/Item.js +++ b/resources/js/components/results/Item.js @@ -1,13 +1,16 @@ import PropTypes from 'prop-types'; import React, { useState } from 'react'; import { Button } from 'react-bootstrap'; +import { withTranslation } from 'react-i18next'; import DetailDialog from './DetailDialog'; +import Icon from '../common/Icon'; import Box from '../users/Box'; import { getIcon, getTime } from '../../helpers/Result'; import { maySeeResults } from '../../helpers/permissions'; import { findResult } from '../../helpers/User'; import { withUser } from '../../helpers/UserContext'; +import i18n from '../../i18n'; const getClassName = result => { const classNames = ['status']; @@ -22,6 +25,31 @@ const getClassName = result => { return classNames.join(' '); }; +const twitchReg = /^https?:\/\/(www\.)?twitch\.tv/; +const youtubeReg = /^https?:\/\/(www\.)?youtu(\.be|be\.)/; + +const getVoDVariant = result => { + if (!result || !result.vod) return 'outline-secondary'; + if (twitchReg.test(result.vod)) { + return 'twitch'; + } + if (youtubeReg.test(result.vod)) { + return 'outline-youtube'; + } + return 'outline-secondary'; +}; + +const getVoDIcon = result => { + const variant = getVoDVariant(result); + if (variant === 'twitch') { + return ; + } + if (variant === 'outline-youtube') { + return ; + } + return ; +}; + const Item = ({ authUser, round, @@ -33,16 +61,30 @@ const Item = ({ const maySee = maySeeResults(authUser, tournament, round); return
- +
+ + {maySee && result && result.vod ? + + : null} +
setShowDialog(false)} round={round} @@ -64,4 +106,4 @@ Item.propTypes = { }), }; -export default withUser(Item, 'authUser'); +export default withTranslation()(withUser(Item, 'authUser')); diff --git a/resources/js/components/results/ReportForm.js b/resources/js/components/results/ReportForm.js index 9940171..35b7e0f 100644 --- a/resources/js/components/results/ReportForm.js +++ b/resources/js/components/results/ReportForm.js @@ -66,19 +66,39 @@ const ReportForm = ({ : null} - - - {i18n.t('results.comment')} - - - + + {i18n.t('results.vod')} + + {touched.vod && errors.vod ? + + {i18n.t(errors.vod)} + + : + + {i18n.t('results.vodNote')} + + } + + + {i18n.t('results.comment')} + + {onCancel ? @@ -97,6 +117,7 @@ ReportForm.propTypes = { comment: PropTypes.string, forfeit: PropTypes.string, time: PropTypes.string, + vod: PropTypes.string, }), handleBlur: PropTypes.func, handleChange: PropTypes.func, @@ -109,11 +130,13 @@ ReportForm.propTypes = { comment: PropTypes.bool, forfeit: PropTypes.bool, time: PropTypes.bool, + vod: PropTypes.bool, }), values: PropTypes.shape({ comment: PropTypes.string, forfeit: PropTypes.bool, time: PropTypes.string, + vod: PropTypes.string, }), }; @@ -121,7 +144,7 @@ export default withFormik({ displayName: 'ReportForm', enableReinitialize: true, handleSubmit: async (values, actions) => { - const { comment, forfeit, round_id, time, user_id } = values; + const { comment, forfeit, round_id, time, user_id, vod } = values; const { setErrors } = actions; const { onCancel } = actions.props; try { @@ -131,6 +154,7 @@ export default withFormik({ round_id, time: parseTime(time) || 0, user_id, + vod, }); toastr.success(i18n.t('results.reportSuccess')); if (onCancel) { @@ -151,6 +175,7 @@ export default withFormik({ round_id: round.id, time: result && result.time ? formatTime(result) : '', user_id: user.id, + vod: result && result.vod ? result.vod : '', }; }, validationSchema: yup.object().shape({ @@ -160,5 +185,6 @@ export default withFormik({ is: false, then: yup.string().required().time(), }), + vod: yup.string().url(), }), })(withTranslation()(ReportForm)); diff --git a/resources/js/components/tournament/Scoreboard.js b/resources/js/components/tournament/Scoreboard.js index 40ad82f..f13adac 100644 --- a/resources/js/components/tournament/Scoreboard.js +++ b/resources/js/components/tournament/Scoreboard.js @@ -31,6 +31,33 @@ const getPlacementDisplay = participant => { return participant.placement; }; +const twitchReg = /^https?:\/\/(www\.)?twitch\.tv/; +const youtubeReg = /^https?:\/\/(www\.)?youtu(\.be|be\.)/; + +const getStreamVariant = participant => { + if (!participant || !participant.user || !participant.user.stream_link) { + return 'outline-secondary'; + } + if (twitchReg.test(participant.user.stream_link)) { + return 'outline-twitch'; + } + if (youtubeReg.test(participant.user.stream_link)) { + return 'outline-youtube'; + } + return 'outline-secondary'; +}; + +const getStreamIcon = participant => { + const variant = getStreamVariant(participant); + if (variant === 'outline-twitch') { + return ; + } + if (variant === 'outline-youtube') { + return ; + } + return ; +}; + const Scoreboard = ({ tournament, user }) => @@ -55,9 +82,9 @@ const Scoreboard = ({ tournament, user }) => size="sm" target="_blank" title={i18n.t('users.stream')} - variant="outline-twitch" + variant={getStreamVariant(participant)} > - + {getStreamIcon(participant)} : null} diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index a2c4837..f794876 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -300,7 +300,10 @@ export default { SettingsIcon: 'Einstellungen', StreamIcon: 'Stream', ThirdPlaceIcon: 'Dritter Platz', + TwitchIcon: 'Twitch', UnlockedIcon: 'Offen', + YoutubeIcon: 'YouTube', + VideoIcon: 'Video', zelda: { 'big-key': 'Big Key', 'blue-boomerang': 'Boomerang', @@ -423,6 +426,8 @@ export default { round: 'Runde', runner: 'Runner', time: 'Zeit: {{ time }}', + vod: 'VoD', + vodNote: 'Falls ihr euer VoD teilen wollte, gerne hier rein.', }, rounds: { code: 'Code', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index 9ef5916..dbe37f1 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -300,7 +300,10 @@ export default { SettingsIcon: 'Settings', StreamIcon: 'Stream', ThirdPlaceIcon: 'Third Place', + TwitchIcon: 'Twitch', UnlockedIcon: 'Unlocked', + YoutubeIcon: 'YouTube', + VideoIcon: 'Video', zelda: { 'big-key': 'Big Key', 'blue-boomerang': 'Boomerang', @@ -423,6 +426,8 @@ export default { round: 'Round', runner: 'Runner', time: 'Time: {{ time }}', + vod: 'VoD', + vodNote: 'If you want to share your VoD, go ahead.', }, rounds: { code: 'Code', diff --git a/resources/sass/_variables.scss b/resources/sass/_variables.scss index 886d1d2..62d7e8f 100644 --- a/resources/sass/_variables.scss +++ b/resources/sass/_variables.scss @@ -12,9 +12,11 @@ $discord: #5865f2; $gold: #c9b037; $silver: #b4b4b4; $twitch: #6441a5; +$youtube: #ff0000; // Custom variant $custom-colors: ( "discord": $discord, - "twitch": $twitch + "twitch": $twitch, + "youtube": $youtube ); diff --git a/resources/sass/results.scss b/resources/sass/results.scss index 3907e0e..32f604e 100644 --- a/resources/sass/results.scss +++ b/resources/sass/results.scss @@ -40,5 +40,12 @@ background: $secondary; } } + + .vod-link { + margin-top: 1ex; + margin-left: 0.5ex; + padding: 0.5em; + border-radius: 1ex; + } } } -- 2.39.2