Skip to content

Commit ea929e5

Browse files
authored
Fix/email body render (#43)
* fix: email body iframe size * fix: iframe placement + bottom margin * fix: dual scrolling, attachments getting overriden * fix: revert back to scrollable page * fix: top whitespace and buggy scroll * fix: white space bug * fix: code style check
1 parent 079cb77 commit ea929e5

File tree

3 files changed

+95
-23
lines changed

3 files changed

+95
-23
lines changed

lib/Controller/ReleasesApiController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,4 @@ public function index(): DataResponse {
7676
}
7777
return new DataResponse($results);
7878
}
79-
}
79+
}

src/filelist.scss

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,45 @@
1818
* You should have received a copy of the GNU Affero General Public License
1919
* along with this program. If not, see <http://www.gnu.org/licenses/>.
2020
*/
21+
22+
// When securemail content is present, disable files-list internal scrolling
23+
// so only the page scrolls. This prevents nested scroll contexts.
24+
.sendent-has-securemail {
25+
overflow: visible !important;
26+
}
27+
28+
// When there are no real files, collapse the files-list entirely
29+
// (hides table headers, empty state, and whitespace).
30+
.sendent-no-files {
31+
.files-list__table,
32+
.files-list__empty,
33+
.empty-content {
34+
display: none !important;
35+
}
36+
}
37+
2138
#sendent-content {
2239
display: block;
23-
margin: 0 0 180px 0;
40+
margin: 0;
41+
padding: 0 16px 48px;
2442
max-width: 100%;
2543
width: 100%;
26-
position: relative;
27-
z-index: 10;
28-
overflow-x: hidden;
44+
box-sizing: border-box;
45+
46+
.sendent-content__header {
47+
margin: 16px 0 8px;
48+
font-size: 16px;
49+
font-weight: bold;
50+
color: var(--color-main-text);
51+
}
2952

3053
iframe {
31-
margin: 10px 10px 0;
32-
width: calc(100% - 20px);
33-
max-width: calc(100% - 20px);
34-
background-color: white;
35-
border: none;
54+
display: block;
55+
width: 100%;
56+
min-height: 120px;
57+
overflow: hidden;
58+
background-color: var(--color-main-background);
59+
border: 1px solid var(--color-border);
60+
border-radius: 8px;
3661
}
3762
}

src/filelist.ts

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class FooterFile {
5858
private source: string,
5959
) {}
6060

61-
public async appendBelowFiles(version: number): Promise<void> {
61+
public async appendBelowFiles(version: number, hasRealFiles: boolean): Promise<void> {
6262
// Skip if a newer render has already been requested
6363
if (version !== renderVersion) return
6464

@@ -72,13 +72,24 @@ class FooterFile {
7272
const container = document.createElement('div')
7373
container.id = CONTENT_ID
7474

75-
// Insert after the file list table (try multiple selectors for NC 28-33)
7675
const anchor = document.querySelector('.files-list__table')
7776
|| document.querySelector('.files-filestable')
7877
|| document.querySelector('#filestable')
7978

8079
if (!anchor) return
8180

81+
const filesList = anchor.closest('.files-list') ?? anchor.parentElement
82+
83+
if (!hasRealFiles && filesList) {
84+
// No real files — hide empty state and table headers
85+
filesList.classList.add('sendent-no-files')
86+
}
87+
88+
// Insert inside files-list, after the table. Mark files-list so we can
89+
// disable its internal scrolling and let only the page scroll.
90+
if (filesList) {
91+
filesList.classList.add('sendent-has-securemail')
92+
}
8293
anchor.insertAdjacentElement('afterend', container)
8394

8495
// Show loading spinner
@@ -98,6 +109,13 @@ class FooterFile {
98109

99110
// Replace loading spinner with iframe
100111
container.innerHTML = ''
112+
113+
// Add a "Message" header so it's clear this is the email body
114+
const header = document.createElement('h3')
115+
header.className = 'sendent-content__header'
116+
header.textContent = 'Message'
117+
container.appendChild(header)
118+
101119
container.appendChild(this.generateIframeElement(content))
102120
} catch (err) {
103121
// eslint-disable-next-line no-console
@@ -127,20 +145,38 @@ class FooterFile {
127145

128146
private generateIframeElement(content: string): HTMLIFrameElement {
129147
const iframe = document.createElement('iframe')
130-
iframe.width = '0'
131-
iframe.height = '0'
132-
iframe.addEventListener('load', () => {
148+
iframe.scrolling = 'no'
149+
150+
const resizeIframe = () => {
133151
const innerHeight = iframe.contentDocument?.documentElement?.scrollHeight
134-
const innerWidth = iframe.contentDocument?.documentElement?.scrollWidth
135-
if (innerHeight) iframe.height = String(innerHeight)
136-
if (innerWidth) iframe.width = String(innerWidth)
152+
if (innerHeight) iframe.style.height = innerHeight + 'px'
153+
}
154+
155+
iframe.addEventListener('load', () => {
156+
resizeIframe()
157+
// Re-measure after images finish loading
158+
const images = iframe.contentDocument?.querySelectorAll('img')
159+
for (const img of Array.from(images ?? [])) {
160+
if (!img.complete) {
161+
img.addEventListener('load', resizeIframe)
162+
img.addEventListener('error', resizeIframe)
163+
}
164+
}
137165
})
138166
iframe.srcdoc = content
139167
return iframe
140168
}
141169

142170
}
143171

172+
/**
173+
* Restores the files-list to its default state.
174+
*/
175+
function restoreFilesList() {
176+
document.querySelector('.sendent-no-files')?.classList.remove('sendent-no-files')
177+
document.querySelector('.sendent-has-securemail')?.classList.remove('sendent-has-securemail')
178+
}
179+
144180
let debounceTimer: ReturnType<typeof setTimeout> | null = null
145181
let renderVersion = 0
146182

@@ -160,18 +196,29 @@ function processFileListDebounced(files: any[]) {
160196
*/
161197
function processFileList(files: any[]) {
162198
const version = ++renderVersion
199+
let securemailFile: any = null
200+
let realFileCount = 0
201+
163202
for (const file of files ?? []) {
164203
const basename = file.basename || file.name
165204
if (file.type === 'file' && basename === FOOTER_NAME) {
166-
// Extract directory from full path (Node.dirname or manual extraction)
167-
const dirPath = file.dirname
168-
?? (file.path ? file.path.substring(0, file.path.lastIndexOf('/')) || '/' : '/')
169-
new FooterFile(basename, dirPath, file.source ?? '').appendBelowFiles(version)
170-
return
205+
securemailFile = file
206+
} else if (file.type === 'file') {
207+
realFileCount++
171208
}
172209
}
210+
211+
if (securemailFile) {
212+
const basename = securemailFile.basename || securemailFile.name
213+
const dirPath = securemailFile.dirname
214+
?? (securemailFile.path ? securemailFile.path.substring(0, securemailFile.path.lastIndexOf('/')) || '/' : '/')
215+
new FooterFile(basename, dirPath, securemailFile.source ?? '').appendBelowFiles(version, realFileCount > 0)
216+
return
217+
}
218+
173219
// No securemail file in this directory — clean up stale preview
174220
document.getElementById(CONTENT_ID)?.remove()
221+
restoreFilesList()
175222
}
176223

177224
/**

0 commit comments

Comments
 (0)