diff --git a/src/backend/backend.js b/src/backend/backend.js index d193464..a2cebc6 100644 --- a/src/backend/backend.js +++ b/src/backend/backend.js @@ -972,6 +972,12 @@ const getOasisVersion = async (feedId) => { if (OASIS_VERSION && mine !== OASIS_VERSION) { ssb.publish({ type: 'oasisVersion', version: OASIS_VERSION, updatedAt: new Date().toISOString() }, () => {}); } + try { + const img = await about.image(ssb.id); + global.__OASIS_SELF_AVATAR__ = (typeof img === 'string' && img[0] === '&') + ? getAvatarUrl(img) + : '/assets/images/default-avatar.png'; + } catch (_) {} } catch (_) {} })(); async function renderBlobMarkdown(text, mentions = {}, myFeedId, myUsername) { @@ -2802,6 +2808,7 @@ router inhabitantsModel.getLastActivityTimestampByUserId(myFeedId).catch(() => null) ]); const larpHouse = larpHouseKey ? { key: larpHouseKey, ...larpModel.getHouse(larpHouseKey) } : null; + global.__OASIS_SELF_AVATAR__ = getAvatarUrl(image); const stats = await inhabitantsModel.getInhabitantStats(myFeedId, myFeedId).catch(() => ({})); const userActions = (allActions || []).filter(a => a && a.author === myFeedId && a.type !== 'tombstone' && a.type !== 'post'); const normTs = t => { const n = Number(t || 0); return !isFinite(n) || n <= 0 ? 0 : n < 1e12 ? n * 1000 : n; }; diff --git a/src/client/assets/styles/mobile.css b/src/client/assets/styles/mobile.css index 18c1720..97dca01 100644 --- a/src/client/assets/styles/mobile.css +++ b/src/client/assets/styles/mobile.css @@ -914,3 +914,620 @@ a.create-button { .peers-technical-block .block-info-table td[data-label=""]::before { content: none !important; } .peers-list .block-info-table td .peer-key, .peers-technical-block .block-info-table td .peer-key { text-align: right !important; } + +.oasis-bottombar { + display: flex !important; + position: fixed !important; + left: 0 !important; right: 0 !important; bottom: 0 !important; + z-index: 1000 !important; + justify-content: space-around !important; + align-items: center !important; + gap: 4px !important; + background: #1F1F1F !important; + border-top: 1px solid #333 !important; + padding: 8px 6px calc(8px + env(safe-area-inset-bottom, 0px)) !important; +} +.oasis-bottombar .bb-item { + position: relative !important; + display: flex !important; + flex-direction: column !important; + align-items: center !important; + gap: 3px !important; + flex: 1 1 0 !important; + min-width: 0 !important; + padding: 2px 0 !important; + text-decoration: none !important; +} +.oasis-bottombar .bb-ico { font-size: 18px !important; line-height: 1 !important; color: #FFA500 !important; } +.oasis-bottombar .bb-lbl { font-size: 9px !important; color: #ccc !important; white-space: nowrap !important; } +.oasis-bottombar .bb-badge { + position: absolute !important; + top: -4px !important; + left: calc(50% + 6px) !important; + min-width: 15px !important; height: 15px !important; + padding: 0 3px !important; + border-radius: 8px !important; + background: #FFA500 !important; + color: #121212 !important; + font-size: 9px !important; font-weight: bold !important; + display: flex !important; align-items: center !important; justify-content: center !important; +} + +body { padding-bottom: 64px !important; } + +.oasis-mobile-topbar { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + width: 100% !important; + padding: 8px 12px !important; + background: transparent !important; + box-shadow: none !important; border: 0 !important; +} + +.oasis-mobile-topbar .omt-logo img, +.oasis-mobile-topbar .omt-avatar img { + width: 32px !important; height: 32px !important; + border-radius: 50% !important; + border: 2px solid #FFA500 !important; + object-fit: cover !important; display: block !important; +} + +.oasis-mobile-topbar .omt-title { + flex: 1 1 auto !important; + text-align: center !important; + font-size: 26px !important; font-weight: bold !important; + letter-spacing: 10px !important; text-indent: 10px !important; + color: #FFD700 !important; text-decoration: none !important; + white-space: nowrap !important; + background: transparent !important; border: 0 !important; box-shadow: none !important; padding: 0 !important; +} + +.header .top-bar-left > a.logo-icon { display: none !important; } + +.oasis-mobile-menus { display: flex !important; gap: 8px !important; padding: 0 10px 6px !important; } +.oasis-mobile-menus .oasis-menu-ico { display: none !important; } +.oasis-mobile-menus .oasis-menu-btn { + flex: 1 1 0 !important; + display: flex !important; align-items: center !important; justify-content: center !important; + padding: 12px !important; + border: 1.5px solid #333 !important; border-radius: 10px !important; + background: #121212 !important; color: #ccc !important; + font-weight: bold !important; letter-spacing: 1px !important; cursor: pointer !important; +} + +#nav-left-toggle:checked ~ .header .oasis-menu-btn[for="nav-left-toggle"] { + background: #FFA500 !important; color: #121212 !important; border-color: #FFA500 !important; +} + +#nav-right-toggle:checked ~ .header .oasis-menu-btn[for="nav-right-toggle"] { + background: #FFD700 !important; color: #121212 !important; border-color: #FFD700 !important; +} + +:root { + --space-1: 4px; --space-2: 8px; --space-3: 16px; --space-4: 24px; --space-5: 32px; + --fs-caption: 12px; --fs-body: 14px; --fs-body-lg: 16px; --fs-title: 20px; + --lh-body: 1.5; --measure: 66ch; + --c-muted: #8c8c8c; --radius-md: 12px; +} + +.oasis-bottombar .bb-fab .bb-ico { + width: 46px !important; height: 46px !important; + margin-top: -22px !important; + border-radius: 50% !important; + background: #FFA500 !important; color: #121212 !important; + display: flex !important; align-items: center !important; justify-content: center !important; + font-size: 22px !important; + box-shadow: 0 2px 8px rgba(0,0,0,.4) !important; +} +.oasis-bottombar .bb-fab .bb-lbl { color: #FFA500 !important; font-weight: bold !important; } + +.main-column, .main-column p, .main-column li, .main-column a, +.main-column span, .main-column td, .main-column h1, .main-column h2, .main-column h3 { + overflow-wrap: anywhere !important; + word-break: break-word !important; +} +.main-column { padding: var(--space-3) !important; } +.main-column p { line-height: var(--lh-body) !important; } + +.main-column > * + * { margin-top: var(--space-3) !important; } + +.main-column img, .main-column video, .main-column table, .main-column pre, .main-column iframe { + max-width: 100% !important; height: auto !important; +} +.main-column pre { white-space: pre-wrap !important; } + +.oasis-nav-group { + list-style: none !important; + background: #1c1c1c !important; + border: 1px solid #333 !important; + border-radius: var(--radius-md) !important; + overflow: hidden !important; + margin-bottom: var(--space-2) !important; +} +.oasis-nav-header { + display: flex !important; align-items: center !important; gap: var(--space-2) !important; + min-height: 48px !important; padding: 0 var(--space-3) !important; + cursor: pointer !important; + color: #FFD700 !important; font-weight: bold !important; + text-transform: uppercase !important; letter-spacing: .04em !important; + font-size: var(--fs-caption) !important; +} +.oasis-nav-header .nav-text { flex: 1 1 auto !important; } +.oasis-nav-header .oasis-nav-arrow { color: var(--c-muted) !important; transition: transform 150ms ease !important; } +.oasis-nav-toggle:checked + .oasis-nav-header .oasis-nav-arrow { transform: rotate(180deg) !important; } +.oasis-nav-header:active { background: #262626 !important; } +.oasis-nav-list { list-style: none !important; padding: 0 !important; margin: 0 !important; } +.oasis-nav-list li a { + display: flex !important; align-items: center !important; gap: var(--space-3) !important; + min-height: 48px !important; padding: 0 var(--space-3) !important; + color: #e8e8e8 !important; text-decoration: none !important; + border-top: 1px solid #262626 !important; +} +.oasis-nav-list li a .emoji { flex: 0 0 auto !important; width: 22px !important; text-align: center !important; color: #FFA500 !important; } +.oasis-nav-list li a .nav-text { font-size: var(--fs-body) !important; } +.oasis-nav-list li a:active { background: #262626 !important; } +.oasis-nav-list li a.current { color: #FFA500 !important; font-weight: bold !important; } + +.main-column h1 { font-size: var(--fs-title) !important; font-weight: 700 !important; line-height: 1.25 !important; margin: 0 0 var(--space-2) !important; } +.main-column h2 { font-size: 18px !important; font-weight: 700 !important; line-height: 1.3 !important; margin: 0 0 var(--space-2) !important; } +.main-column h3 { font-size: var(--fs-body-lg) !important; font-weight: 600 !important; margin: 0 0 var(--space-1) !important; } +.main-column p { font-size: var(--fs-body-lg) !important; } +.main-column .created-at, .main-column .small, .main-column .time, +.main-column .meta, .main-column .author-meta, .main-column .subtitle { + color: var(--c-muted) !important; font-size: var(--fs-caption) !important; +} + +.main-column article, +.main-column [class*="-card"] { + background: #1c1c1c !important; + border: 1px solid #333 !important; + border-radius: var(--radius-md) !important; + padding: var(--space-3) !important; +} +.main-column article:active, +.main-column [class*="-card"]:active { background: #202020 !important; } + +.btn, button, input[type="submit"], input[type="button"], .submit-button, +.filter-btn, .create-button, .edit-btn, .delete-btn, .join-btn, .leave-btn, .buy-btn { + min-height: 48px !important; +} +button:active, input[type="submit"]:active, .submit-button:active, +.filter-btn:active, .create-button:active, .edit-btn:active, +.delete-btn:active, .join-btn:active, .leave-btn:active, .buy-btn:active { + filter: brightness(.85) !important; +} +.main-column a:active { opacity: .6 !important; } + +* { -webkit-tap-highlight-color: transparent !important; } +a, button, label, .tap, .oasis-menu-btn, .bb-item, .oasis-nav-header, .oasis-nav-list li a { + touch-action: manipulation !important; +} +body { overscroll-behavior: none !important; } + +.header .top-bar-left nav, +.header .top-bar-right nav { display: none !important; } + +.oasis-mobile-topbar { padding-top: calc(env(safe-area-inset-top, 0px) + 10px) !important; } + +.oasis-bottombar .bb-pair { + display: flex !important; align-items: center !important; + flex: 0 0 auto !important; + gap: 2px !important; padding: 3px !important; + background: #262626 !important; + border: 1px solid #3a3a3a !important; + border-radius: 999px !important; +} +.oasis-bottombar .bb-pair .bb-item { flex: 0 0 auto !important; padding: 2px 8px !important; } +.oasis-bottombar .bb-pair .bb-lbl { font-size: 8px !important; } + +.oasis-bottombar a.bb-item.current .bb-ico { + background: #FFA500 !important; color: #121212 !important; + border-radius: 999px !important; padding: 4px 12px !important; +} +.oasis-bottombar a.bb-item.current .bb-lbl { color: #FFA500 !important; font-weight: bold !important; } +.oasis-bottombar a.bb-item.current .bb-badge { background: #121212 !important; color: #FFA500 !important; } + +.main-column, .main-column p, .main-column li, .main-column a, +.main-column span, .main-column td, .main-column th, .main-column label, +.main-column h1, .main-column h2, .main-column h3, .main-column h4, +.main-column strong, .main-column em, .main-column small, +.created-at, .post-meta, .feed-post-meta, +.feed-row .small, .feed-row .time, .feed-row .created-at, +.profile-sensors-box > *, +.card-field .card-value, .card-field .card-label { + overflow-wrap: break-word !important; + word-break: normal !important; + -webkit-hyphens: none !important; + hyphens: none !important; +} + +.main-column pre, .main-column code, kbd, samp, +.peer-key, .user-link.peer-key, .oasis-id-copy, .search-oasis-id, +.pm-sent-key-value, .kpi__value, +.block-info-table td .peer-key, .block-info-table td .user-link, +.pm-info-table td .user-link, +.main-column a[href*="ed25519"], .main-column a[href^="ssb:"], +.main-column a[href^="/author/"] { + overflow-wrap: anywhere !important; + word-break: break-all !important; +} + +button, input[type="submit"], input[type="button"], +.filter-btn, a.filter-btn, .create-button, .edit-btn, .delete-btn, +.join-btn, .leave-btn, .buy-btn, .submit-button, .btn, +.conn-actions button, .peers-conn-actions button, +.mode-buttons button, .mode-buttons .filter-btn, +.filter-group button, .filter-group .filter-btn, +.main-column button, .main-column input[type="submit"] { + white-space: normal !important; + word-break: normal !important; + overflow-wrap: break-word !important; + -webkit-hyphens: none !important; + hyphens: none !important; +} + +.conn-actions, .peers-conn-actions { + display: flex !important; + flex-direction: row !important; + flex-wrap: wrap !important; + justify-content: center !important; + align-items: stretch !important; + gap: 8px !important; + width: 100% !important; + max-width: 100% !important; + overflow-x: hidden !important; +} +.conn-actions > form, .peers-conn-actions > form { + flex: 0 1 auto !important; + width: auto !important; + min-width: 0 !important; + margin: 0 !important; + display: flex !important; +} + +.pm-info-table, .pm-info-table tbody, +.block-info-table, .block-info-table tbody { + display: block !important; + width: 100% !important; + max-width: 100% !important; + border: 0 !important; + background: transparent !important; + box-shadow: none !important; + table-layout: auto !important; +} +.pm-info-table thead, .block-info-table thead { display: none !important; } +.pm-info-table tr, .block-info-table tr { + display: block !important; + width: 100% !important; + box-sizing: border-box !important; + border: 0 !important; + border-bottom: 1px solid rgba(255,165,0,0.18) !important; + border-radius: 0 !important; + margin: 0 0 8px 0 !important; + padding: 6px 0 !important; + background: transparent !important; +} +.pm-info-table td, .block-info-table td { + display: block !important; + width: 100% !important; + max-width: 100% !important; + box-sizing: border-box !important; + border: 0 !important; + background: transparent !important; + padding: 1px 0 !important; + text-align: left !important; + overflow-wrap: break-word !important; + word-break: normal !important; +} + +.pm-info-table td.card-label, .block-info-table td.card-label { + color: var(--c-muted, #8c8c8c) !important; + font-size: 11px !important; + font-weight: 700 !important; + text-transform: uppercase !important; + letter-spacing: .03em !important; + margin-bottom: 2px !important; + padding-bottom: 0 !important; +} +.pm-info-table td.card-value, .block-info-table td.card-value { + font-size: 15px !important; + line-height: 1.5 !important; + margin-bottom: 6px !important; +} + +.block-info-table tr:first-child:has(> td.card-label) { display: none !important; } +.block-info-table td[data-label]:not([data-label=""])::before { + content: attr(data-label); + display: block !important; + color: var(--c-muted, #8c8c8c) !important; + font-size: 10px !important; + font-weight: 700 !important; + text-transform: uppercase !important; + letter-spacing: .03em !important; + margin-bottom: 1px !important; + white-space: nowrap !important; + word-break: normal !important; +} +.block-info-table td[data-label=""]::before { content: none !important; } + +.peers-list .block-info-table td, +.peers-technical-block .block-info-table td, +.gov-overview tbody td { + word-break: normal !important; + overflow-wrap: break-word !important; +} + +.pm-info-table td .user-link, .pm-info-table td .peer-key, .pm-info-table td a[href], +.block-info-table td .peer-key, .block-info-table td .user-link, .block-info-table td a[href] { + display: inline-block !important; + max-width: 100% !important; + overflow-wrap: anywhere !important; + word-break: break-all !important; +} + +.pm-info-table td form, .block-info-table td form, +.pm-info-table td button, .block-info-table td button { + width: 100% !important; + margin: 0 !important; +} + +.main-content { width: 100% !important; max-width: 100% !important; } +.main-column { + width: 100% !important; max-width: 100% !important; + padding: 16px !important; box-sizing: border-box !important; +} +.main-column .card-field { + flex-direction: column !important; + align-items: stretch !important; + gap: 2px !important; +} +.main-column .card-field .card-value { + width: 100% !important; max-width: 100% !important; font-size: 15px !important; +} +.main-column .card-field .card-label { + color: var(--c-muted, #8c8c8c) !important; font-size: 11px !important; +} +.main-column img, .main-column video, .main-column iframe, .main-column canvas { + max-width: 100% !important; height: auto !important; +} +.main-column hr { + width: 100% !important; border: 0 !important; + border-top: 1px solid #333 !important; margin: 16px 0 !important; +} + +.activity-filter-acc { width: 100% !important; margin-bottom: 8px !important; } + +.activity-quick-modes { + display: flex !important; flex-wrap: nowrap !important; gap: 6px !important; margin-bottom: 8px !important; +} +.activity-quick-modes form { flex: 1 1 0 !important; min-width: 0 !important; margin: 0 !important; display: flex !important; } +.activity-quick-modes .filter-btn { + flex: 1 1 0 !important; width: 100% !important; min-height: 44px !important; + white-space: nowrap !important; word-break: keep-all !important; overflow-wrap: normal !important; + padding-left: 6px !important; padding-right: 6px !important; font-size: 14px !important; +} + +.activity-groups { list-style: none !important; padding: 0 !important; margin: 0 !important; } + +.activity-groups .oasis-nav-header .emoji { display: none !important; } +.activity-groups .oasis-nav-header .nav-text { flex: 1 1 auto !important; } +.activity-groups .act-count { + flex: 0 0 auto !important; min-width: 20px !important; text-align: center !important; + font-size: 11px !important; font-weight: 700 !important; color: #121212 !important; + background: #FFA500 !important; border-radius: 999px !important; padding: 1px 7px !important; + margin: 0 8px 0 0 !important; +} + +.activity-groups .oasis-nav-list li { list-style: none !important; margin: 0 !important; padding: 0 !important; } +.activity-groups .oasis-nav-list form { margin: 0 !important; display: block !important; width: 100% !important; } +.activity-groups .oasis-nav-list .filter-btn { + display: block !important; width: 100% !important; text-align: left !important; + min-height: 44px !important; margin: 2px 0 !important; box-sizing: border-box !important; +} +.activity-groups .oasis-nav-list .filter-btn.active { + outline: 2px solid #FFA500 !important; outline-offset: -2px !important; +} + +.message-list { display: flex !important; flex-direction: column !important; gap: 4px !important; width: 100% !important; } +.message-list .pm-thread { display: flex !important; flex-direction: column !important; width: 100% !important; align-self: stretch !important; } +.pm-thread-replies { display: flex !important; flex-direction: column !important; gap: 4px !important; } + +.pm-card.pm-bubble { + max-width: 82% !important; width: fit-content !important; min-width: 0 !important; + box-sizing: border-box !important; padding: 8px 12px !important; margin: 2px 0 !important; + border-radius: 16px !important; border: 1px solid #333 !important; + background: #1e1e1e !important; box-shadow: none !important; cursor: default !important; +} + +.pm-card.pm-bubble.pm-recv { + align-self: flex-start !important; margin-left: 0 !important; margin-right: auto !important; + border-bottom-left-radius: 4px !important; background: #1e1e1e !important; border-color: #343434 !important; +} + +.pm-card.pm-bubble.pm-sent { + align-self: flex-end !important; margin-left: auto !important; margin-right: 0 !important; + border-bottom-right-radius: 4px !important; background: #2a2413 !important; border-color: #5c4c1f !important; +} + +.pm-bubble .pm-bubble-sender { font-size: 11px !important; font-weight: 700 !important; color: #FFA500 !important; margin-bottom: 3px !important; } +.pm-bubble .pm-bubble-sender a { color: #FFA500 !important; } + +.pm-bubble .message-text { font-size: 15px !important; line-height: 1.42 !important; color: #ececec !important; margin: 0 !important; } +.pm-bubble .message-text a { color: #FFD700 !important; } + +.pm-bubble .pm-bubble-meta { + display: flex !important; flex-wrap: wrap !important; align-items: center !important; + gap: 6px !important; margin-top: 4px !important; font-size: 10px !important; color: var(--c-muted, #8c8c8c) !important; +} +.pm-card.pm-sent .pm-bubble-meta { justify-content: flex-end !important; } +.pm-card.pm-recv .pm-bubble-meta { justify-content: flex-start !important; } +.pm-bubble .pm-bubble-subject { font-style: italic !important; opacity: .75 !important; max-width: 50% !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; } +.pm-bubble .pm-bubble-lock { opacity: .85 !important; } +.pm-bubble .pm-bubble-eco { font-size: 9px !important; opacity: .9 !important; } +.pm-bubble .pm-bubble-eco * { font-size: 9px !important; } + +.pm-bubble .pm-actions { margin-top: 6px !important; display: flex !important; gap: 6px !important; } +.pm-card.pm-sent .pm-actions { justify-content: flex-end !important; } +.pm-bubble .pm-actions .pm-btn { font-size: 10px !important; padding: 3px 8px !important; min-height: 28px !important; } +.pm-bubble .pm-actions form { margin: 0 !important; } + +.pm-bubble .pm-info-table { display: none !important; } + +.pm-thread .pm-thread-details { width: 100% !important; } +.pm-thread .pm-thread-toggle { font-size: 12px !important; color: var(--c-muted, #8c8c8c) !important; padding: 4px 0 !important; cursor: pointer !important; } + +.main-column h1, .main-column h2, .main-column h3, .main-column h4, +.tags-header h2, .tags-header h3, .title-with-chip h2, .pm-title, h2.pm-title, +.cn-name, h1.cn-name, .cn-section, h2.cn-section, .profile-side-name, h2.profile-side-name { + text-wrap: balance !important; + overflow-wrap: break-word !important; + word-break: keep-all !important; + -webkit-hyphens: none !important; hyphens: none !important; + text-align: left !important; +} +.main-column h1, .tags-header h2, .cn-name { + font-size: var(--fs-title, 20px) !important; font-weight: 700 !important; + line-height: 1.22 !important; letter-spacing: -0.01em !important; + margin: 0 0 var(--space-2, 8px) !important; +} +.main-column h2, .pm-title, .title-with-chip h2, .cn-section { + font-size: 18px !important; font-weight: 700 !important; + line-height: 1.28 !important; margin: var(--space-1, 4px) 0 var(--space-2, 8px) !important; +} +.main-column h3 { + font-size: var(--fs-body-lg, 16px) !important; font-weight: 600 !important; + line-height: 1.35 !important; margin: var(--space-1, 4px) 0 !important; +} +.main-column h1, .main-column h2, .main-column h3, +.tags-header h2, .pm-title, .cn-name, .cn-section { + max-width: 40ch !important; width: auto !important; min-width: 0 !important; +} + +.main-column h1 code, .main-column h2 code, .main-column h3 code, +.tags-header h2 code, .pm-title code, +.main-column h1 a[href*="ed25519"], .main-column h2 a[href*="ed25519"], +.main-column h1 a[href^="ssb:"], .main-column h2 a[href^="ssb:"] { + word-break: break-all !important; overflow-wrap: anywhere !important; +} + +.main-column .tags-header { + display: flex !important; flex-direction: column !important; align-items: flex-start !important; + gap: var(--space-1, 4px) !important; width: 100% !important; max-width: 100% !important; + margin: 0 0 var(--space-2, 8px) !important; +} +.main-column .tags-header h2 { margin: 0 !important; } + +.main-column .title-with-chip { + display: flex !important; flex-wrap: wrap !important; align-items: center !important; + gap: var(--space-2, 8px) !important; width: 100% !important; max-width: 100% !important; +} +.main-column .title-with-chip h2 { flex: 1 1 auto !important; min-width: 0 !important; margin: 0 !important; max-width: none !important; } +.main-column .title-with-chip > :not(h2) { flex: 0 0 auto !important; } + +.main-column p, .main-column li, +.tags-header p, .tags-header .prefs-help, .indexing-note, .indexing-percent, +.error-page-message, .welcome-text, .message-text, .pm-bubble .message-text { + text-wrap: pretty !important; + line-height: 1.55 !important; + max-width: min(66ch, 100%) !important; + overflow-wrap: anywhere !important; + word-break: normal !important; + min-width: 0 !important; + -webkit-hyphens: none !important; hyphens: none !important; +} +.main-column p + p { margin-top: var(--space-3, 16px) !important; } +.tags-header p, .tags-header .prefs-help, .indexing-note { line-height: 1.5 !important; margin: 4px 0 0 0 !important; } +.pm-bubble .message-text { line-height: 1.45 !important; max-width: 100% !important; } + +[style*="auto-fill"], +[style*="minmax(240px"], [style*="minmax(280px"], [style*="minmax(300px"], +[style*="minmax( 240px"], [style*="minmax( 280px"], [style*="minmax( 300px"], +.trending-container, .cn-hub-grid, .cn-products { + display: grid !important; grid-template-columns: 1fr !important; gap: 12px !important; + width: 100% !important; max-width: 100% !important; +} +.trending-container > *, .games-grid > *, .market-grid > *, .tribe-grid > *, +.feed-container, .feed-card, .trending-card, +.main-column article, .main-column [class*="-card"], .main-column [class*="-item"], +.main-column [class*="-entry"], .main-column .card { + width: 100% !important; max-width: 100% !important; min-width: 0 !important; + box-sizing: border-box !important; margin-left: 0 !important; margin-right: 0 !important; +} +.feed-card .feed-text, .trending-card .feed-text, +.main-column article p, .main-column [class*="-card"] p { max-width: var(--measure, 66ch) !important; } +.main-column form, .comment-form, .comment-form-wrapper, .market-form, +.new-message-form, .project-control-form, .filter-box { + display: block !important; width: 100% !important; max-width: 100% !important; + min-width: 0 !important; box-sizing: border-box !important; +} +.main-column input:not([type="checkbox"]):not([type="radio"]):not(.oasis-nav-toggle):not(.oasis-nav-side-toggle), +.main-column textarea, .main-column select { + width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; +} +.main-column form > button:only-of-type, .main-column form > .create-button, +.comment-form button[type="submit"], .market-form button[type="submit"], +.new-message-form button[type="submit"] { + display: block !important; width: 100% !important; max-width: 100% !important; + box-sizing: border-box !important; margin-top: 8px !important; +} +[style*="width:33%"], [style*="width: 33%"], [style*="width:25%"], [style*="width: 25%"], +[style*="width:60%"], [style*="width: 60%"], [style*="width:40%"], [style*="width: 40%"] { + width: 100% !important; max-width: 100% !important; +} +.main-column [style*="max-width: 800px"], .main-column [style*="max-width:800px"], +.ai-response, .ai-train-bar { + max-width: 100% !important; width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; +} +.message-list, .pm-thread, .pm-thread-replies { width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; } + +.main-column p .user-link, .main-column p a[href^="/author/"], +.main-column li .user-link, .main-column span .user-link, +.main-column .message-text .user-link, .main-column .message-text a[href^="/author/"], +.pm-bubble .message-text .user-link, .pm-bubble .message-text a[href^="/author/"], +.pm-quote a[href^="/author/"] { + display: inline !important; white-space: normal !important; + overflow-wrap: anywhere !important; word-break: keep-all !important; + -webkit-hyphens: none !important; hyphens: none !important; +} + +.peer-key, .user-link.peer-key, .block-info-table td .peer-key, .pm-info-table td .peer-key, +.pm-info-table td .user-link, .block-info-table td .user-link, +.search-oasis-id, .pm-sent-key-value, .kpi__value { + display: inline-block !important; max-width: 100% !important; + overflow-wrap: anywhere !important; word-break: break-all !important; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important; + font-variant-numeric: tabular-nums !important; line-height: 1.45 !important; +} + +.oasis-id-copy { + display: block !important; width: 100% !important; white-space: nowrap !important; + overflow: hidden !important; text-overflow: ellipsis !important; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important; +} +.invites-list .user-link.peer-key, .peers-list .block-info-table td .peer-key { + display: block !important; max-width: 100% !important; white-space: nowrap !important; + overflow: hidden !important; text-overflow: ellipsis !important; word-break: normal !important; +} + +.main-column .message-text a[href^="http"], .main-column p a[href^="http"], +.pm-bubble .message-text a[href^="http"], .main-column .message-text a.oasis-path-link, +.main-column .message-text a.job-link, .main-column .message-text a.project-link, +.main-column .message-text a.market-link { + overflow-wrap: anywhere !important; word-break: keep-all !important; + -webkit-hyphens: none !important; hyphens: none !important; +} +.main-column code:not(pre code), .pm-bubble .message-text code { + display: inline !important; max-width: 100% !important; + overflow-wrap: anywhere !important; word-break: break-word !important; white-space: pre-wrap !important; +} + +.main-column .tags-header { margin-bottom: 6px !important; gap: 2px !important; } +.main-column .tags-header h2, .tags-header h2 { + font-size: 17px !important; line-height: 1.2 !important; letter-spacing: 0 !important; +} +.main-column .tags-header p { font-size: 13px !important; line-height: 1.35 !important; margin: 0 !important; } + +.main-column { padding: 10px 12px 16px 12px !important; } +.activity-filter-acc { margin-bottom: 4px !important; } + diff --git a/src/client/assets/translations/oasis_ar.js b/src/client/assets/translations/oasis_ar.js index 4e6d76b..af7e1dd 100644 --- a/src/client/assets/translations/oasis_ar.js +++ b/src/client/assets/translations/oasis_ar.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { ar: { + activityGroupCommunity: "المجتمع والحوكمة", + activityGroupOrg: "التنظيم", + activityGroupComm: "التواصل", + activityGroupEconomy: "الاقتصاد", + activityGroupMedia: "المحتوى والوسائط", + activityGroupOther: "أخرى", languageName: "العربية", shopOrderStatusPaid: "تم استلام الدفع", shopOrderStatusReceived: "تم الاستلام", diff --git a/src/client/assets/translations/oasis_de.js b/src/client/assets/translations/oasis_de.js index dc782ac..4ce472a 100644 --- a/src/client/assets/translations/oasis_de.js +++ b/src/client/assets/translations/oasis_de.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { de: { + activityGroupCommunity: "Gemeinschaft & Governance", + activityGroupOrg: "Organisation", + activityGroupComm: "Kommunikation", + activityGroupEconomy: "Wirtschaft", + activityGroupMedia: "Inhalte & Medien", + activityGroupOther: "Sonstiges", languageName: "Deutsch", shopOrderStatusPaid: "Zahlung erhalten", shopOrderStatusReceived: "Erhalten", diff --git a/src/client/assets/translations/oasis_en.js b/src/client/assets/translations/oasis_en.js index 76a876b..ae5e327 100644 --- a/src/client/assets/translations/oasis_en.js +++ b/src/client/assets/translations/oasis_en.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { en: { + activityGroupCommunity: "Community & Governance", + activityGroupOrg: "Organization", + activityGroupComm: "Communication", + activityGroupEconomy: "Economy", + activityGroupMedia: "Content & Media", + activityGroupOther: "Others", languageName: "English", shopOrderStatusPaid: "Payment received", shopOrderStatusReceived: "Received", diff --git a/src/client/assets/translations/oasis_es.js b/src/client/assets/translations/oasis_es.js index 099b669..46f3f48 100644 --- a/src/client/assets/translations/oasis_es.js +++ b/src/client/assets/translations/oasis_es.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { es: { + activityGroupCommunity: "Comunidad y Gobernanza", + activityGroupOrg: "Organización", + activityGroupComm: "Comunicación", + activityGroupEconomy: "Economía", + activityGroupMedia: "Contenido y Media", + activityGroupOther: "Otros", languageName: "Castellano", shopOrderStatusPaid: "Pago recibido", shopOrderStatusReceived: "Recibido", diff --git a/src/client/assets/translations/oasis_eu.js b/src/client/assets/translations/oasis_eu.js index 648f9aa..251748d 100644 --- a/src/client/assets/translations/oasis_eu.js +++ b/src/client/assets/translations/oasis_eu.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { eu: { + activityGroupCommunity: "Komunitatea eta gobernantza", + activityGroupOrg: "Antolakuntza", + activityGroupComm: "Komunikazioa", + activityGroupEconomy: "Ekonomia", + activityGroupMedia: "Edukia eta multimedia", + activityGroupOther: "Bestelakoak", languageName: "Basque", shopOrderStatusPaid: "Ordainketa jasota", shopOrderStatusReceived: "Jasota", diff --git a/src/client/assets/translations/oasis_fr.js b/src/client/assets/translations/oasis_fr.js index d18f647..3d6f340 100644 --- a/src/client/assets/translations/oasis_fr.js +++ b/src/client/assets/translations/oasis_fr.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { fr: { + activityGroupCommunity: "Communauté et gouvernance", + activityGroupOrg: "Organisation", + activityGroupComm: "Communication", + activityGroupEconomy: "Économie", + activityGroupMedia: "Contenu et médias", + activityGroupOther: "Autres", languageName: "Français", shopOrderStatusPaid: "Paiement reçu", shopOrderStatusReceived: "Reçu", diff --git a/src/client/assets/translations/oasis_hi.js b/src/client/assets/translations/oasis_hi.js index c56e877..0108885 100644 --- a/src/client/assets/translations/oasis_hi.js +++ b/src/client/assets/translations/oasis_hi.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { hi: { + activityGroupCommunity: "समुदाय और शासन", + activityGroupOrg: "संगठन", + activityGroupComm: "संचार", + activityGroupEconomy: "अर्थव्यवस्था", + activityGroupMedia: "सामग्री और मीडिया", + activityGroupOther: "अन्य", languageName: "हिन्दी", shopOrderStatusPaid: "भुगतान प्राप्त", shopOrderStatusReceived: "प्राप्त", diff --git a/src/client/assets/translations/oasis_it.js b/src/client/assets/translations/oasis_it.js index 5c32782..fcd8d21 100644 --- a/src/client/assets/translations/oasis_it.js +++ b/src/client/assets/translations/oasis_it.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { it: { + activityGroupCommunity: "Comunità e governance", + activityGroupOrg: "Organizzazione", + activityGroupComm: "Comunicazione", + activityGroupEconomy: "Economia", + activityGroupMedia: "Contenuti e media", + activityGroupOther: "Altri", languageName: "Italiano", shopOrderStatusPaid: "Pagamento ricevuto", shopOrderStatusReceived: "Ricevuto", diff --git a/src/client/assets/translations/oasis_pt.js b/src/client/assets/translations/oasis_pt.js index 2b2f5e5..0a84578 100644 --- a/src/client/assets/translations/oasis_pt.js +++ b/src/client/assets/translations/oasis_pt.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { pt: { + activityGroupCommunity: "Comunidade e governança", + activityGroupOrg: "Organização", + activityGroupComm: "Comunicação", + activityGroupEconomy: "Economia", + activityGroupMedia: "Conteúdo e média", + activityGroupOther: "Outros", languageName: "Português", shopOrderStatusPaid: "Pagamento recebido", shopOrderStatusReceived: "Recebido", diff --git a/src/client/assets/translations/oasis_ru.js b/src/client/assets/translations/oasis_ru.js index 58f2059..a69a349 100644 --- a/src/client/assets/translations/oasis_ru.js +++ b/src/client/assets/translations/oasis_ru.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { ru: { + activityGroupCommunity: "Сообщество и управление", + activityGroupOrg: "Организация", + activityGroupComm: "Коммуникация", + activityGroupEconomy: "Экономика", + activityGroupMedia: "Контент и медиа", + activityGroupOther: "Другое", languageName: "Русский", shopOrderStatusPaid: "Оплата получена", shopOrderStatusReceived: "Получено", diff --git a/src/client/assets/translations/oasis_zh.js b/src/client/assets/translations/oasis_zh.js index 2b7b412..8ea0cf1 100644 --- a/src/client/assets/translations/oasis_zh.js +++ b/src/client/assets/translations/oasis_zh.js @@ -1,6 +1,12 @@ const { a, em, strong } = require('../../../server/node_modules/hyperaxe'); module.exports = { zh: { + activityGroupCommunity: "社区与治理", + activityGroupOrg: "组织", + activityGroupComm: "沟通", + activityGroupEconomy: "经济", + activityGroupMedia: "内容与媒体", + activityGroupOther: "其他", languageName: "中文", shopOrderStatusPaid: "已收款", shopOrderStatusReceived: "已收到", diff --git a/src/client/public/js/oasis-mobile-nav.js b/src/client/public/js/oasis-mobile-nav.js new file mode 100644 index 0000000..2878122 --- /dev/null +++ b/src/client/public/js/oasis-mobile-nav.js @@ -0,0 +1,36 @@ +(function () { + function mark() { + var p = (location.pathname || "/").replace(/\/+$/, "") || "/"; + var links = document.querySelectorAll(".oasis-bottombar a.bb-item"); + var best = null, bestLen = -1; + links.forEach(function (a) { + var h = (a.getAttribute("href") || "").replace(/\/+$/, "") || "/"; + if (h === "/") return; + if (p === h || p.indexOf(h + "/") === 0) { + if (h.length > bestLen) { best = a; bestLen = h.length; } + } + }); + if (best) best.classList.add("current"); + } + + function onToggle(e) { + var t = e.target; + if (!t || !t.classList || !t.classList.contains("oasis-nav-toggle")) return; + if (!t.checked) return; + var group = t.closest(".oasis-nav-group"); + var scope = group && group.parentElement; + if (!scope) return; + var others = scope.querySelectorAll( + ":scope > .oasis-nav-group > .oasis-nav-toggle" + ); + others.forEach(function (o) { if (o !== t) o.checked = false; }); + } + + function init() { + mark(); + document.addEventListener("change", onToggle); + } + + if (document.readyState !== "loading") init(); + else document.addEventListener("DOMContentLoaded", init); +})(); diff --git a/src/views/activity_view.js b/src/views/activity_view.js index 6b01f62..fe9fb95 100644 --- a/src/views/activity_view.js +++ b/src/views/activity_view.js @@ -1,4 +1,4 @@ -const { div, h2, p, section, button, form, a, input, img, textarea, br, span, video: videoHyperaxe, audio: audioHyperaxe, table, tr, td, th, details, summary } = require("../server/node_modules/hyperaxe"); +const { div, h2, p, section, button, form, a, input, img, textarea, br, span, video: videoHyperaxe, audio: audioHyperaxe, table, tr, td, th, details, summary, ul, li, label } = require("../server/node_modules/hyperaxe"); const { template, i18n, userLink, userLinkLabel, renderSpreadButton, renderContentActions } = require('./main_views'); const opinionCategories = require('../backend/opinion_categories'); @@ -1841,25 +1841,43 @@ exports.activityView = (actions, filter, userId, q = '', extras = {}) => { h2(i18n.activityList), p(desc) ), - div({ class: 'activity-filter-grid' }, - ...[ - activityTypes.slice(0, 3), - activityTypes.slice(3, 8), - activityTypes.slice(8, 13), - activityTypes.slice(13, 19), - activityTypes.slice(19, 26), - activityTypes.slice(26) - ].map(col => - div({ class: 'activity-filter-col' }, - col.map(({ type, label }) => - form({ method: 'GET', action: '/activity' }, - input({ type: 'hidden', name: 'filter', value: type }), - button({ type: 'submit', class: filter === type ? 'filter-btn active' : 'filter-btn' }, label) - ) - ) + (() => { + const labelOf = {}; + activityTypes.forEach(t => { labelOf[t.type] = t.label; }); + const mkBtn = (type) => form({ method: 'GET', action: '/activity' }, + input({ type: 'hidden', name: 'filter', value: type }), + button({ type: 'submit', class: filter === type ? 'filter-btn active' : 'filter-btn' }, labelOf[type] || type) + ); + const QUICK = ['recent', 'all', 'mine']; + const GROUPS = [ + { id: 'act-comunidad', emoji: '👥', title: i18n.activityGroupCommunity || 'Community & Governance', types: ['inhabitants', 'tribe', 'larp', 'parliament', 'courts', 'votes', 'report'] }, + { id: 'act-organizacion', emoji: '🗓', title: i18n.activityGroupOrg || 'Organization', types: ['event', 'calendar', 'task', 'project', 'job'] }, + { id: 'act-comunicacion', emoji: '💬', title: i18n.activityGroupComm || 'Communication', types: ['post', 'feed', 'chat', 'pad', 'forum'] }, + { id: 'act-economia', emoji: '💰', title: i18n.activityGroupEconomy || 'Economy', types: ['banking', 'market', 'shop', 'transfer'] }, + { id: 'act-media', emoji: '🎬', title: i18n.activityGroupMedia || 'Content & Media', types: ['audio', 'bookmark', 'document', 'image', 'torrent', 'video'] }, + { id: 'act-otros', emoji: '⋯', title: i18n.activityGroupOther || 'Others', types: ['map', 'curriculum'] }, + ]; + return div({ class: 'activity-filter-acc' }, + div({ class: 'activity-quick-modes' }, QUICK.map(mkBtn)), + ul({ class: 'oasis-nav-groups activity-groups' }, + GROUPS.map(g => { + const openByFilter = g.types.includes(filter); + return li({ class: 'oasis-nav-group' }, + input({ type: 'checkbox', id: `oasis-nav-group-${g.id}`, class: 'oasis-nav-toggle', ...(openByFilter ? { checked: true } : {}) }), + label({ for: `oasis-nav-group-${g.id}`, class: 'oasis-nav-header' }, + span({ class: 'emoji' }, g.emoji), + span({ class: 'nav-text' }, g.title), + span({ class: 'act-count' }, String(g.types.length)), + span({ class: 'oasis-nav-arrow' }, '▾') + ), + ul({ class: 'oasis-nav-list' }, + g.types.map(t => li(mkBtn(t))) + ) + ); + }) ) - ) - ), + ); + })(), sub ? div({ class: 'activity-sub-filter' }, sub.filters.map(f => a({ href: `${sub.url}?filter=${encodeURIComponent(f)}`, class: 'filter-btn' }, String(f).toUpperCase())) diff --git a/src/views/main_views.js b/src/views/main_views.js index 4e70d14..b16b17e 100644 --- a/src/views/main_views.js +++ b/src/views/main_views.js @@ -23,7 +23,7 @@ const getUserId = async () => { return userId; }; -const { a, article, br, body, button, details, div, em, footer, form, h1, h2, h3, head, header, hr, html, img, input, label, li, link, main, meta, nav, option, p, pre, section, select, span, summary, table, td, textarea, title, tr, ul, strong, video: videoHyperaxe, audio: audioHyperaxe } = require("../server/node_modules/hyperaxe"); +const { a, article, br, body, button, details, div, em, footer, form, h1, h2, h3, head, header, hr, html, img, input, label, li, link, main, meta, nav, option, p, pre, section, select, span, summary, table, td, textarea, title, tr, ul, strong, script, video: videoHyperaxe, audio: audioHyperaxe } = require("../server/node_modules/hyperaxe"); const lodash = require("../server/node_modules/lodash"); const markdown = require("./markdown"); @@ -1073,6 +1073,34 @@ const renderTasksLink = () => { : ""; }; +const renderMobileTopbar = () => div( + { class: "oasis-mobile-topbar" }, + a({ class: "omt-logo", href: "/" }, img({ src: "/assets/images/snh-oasis.jpg", alt: "Oasis" })), + a({ class: "omt-title", href: "/" }, "OASIS"), + a({ class: "omt-avatar", href: "/profile" }, img({ src: (typeof global !== "undefined" && global.__OASIS_SELF_AVATAR__) || "/assets/images/default-avatar.png", alt: i18n.profile || "Profile" })) +); + +const renderBottomBar = () => { + const inboxN = sharedState.getInboxCount(); + const bbLink = (href, ico, label, badge) => a( + { class: "bb-item", href }, + span({ class: "bb-ico" }, ico), + (badge && badge > 0) ? span({ class: "bb-badge" }, String(badge)) : "", + span({ class: "bb-lbl" }, label) + ); + return nav( + { class: "oasis-bottombar", style: "display:none", "aria-label": "Quick actions" }, + bbLink("/search", "ꔅ", i18n.searchTitle || "Buscar"), + bbLink("/inbox", "☂", i18n.inbox || "Inbox", inboxN), + bbLink("/publish", "❂", i18n.publish || "Write"), + bbLink("/activity", "ꔙ", i18n.activityTitle || "Activity"), + div({ class: "bb-pair" }, + bbLink("/peers", "⧖", i18n.peers || "Peers"), + bbLink("/invites", "ꔮ", i18n.invites || "Invites") + ) + ); +}; + const template = (titlePrefix, ...elements) => { const currentConfig = getConfig(); const theme = currentConfig.themes.current || "Dark-SNH"; @@ -1095,7 +1123,8 @@ const template = (titlePrefix, ...elements) => { name: "viewport", content: toAttributes({ width: "device-width", - "initial-scale": 1 + "initial-scale": 1, + "viewport-fit": "cover" }) }) ), @@ -1104,6 +1133,7 @@ const template = (titlePrefix, ...elements) => { input({ type: "checkbox", id: "nav-right-toggle", class: "oasis-nav-side-toggle" }), div( { class: uxMode === "ainav" ? "header ainav-only" : "header" }, + renderMobileTopbar(), div( { class: "top-bar-left" }, a( @@ -1387,6 +1417,8 @@ const template = (titlePrefix, ...elements) => { ) ) ), + renderBottomBar(), + script({ src: "/js/oasis-mobile-nav.js", defer: "defer" }), renderFooter() ) ); @@ -2991,6 +3023,16 @@ exports.privateView = async (messagesInput, filter, decrypted = null) => { try { return Buffer.byteLength(JSON.stringify(m && m.value), 'utf8'); } catch (_) { return 0; } } + function bubbleMeta({ sentAt, subjectRaw, crypter, msgSize, msgKey }) { + const ecoChip = msgSize ? renderEcoTax(msgSize, msgKey) : null + return div({ class: 'pm-bubble-meta' }, + subjectRaw ? span({ class: 'pm-bubble-subject' }, subjectRaw) : null, + span({ class: 'pm-bubble-time' }, moment(sentAt).format('DD/MM HH:mm')), + span({ class: 'pm-bubble-lock', title: crypter ? '2xE2E' : 'E2E' }, crypter ? '🔒🔒' : '🔒'), + ecoChip ? span({ class: 'pm-bubble-eco' }, ecoChip) : null + ) + } + function actions({ key, replyId, subjectRaw, text }) { const stop = { onclick: 'event.stopPropagation()' } const subjectReply = /^(\s*RE:\s*)/i.test(subjectRaw || '') ? (subjectRaw || '') : `RE: ${subjectRaw || ''}` @@ -3278,6 +3320,7 @@ exports.privateView = async (messagesInput, filter, decrypted = null) => { const toLinks = Array.isArray(content.to) ? content.to.map(addr => linkAuthor(addr)) : [] const level = threadLevel(subjectRaw) const msgSize = msgSizeBytes(msg) + const isMob = process.env.OASIS_MOBILE === '1' if (subjectU === 'JOB_SUBSCRIBED' || subjectU === 'JOB_UNSUBSCRIBED') { return JobCard({ type: subjectU, sentAt, from: fromResolved, toLinks, text, key: msg.key, msgSize }) @@ -3295,23 +3338,48 @@ exports.privateView = async (messagesInput, filter, decrypted = null) => { if (content.crypter === true) { const dec = (decrypted && decrypted.key === msg.key) ? decrypted : null const shownText = dec && typeof dec.text === 'string' ? dec.text : '' + const cbody = dec && typeof dec.text === 'string' + ? div({ class: 'message-text', innerHTML: sanitizeHtml(clickableLinks(dec.text)) }) + : dec && dec.error + ? div({ class: 'pm-form-error-msg' }, p('✗ ' + i18n.pmCrypterBadKey)) + : null + const cform = dec && typeof dec.text === 'string' + ? null + : form({ method: 'POST', action: '/inbox/decrypt', class: 'pm-crypter-decrypt-form' }, + input({ type: 'hidden', name: 'id', value: msg.key }), + input({ type: 'hidden', name: 'returnFilter', value: filter || 'all' }), + input({ type: 'text', name: 'key', placeholder: 'd66d7d32f4d30f34812aee3d01347154', minlength: 32, maxlength: 32, size: 34, required: true, class: 'pm-crypter-key-input' }), + button({ type: 'submit', class: 'pm-btn pm-crypter-decrypt-btn' }, (i18n.pmCrypterDecryptButton || 'Decrypt').toUpperCase()) + ) + const cacts = actions({ key: msg.key, replyId: fromResolved, subjectRaw, text: shownText }) + if (isMob) { + const mine = isSent(msg) + return div( + { class: `pm-card normal-pm pm-crypter-card pm-bubble ${mine ? 'pm-sent' : 'pm-recv'} thread-level-${level}` }, + mine ? null : div({ class: 'pm-bubble-sender' }, linkAuthor(fromResolved)), + cbody, + cform, + bubbleMeta({ sentAt, subjectRaw, crypter: true, msgSize, msgKey: msg.key }), + cacts + ) + } return div( { class: 'pm-card normal-pm pm-crypter-card' }, headerLine({ sentAt, from: fromResolved, toLinks, subject: subjectRaw, msgKey: msg.key, msgSize, crypter: true }), - dec && typeof dec.text === 'string' - ? div({ class: 'message-text', innerHTML: sanitizeHtml(clickableLinks(dec.text)) }) - : dec && dec.error - ? div({ class: 'pm-form-error-msg' }, p('✗ ' + i18n.pmCrypterBadKey)) - : null, - dec && typeof dec.text === 'string' - ? null - : form({ method: 'POST', action: '/inbox/decrypt', class: 'pm-crypter-decrypt-form' }, - input({ type: 'hidden', name: 'id', value: msg.key }), - input({ type: 'hidden', name: 'returnFilter', value: filter || 'all' }), - input({ type: 'text', name: 'key', placeholder: 'd66d7d32f4d30f34812aee3d01347154', minlength: 32, maxlength: 32, size: 34, required: true, class: 'pm-crypter-key-input' }), - button({ type: 'submit', class: 'pm-btn pm-crypter-decrypt-btn' }, (i18n.pmCrypterDecryptButton || 'Decrypt').toUpperCase()) - ), - actions({ key: msg.key, replyId: fromResolved, subjectRaw, text: shownText }) + cbody, + cform, + cacts + ) + } + + if (isMob) { + const mine = isSent(msg) + return div( + { class: `pm-card normal-pm pm-bubble ${mine ? 'pm-sent' : 'pm-recv'} thread-level-${level}` }, + mine ? null : div({ class: 'pm-bubble-sender' }, linkAuthor(fromResolved)), + div({ class: 'message-text', innerHTML: sanitizeHtml(clickableLinks(text)) }), + bubbleMeta({ sentAt, subjectRaw, crypter: false, msgSize, msgKey: msg.key }), + actions({ key: msg.key, replyId: fromResolved, subjectRaw, text }) ) }