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
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
5 changes: 3 additions & 2 deletions src/lib/components/filesharing/SendButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -739,8 +739,9 @@
background: var(--pg-soft-background);
border-radius: var(--pg-border-radius-md);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
padding: 1rem 1.5rem 1.5rem 1.5rem;
width: var(--popup-width);
padding: 1rem;
width: fit-content;
max-width: calc(330px + 2rem);
z-index: 4;
}

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