Skip to content
Merged
2 changes: 1 addition & 1 deletion src/lib/components/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
color: white;
border-radius: var(--pg-border-radius-sm);
text-decoration: none;
font-weight: var(--pg-font-weight-semibold);
font-weight: var(--pg-font-weight-medium);
font-size: var(--pg-font-size-sm);
transition: opacity 0.2s ease;
white-space: nowrap;
Expand Down
45 changes: 40 additions & 5 deletions src/lib/components/fallback/EmailView.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script>
import { run } from 'svelte/legacy'
import { onMount } from 'svelte'

import * as email from './email'
import { emails, currSelected } from './../fallback/stores.js'
Expand All @@ -9,6 +10,17 @@

let parsed = $state()
let date = $state()
let isDark = $state(false)

onMount(() => {
const html = document.documentElement
isDark = html.classList.contains('dark')
const obs = new MutationObserver(() => {
isDark = html.classList.contains('dark')
})
obs.observe(html, { attributes: true, attributeFilter: ['class'] })
return () => obs.disconnect()
})

run(() => {
if ($currSelected >= 0) {
Expand All @@ -21,6 +33,33 @@
}
}
})

function escapeHtml(s) {
return s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}

// HTML emails carry their own design — leave their styling untouched.
// Plain-text emails have no styling of their own, so mirror the site
// theme so the text stays legible when dark mode is toggled, and use
// a monospace face — that's what real mail clients render text/plain
// in, and ASCII tables / signatures / quoted replies rely on it.
let bodyDoc = $derived.by(() => {
if (!parsed) return ''
if (parsed.html) {
return `<!doctype html><html><body style="margin:1rem">${parsed.html}</body></html>`
}
const bg = isDark ? '#061b2d' : '#ffffff'
const fg = isDark ? '#ffffff' : '#030e17'
const scheme = isDark ? 'dark' : 'light'
const text = escapeHtml(parsed.text ?? '')
const bodyStyle = `margin:1rem;background:${bg};color:${fg};color-scheme:${scheme};font-size:13px;line-height:1.5`
const preStyle =
'margin:0;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace'
return `<!doctype html><html><body style="${bodyStyle}"><pre style="${preStyle}">${text}</pre></body></html>`
})
</script>

