Add deletion control for attached videos
| @@ -7066,6 +7066,16 @@ | |||
| 7066 | <div class="right_menu_button mes_file_delete fa-solid fa-trash-can" title="Remove the file" data-i18n="[title]Remove the file"></div> | 7066 | <div class="right_menu_button mes_file_delete fa-solid fa-trash-can" title="Remove the file" data-i18n="[title]Remove the file"></div> |
| 7067 | </div> | 7067 | </div> |
| 7068 | </div> | 7068 | </div> |
| 7069 | |||
| 7070 | <div id="message_video_template" class="template_element"> | ||
| 7071 | <div class="mes_video_container"> | ||
| 7072 | <div class="mes_video_controls"> | ||
| 7073 | <div><!-- Placeholder --></div> | ||
| 7074 | <div title="Delete" class="right_menu_button fa-lg fa-solid fa-trash-can mes_video_delete" data-i18n="[title]Delete"></div> | ||
| 7075 | </div> | ||
| 7076 | <video class="mes_video" controls preload="metadata"></video> | ||
| 7077 | </div> | ||
| 7078 | </div> | ||
| 7069 | </div> | 7079 | </div> |
| 7070 | <div id="movingDivs"> | 7080 | <div id="movingDivs"> |
| 7071 | <div id="floatingPrompt" class="drawer-content flexGap5"> | 7081 | <div id="floatingPrompt" class="drawer-content flexGap5"> |
| @@ -2505,16 +2505,11 @@ export function appendMediaToMessage(mes, messageElement, adjustScroll = true) { | |||
| 2505 | 2505 | ||
| 2506 | // Add video to message | 2506 | // Add video to message |
| 2507 | if (mes.extra?.video) { | 2507 | if (mes.extra?.video) { |
| 2508 | const container = messageElement.find('.mes_block'); | 2508 | const container = $('#message_video_template .mes_video_container').clone(); |
| 2509 | messageElement.find('.mes_video_container').remove(); | ||
| 2510 | messageElement.find('.mes_block').append(container); | ||
| 2509 | const chatHeight = $('#chat').prop('scrollHeight'); | 2511 | const chatHeight = $('#chat').prop('scrollHeight'); |
| 2510 | 2512 | const video = container.find('.mes_video'); | |
| 2511 | // Create video element if it doesn't exist | ||
| 2512 | let video = messageElement.find('.mes_video'); | ||
| 2513 | if (video.length === 0) { | ||
| 2514 | video = $('<video class="mes_video" controls preload="metadata"></video>'); | ||
| 2515 | container.append(video); | ||
| 2516 | } | ||
| 2517 | |||
| 2518 | video.off('loadedmetadata').on('loadedmetadata', function () { | 2513 | video.off('loadedmetadata').on('loadedmetadata', function () { |
| 2519 | if (!adjustScroll) { | 2514 | if (!adjustScroll) { |
| 2520 | return; | 2515 | return; |
| @@ -2527,7 +2522,7 @@ export function appendMediaToMessage(mes, messageElement, adjustScroll = true) { | |||
| 2527 | 2522 | ||
| 2528 | video.attr('src', mes.extra?.video); | 2523 | video.attr('src', mes.extra?.video); |
| 2529 | } else { | 2524 | } else { |
| 2530 | messageElement.find('.mes_video').remove(); | 2525 | messageElement.find('.mes_video_container').remove(); |
| 2531 | } | 2526 | } |
| 2532 | 2527 | ||
| 2533 | // Add file to message | 2528 | // Add file to message |
| @@ -893,6 +893,27 @@ async function deleteMessageImage() { | |||
| 893 | await saveChatConditional(); | 893 | await saveChatConditional(); |
| 894 | } | 894 | } |
| 895 | 895 | ||
| 896 | async function deleteMessageVideo() { | ||
| 897 | const confirm = await Popup.show.confirm(t`Delete video from message?`, t`This action can't be undone.`); | ||
| 898 | if (!confirm) { | ||
| 899 | return; | ||
| 900 | } | ||
| 901 | |||
| 902 | const mesBlock = $(this).closest('.mes'); | ||
| 903 | const mesId = mesBlock.attr('mesid'); | ||
| 904 | const message = chat[mesId]; | ||
| 905 | |||
| 906 | if (!message?.extra?.video) { | ||
| 907 | console.warn('Message has no video or it is empty'); | ||
| 908 | return; | ||
| 909 | } | ||
| 910 | |||
| 911 | delete message.extra.video; | ||
| 912 | mesBlock.find('.mes_video_container').remove(); | ||
| 913 | |||
| 914 | await saveChatConditional(); | ||
| 915 | } | ||
| 916 | |||
| 896 | /** | 917 | /** |
| 897 | * Deletes file from the server. | 918 | * Deletes file from the server. |
| 898 | * @param {string} url Path to the file on the server | 919 | * @param {string} url Path to the file on the server |
| @@ -1856,6 +1877,7 @@ export function initChatUtilities() { | |||
| 1856 | $(document).on('click', '.mes_img', expandMessageImage); | 1877 | $(document).on('click', '.mes_img', expandMessageImage); |
| 1857 | $(document).on('click', '.mes_img_enlarge', expandAndZoomMessageImage); | 1878 | $(document).on('click', '.mes_img_enlarge', expandAndZoomMessageImage); |
| 1858 | $(document).on('click', '.mes_img_delete', deleteMessageImage); | 1879 | $(document).on('click', '.mes_img_delete', deleteMessageImage); |
| 1880 | $(document).on('click', '.mes_video_delete', deleteMessageVideo); | ||
| 1859 | 1881 | ||
| 1860 | $('#file_form_input').on('change', async () => { | 1882 | $('#file_form_input').on('change', async () => { |
| 1861 | const fileInput = document.getElementById('file_form_input'); | 1883 | const fileInput = document.getElementById('file_form_input'); |
| @@ -5033,8 +5033,9 @@ a:hover { | |||
| 5033 | display: none; | 5033 | display: none; |
| 5034 | } | 5034 | } |
| 5035 | 5035 | ||
| 5036 | /* Message images */ | 5036 | /* Message images/video */ |
| 5037 | .mes .mes_img_container { | 5037 | .mes .mes_img_container, |
| 5038 | .mes .mes_video_container { | ||
| 5038 | max-width: 100%; | 5039 | max-width: 100%; |
| 5039 | /*to fit inside single window height of mobile landscape*/ | 5040 | /*to fit inside single window height of mobile landscape*/ |
| 5040 | display: none; | 5041 | display: none; |
| @@ -5044,8 +5045,12 @@ a:hover { | |||
| 5044 | padding: 0.5rem; | 5045 | padding: 0.5rem; |
| 5045 | } | 5046 | } |
| 5046 | 5047 | ||
| 5048 | .mes .mes_video_container:has(.mes_video[src]) { | ||
| 5049 | display: flex; | ||
| 5050 | } | ||
| 5051 | |||
| 5047 | .mes_img { | 5052 | .mes_img { |
| 5048 | border-radius: 10px; | 5053 | border-radius: 5px; |
| 5049 | max-width: 100%; | 5054 | max-width: 100%; |
| 5050 | max-height: 40vh; | 5055 | max-height: 40vh; |
| 5051 | image-rendering: -webkit-optimize-contrast; | 5056 | image-rendering: -webkit-optimize-contrast; |
| @@ -5059,7 +5064,8 @@ a:hover { | |||
| 5059 | } | 5064 | } |
| 5060 | 5065 | ||
| 5061 | .mes_img_swipes, | 5066 | .mes_img_swipes, |
| 5062 | .mes_img_controls { | 5067 | .mes_img_controls, |
| 5068 | .mes_video_controls { | ||
| 5063 | position: absolute; | 5069 | position: absolute; |
| 5064 | top: 0.1em; | 5070 | top: 0.1em; |
| 5065 | left: 0; | 5071 | left: 0; |
| @@ -5070,6 +5076,8 @@ a:hover { | |||
| 5070 | justify-content: space-between; | 5076 | justify-content: space-between; |
| 5071 | align-items: center; | 5077 | align-items: center; |
| 5072 | padding: 1em; | 5078 | padding: 1em; |
| 5079 | z-index: 1; | ||
| 5080 | transition: opacity var(--animation-duration) ease-in-out; | ||
| 5073 | } | 5081 | } |
| 5074 | 5082 | ||
| 5075 | .mes_img_swipes { | 5083 | .mes_img_swipes { |
| @@ -5078,7 +5086,8 @@ a:hover { | |||
| 5078 | } | 5086 | } |
| 5079 | 5087 | ||
| 5080 | .mes_img_swipes .right_menu_button, | 5088 | .mes_img_swipes .right_menu_button, |
| 5081 | .mes_img_controls .right_menu_button { | 5089 | .mes_img_controls .right_menu_button, |
| 5090 | .mes_video_controls .right_menu_button { | ||
| 5082 | filter: brightness(90%); | 5091 | filter: brightness(90%); |
| 5083 | text-shadow: 1px 1px var(--SmartThemeShadowColor) !important; | 5092 | text-shadow: 1px 1px var(--SmartThemeShadowColor) !important; |
| 5084 | padding: 1px; | 5093 | padding: 1px; |
| @@ -5087,23 +5096,29 @@ a:hover { | |||
| 5087 | } | 5096 | } |
| 5088 | 5097 | ||
| 5089 | .mes_img_swipes .right_menu_button::before, | 5098 | .mes_img_swipes .right_menu_button::before, |
| 5090 | .mes_img_controls .right_menu_button::before { | 5099 | .mes_img_controls .right_menu_button::before, |
| 5100 | .mes_video_controls .right_menu_button::before { | ||
| 5091 | /* Fix weird alignment with this font-awesome icons on focus */ | 5101 | /* Fix weird alignment with this font-awesome icons on focus */ |
| 5092 | position: relative; | 5102 | position: relative; |
| 5093 | top: 0.6125em; | 5103 | top: 0.6125em; |
| 5094 | } | 5104 | } |
| 5095 | 5105 | ||
| 5096 | .mes_img_swipes .right_menu_button:hover, | 5106 | .mes_img_swipes .right_menu_button:hover, |
| 5097 | .mes_img_controls .right_menu_button:hover { | 5107 | .mes_img_controls .right_menu_button:hover, |
| 5108 | .mes_video_controls .right_menu_button:hover { | ||
| 5098 | filter: brightness(150%); | 5109 | filter: brightness(150%); |
| 5099 | } | 5110 | } |
| 5100 | 5111 | ||
| 5112 | /* It's better to show controls only when the video is not playing */ | ||
| 5113 | /* but :playing pseudo-class is not supported in all browsers */ | ||
| 5114 | /* .mes_video_container:focus-within .mes_video_controls:not(:has(.mes_video:playing)), */ | ||
| 5101 | .mes_img_container:has(.mes_img.error) .mes_img_swipes, | 5115 | .mes_img_container:has(.mes_img.error) .mes_img_swipes, |
| 5102 | .mes_img_container:has(.mes_img.error) .mes_img_controls, | 5116 | .mes_img_container:has(.mes_img.error) .mes_img_controls, |
| 5103 | .mes_img_container:hover .mes_img_swipes, | 5117 | .mes_img_container:hover .mes_img_swipes, |
| 5104 | .mes_img_container:focus-within .mes_img_swipes, | 5118 | .mes_img_container:focus-within .mes_img_swipes, |
| 5105 | .mes_img_container:hover .mes_img_controls, | 5119 | .mes_img_container:hover .mes_img_controls, |
| 5106 | .mes_img_container:focus-within .mes_img_controls { | 5120 | .mes_img_container:focus-within .mes_img_controls, |
| 5121 | .mes_video_container:hover .mes_video_controls { | ||
| 5107 | opacity: 1; | 5122 | opacity: 1; |
| 5108 | } | 5123 | } |
| 5109 | 5124 | ||
| @@ -5203,9 +5218,8 @@ body:not(.sd) .mes_img_swipes { | |||
| 5203 | .mes_video { | 5218 | .mes_video { |
| 5204 | max-width: 100%; | 5219 | max-width: 100%; |
| 5205 | max-height: 400px; | 5220 | max-height: 400px; |
| 5206 | border-radius: 8px; | 5221 | border-radius: 5px; |
| 5207 | background: #000; | 5222 | background: #000; |
| 5208 | margin: 0.5rem; | ||
| 5209 | } | 5223 | } |
| 5210 | 5224 | ||
| 5211 | /* Ensure video controls are visible */ | 5225 | /* Ensure video controls are visible */ |