Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 150 additions & 7 deletions dist/doboard-widget-bundle.js

Large diffs are not rendered by default.

22 changes: 20 additions & 2 deletions dist/doboard-widget-bundle.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/doboard-widget-bundle.min.js.map

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions js/src/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,15 @@ function addIconPack() {
}

function formatToDotMonthDate(dateString) {
const date = new Date(dateString.replace(' ', 'T'));
if (dateString) {
const date = new Date(dateString.replace(' ', 'T'));

const day = String(date.getDate()).padStart(2, '0');
const month = date.toLocaleString('en-GB', {month: 'short'});
const year = date.getFullYear();
const day = String(date.getDate()).padStart(2, '0');
Comment thread
veronika-tseleva-cleantalk marked this conversation as resolved.
const month = date.toLocaleString('en-GB', {month: 'short'});
const year = date.getFullYear();

return `${day}.${month}.${year}`;
return `${day}.${month}.${year}`;
} else return '';
}

function getSafeUrl(url) {
Expand Down
143 changes: 142 additions & 1 deletion js/src/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -1668,7 +1668,13 @@ class CleanTalkWidgetDoboard {
const attThumbnailUrl = att.URL_thumbnail || att.URL || '#';
const attIcon = SpotFixSVGLoader.getAttachmentIcon(attFilename, attUrl, attThumbnailUrl);
const attIsImage = this.isImageFile(attFilename);
const attClass = attIsImage ? 'image-attachment' : '';
const attIsText = this.isTextFile(attFilename);
let attClass = '';
if (attIsImage) {
attClass = 'image-attachment';
} else if (attIsText) {
attClass = 'text-attachment';
}
attachmentsHTML += this.loadTemplate('concrete_issue_attachment', {
attachmentUrl: attUrl,
attachmentFilename: attFilename,
Expand Down Expand Up @@ -2468,6 +2474,137 @@ class CleanTalkWidgetDoboard {
return imageExtensions.includes(ext);
}

isTextFile(filename) {
if (!filename) return false;
const ext = filename.split('.').pop().toLowerCase();
const textExtensions = [
'js', 'ts', 'json', 'json5', 'yaml', 'yml', 'xml', 'md', 'toml',
'csv', 'html', 'css', 'scss', 'less', 'hbs', 'twig', 'php', 'py',
'java', 'c', 'cpp', 'cs', 'rb', 'rs', 'swift', 'kt', 'pl', 'r',
'lua', 'dockerfile', 'nginx', 'ini', 'bat', 'ps1', 'makefile',
'diff', 'tex', 'log', 'txt'
];
return textExtensions.includes(ext);
}

getAceMode(filename) {
if (!filename) return 'text';
const ext = filename.split('.').pop().toLowerCase();
switch (ext) {
case 'js': return 'javascript';
case 'ts': return 'typescript';
case 'yaml': case 'yml': return 'yaml';
case 'md': return 'markdown';
case 'c': case 'cpp': return 'c_cpp';
case 'cs': return 'csharp';
case 'rb': return 'ruby';
case 'rs': return 'rust';
case 'kt': return 'kotlin';
case 'pl': return 'perl';
case 'bat': return 'batchfile';
case 'ps1': return 'powershell';
case 'tex': return 'latex';
case 'log': case 'txt': return 'text';
default: return ext;
}
}

async loadAceEditor() {
if (window.ace) return Promise.resolve();
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = "https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.3/ace.js";
script.onload = () => {
window.ace.config.set('basePath', 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.3/');
resolve();
};
script.onerror = reject;
document.head.appendChild(script);
});
}

async showTextLightbox(fileUrl, fileName = 'file.txt') {
this.hideImageLightbox();

let fileContent = "Loading...";
try {
const response = await fetch(fileUrl);
if (response.ok) {
fileContent = await response.text();
} else {
fileContent = "Failed to load file.";
}
} catch (e) {
fileContent = "Error fetching file.";
}

const closeIconSrc = this.srcVariables.buttonCloseScreen;

const lightboxHTML = `
<div id="doboard_task_widget-lightbox" class="doboard_task_widget-lightbox-text active">
<div class="doboard_task_widget-lightbox-text-overlay"></div>

<div class="doboard_task_widget-lightbox-text-wrapper">
<button class="doboard_task_widget-lightbox-text-close">
<img src="${closeIconSrc}" alt="Close" />
</button>

<div class="doboard_task_widget-lightbox-text-content">
<div class="doboard_task_widget-lightbox-text-header">
${this.escapeHtml(fileName)}
</div>

<div id="ace-editor-container" class="doboard_task_widget-lightbox-text-editor"></div>
</div>
</div>
</div>
`;

const tempDiv = document.createElement('div');
tempDiv.innerHTML = lightboxHTML;
const lightbox = tempDiv.firstElementChild;
document.body.appendChild(lightbox);

const closeBtn = lightbox.querySelector('.doboard_task_widget-lightbox-text-close');
const overlay = lightbox.querySelector('.doboard_task_widget-lightbox-text-overlay');

const escHandler = (e) => {
if (e.key === 'Escape') {
closeHandler();
}
};
const closeHandler = () => {
this.hideImageLightbox();
document.removeEventListener('keydown', escHandler);
};
if (closeBtn) {
closeBtn.addEventListener('click', closeHandler);
}
if (overlay) {
overlay.addEventListener('click', closeHandler);
}
document.addEventListener('keydown', escHandler);

await this.loadAceEditor();

if (window.ace) {
const editor = window.ace.edit("ace-editor-container");
editor.setTheme("ace/theme/github");
const mode = this.getAceMode(fileName);
editor.session.setMode("ace/mode/" + mode);
editor.setValue(fileContent, -1);
editor.setReadOnly(true);
editor.setShowPrintMargin(false);
} else {
const container = document.getElementById('ace-editor-container');
container.style.overflow = 'auto';
container.style.padding = '16px';
container.style.fontFamily = 'monospace';
container.style.whiteSpace = 'pre-wrap';
container.innerText = fileContent;
}
}

/**
* Show image in lightbox
* @param {string} imageUrl - The image URL to show
Expand Down Expand Up @@ -2540,10 +2677,14 @@ class CleanTalkWidgetDoboard {
e.stopPropagation();
const fileUrl = newItem.getAttribute('data-attachment-url');
const fileName = newItem.querySelector('.doboard_task_widget-attachment_filename')?.textContent || 'file';

const isImage = newItem.classList.contains('image-attachment');
const isText = newItem.classList.contains('text-attachment');

if (isImage && fileUrl) {
this.showImageLightbox(fileUrl, fileName);
} else if (isText && fileUrl) {
this.showTextLightbox(fileUrl, fileName);
} else if (fileUrl) {
this.downloadFile(fileUrl, fileName);
}
Expand Down
88 changes: 88 additions & 0 deletions styles/doboard-widget.css
Original file line number Diff line number Diff line change
Expand Up @@ -2050,3 +2050,91 @@ input:checked + .slider:before {
height: 22px;
border-radius: 50%;
}

.doboard_task_widget-lightbox-text {
display: none;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 999999;
}

.doboard_task_widget-lightbox-text.active {
display: flex;
}

.doboard_task_widget-lightbox-text-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
cursor: pointer;
}

.doboard_task_widget-lightbox-text-wrapper {
position: relative;
width: 80vw;
height: 85vh;
max-width: 1200px;
z-index: 10001;
}

.doboard_task_widget-lightbox-text-close {
position: absolute;
top: -55px;
right: -55px;
cursor: pointer;
border: none;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
padding: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
}

.doboard_task_widget-lightbox-text-close:hover {
background: rgba(255, 255, 255, 0.8);
}

.doboard_task_widget-lightbox-text-close img {
width: 20px;
height: 20px;
}

.doboard_task_widget-lightbox-text-content {
width: 100%;
height: 100%;
background: #fff;
border-radius: 8px;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
}

.doboard_task_widget-lightbox-text-header {
padding: 10px 16px;
background: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
font-family: sans-serif;
font-size: 13px;
color: #555;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.doboard_task_widget-lightbox-text-editor {
flex-grow: 1;
width: 100%;
position: relative;
}
Loading