{#if parsed}
Expand Down Expand Up @@ -69,11 +108,7 @@
</div>

<div class="email-body">
<iframe
srcdoc={`<style>body{margin:1rem}</style>${parsed.html ?? parsed.text ?? ''}`}
title="Mail message"
sandbox=""
></iframe>
<iframe srcdoc={bodyDoc} title="Mail message" sandbox=""></iframe>
</div>

{#if parsed.attachments && parsed.attachments.length > 0}
Expand Down
14 changes: 10 additions & 4 deletions src/lib/components/fallback/ListView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,23 @@
gap: 0.15rem;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--pg-input-normal);
border-left: 3px solid transparent;
text-align: left;
transition: background 0.15s ease;
transition:
background 0.15s ease,
color 0.15s ease;

&:hover {
background: var(--pg-soft-background);
}

&.selected {
background: var(--pg-general-background);
border-left-color: var(--pg-primary);
background: var(--pg-primary);
color: white;

.email-sender,
.email-date {
color: rgba(255, 255, 255, 0.85);
}
}

&:focus-visible {
Expand Down
2 changes: 2 additions & 0 deletions src/lib/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
"privacy": "Your emails are stored only in your browser. Clear them anytime in settings."
},
"drop": "Drop the \"postguard.encrypted\" attachment here",
"upload": "Upload \"postguard.encrypted\"",
"back": "Back to inbox",
"search": "Search decrypted mails...",
"decrypt": {
"helper": "Decrypt this mail by showing who you are with <i class=\"yivi-web-logo\">Yivi</i>",
Expand Down
2 changes: 2 additions & 0 deletions src/lib/locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@
"privacy": "Je e-mails worden alleen in je browser opgeslagen. Verwijder ze op elk moment via instellingen."
},
"drop": "Selecteer \"postguard.encrypted\" attachment hier",
"upload": "Upload \"postguard.encrypted\"",
"back": "Terug naar inbox",
"search": "Zoek in ontsleutelde mails...",
"decrypt": {
"helper": "Ontsleutel deze email door te laten zien wie je bent met <i class=\"yivi-web-logo\">Yivi</i>",
Expand Down
127 changes: 116 additions & 11 deletions src/routes/(app)/decrypt/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@
currRight = RIGHTMODES.Decrypt
}

function backToList() {
// On mobile the list and reader are stacked single-screen flows;
// resetting both selection and hashMode collapses currRight back to
// Nothing via the effect above, which hides the reader panel.
currSelected.set(-1)
hashMode = false
currRight = RIGHTMODES.Nothing
}

function fromUrlSafeBase64(urlSafe) {
let base64 = urlSafe.replace(/-/g, '+').replace(/_/g, '/')
const pad = base64.length % 4
Expand Down Expand Up @@ -129,7 +138,10 @@
<span>{$_('fallback.extensionPrompt')}</span>
<a href={resolve('/addons/')}>{$_('fallback.extensionLink')}</a>
</div>
<div class="fallback-container">
<div
class="fallback-container"
class:mobile-reading={currRight !== RIGHTMODES.Nothing}
>
<div class="left-panel">
{#if !hashMode}
<label class="upload-area">
Expand All @@ -138,11 +150,16 @@
width="28px"
aria-hidden="true"
/>
<span>{$_('fallback.drop')}</span>
<span class="upload-text upload-text-desktop"
>{$_('fallback.drop')}</span
>
<span class="upload-text upload-text-mobile"
>{$_('fallback.upload')}</span
>
<input
type="file"
onchange={onFile}
aria-label={$_('fallback.drop')}
aria-label={$_('fallback.upload')}
/>
</label>
{/if}
Expand Down Expand Up @@ -180,6 +197,12 @@
</div>

<div class="right-panel">
{#if currRight !== RIGHTMODES.Nothing}
<button class="mobile-back" type="button" onclick={backToList}>
<Icon icon="mdi:arrow-left" width="20px" />
<span>{$_('fallback.back')}</span>
</button>
{/if}
{#if currRight === RIGHTMODES.MailView}
<EmailView />
{:else if currRight === RIGHTMODES.Nothing}
Expand Down Expand Up @@ -242,7 +265,7 @@

a {
color: var(--pg-primary);
font-weight: var(--pg-font-weight-semibold);
font-weight: var(--pg-font-weight-medium);
text-decoration: none;

&:hover {
Expand Down Expand Up @@ -294,6 +317,14 @@
}
}

.upload-text-mobile {
display: none;
}

.mobile-back {
display: none;
}

.search-bar {
display: flex;
align-items: center;
Expand Down Expand Up @@ -377,7 +408,7 @@

h2 {
font-size: var(--pg-font-size-lg);
font-weight: var(--pg-font-weight-semibold);
font-weight: var(--pg-font-weight-medium);
margin: 0 0 0.75rem;
}

Expand Down Expand Up @@ -411,22 +442,96 @@

@media only screen and (max-width: 768px) {
.fallback-page {
height: auto;
min-height: calc(100vh - 52px);
height: calc(100vh - 52px);
min-height: 0;
padding: 0;
}

.extension-banner {
display: none;
}

.upload-area {
flex: 0 0 auto;
flex-direction: row;
padding: 0.75rem 1rem;
margin: 0.75rem 1rem;
border-style: solid;
border-color: var(--pg-primary);
background: var(--pg-primary);
color: white;
order: 3;

&:hover {
color: white;
opacity: 0.9;
}
}

.upload-text-desktop {
display: none;
}

.upload-text-mobile {
display: inline;
}

.search-bar {
order: 1;
padding-top: 0.75rem;
flex: 0 0 auto;
}

.email-list-area {
order: 2;
flex: 1 1 0;
min-height: 0;
}

.fallback-container {
flex-direction: column;
height: auto;
height: 100%;
gap: 0;
flex: 1;
min-height: 0;
}

.left-panel {
flex: none;
max-height: 40vh;
flex: 1 1 0;
min-height: 0;
max-height: none;
border: none;
border-radius: 0;
}

.right-panel {
min-height: 50vh;
min-height: 0;
border: none;
border-radius: 0;
flex: 1 1 0;
}

// Single-screen flow: list view OR reader view, never both.
.fallback-container.mobile-reading .left-panel {
display: none;
}

.fallback-container:not(.mobile-reading) .right-panel {
display: none;
}

.mobile-back {
all: unset;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.75rem 1rem;
font-size: var(--pg-font-size-sm);
font-weight: var(--pg-font-weight-medium);
color: var(--pg-primary);
border-bottom: 1px solid var(--pg-input-normal);
flex-shrink: 0;
}
}
</style>
6 changes: 3 additions & 3 deletions src/routes/(marketing)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
padding: 0.5rem 1rem;
background: var(--pg-primary);
color: var(--pg-on-primary);
font-weight: var(--pg-font-weight-semibold);
font-weight: var(--pg-font-weight-medium);
text-decoration: none;
z-index: 1000;
transform: translateY(-200%);
Expand Down Expand Up @@ -156,7 +156,7 @@
.footer-col {
h4 {
font-size: var(--pg-font-size-sm);
font-weight: var(--pg-font-weight-semibold);
font-weight: var(--pg-font-weight-medium);
color: var(--pg-text);
margin: 0 0 0.75rem;
}
Expand Down Expand Up @@ -193,7 +193,7 @@
a {
color: var(--pg-text-secondary);
text-decoration: none;
font-weight: var(--pg-font-weight-semibold);
font-weight: var(--pg-font-weight-medium);

&:hover {
color: var(--pg-primary);
Expand Down
Loading