diff --git a/README.md b/README.md index c5014c2..4db4125 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,15 @@ -# Yo, Sup? - -Yo Sup? or simply "Yo" for short is a web client for the Nostr protocol. Its -aim is to be as good of an experience (if not better than) as Twitter. Note Yo -will not be the same as Twitter and will not implement all of it's features. -Nor will Yo try to implement all of Nostr's features as there are many. - -The true purpose of Yo is to provide a great experience on any platform for -anyone. It should be easy to use and understand making it a great option for -people coming from other social networks to engage in their community. - -Yo comes from the legacy Damus Web app an holds all of its history. It has been -rewritten to accomodate for the scale issues that we have seen so that it can -continue to be used. The main reason for branching off is due to the lack of -parity between Damus iOS (and new codebase improvements) and that of what the -web version would support. - -New minor features will continue to be added, but nothing substancial without -full time maintainers. Security will always be a top concern. +# Yo Sup [Issue Tracker](https://todo.sr.ht/~tomtom/damus-web-issues) -## Contribution Guide +"Yo Sup" is a minimal Nostr client that grew out of the original Damus Web +code. It's goal is to view your feed and access your direct messages very fast. +So fast it works over 3G with a fresh page load. It has no goals to fulfill any +other NIPs, please use other clients such as Snort, Coracle, or Iris. -There are rules to contributing to this client. Please ensure you read them -before making changes and supplying patch notes. +It's written in plain JavaScript, HTML, and CSS for ease of development and +building, see the example Dockerfile. Small features and optimizations will be +added as needed, but the application is considered "complete". - - No transpilers. All source code should work out of the box. - - Keep source code organised. Refer to the folder structure. If you have a - question, ask it. - - Do not include your personal tools in the source code. Use your own scripts - outside of the project. This does not include build tools such as Make. - - Use tabs & write JS with snake_case. End of discussion. - - Do not include binary files. - - No NPM (and kin) environments. If you need a file from an external resource - mark the location in the "sources" file and add it to the repo. - - No frameworks. Learn the browser tools and write good code. - - No experimental browser APIs. - - Do not write animations in JavaScript, CSS only. Keep them short and snappy. - Animations should not be a forefront, but an enjoyable addition. - - All new & modified code should be properly documented. - - Source code should be readable in the browser. - - Search for the TODOs. - -These rules are subject to discussion. - -## Terminology - - * Sign Out - Not "log out", "logout", "log off", etc. - * Sign In - Not "login", "log in", "signin", "sign-in", etc. - * Share - Not "boosted", "retweeted", "repost", etc. - * Send - Not "tweet", "toot", "post", etc. - * Link - Not "share". - -## Known Issues - - * You cannot send events when running from an IP address that is not secure. - Work arounds are not known at this time. +Patches are welcomed via email. diff --git a/css/responsive.css b/css/responsive.css index 9064b38..d30f23f 100644 --- a/css/responsive.css +++ b/css/responsive.css @@ -1,32 +1,29 @@ -@media (max-width: 800px){ - :root { - /* TODO font size should not be controlled by CSS: - * Instead I would prefer user settings. The main reason is the font is - * too small on my desktop when I use the app in column mode. - */ - --fsSmall: 10px; - --fsNormal: 14px; - --fsReduced: 12px; - --fsEnlarged: 16px; - } - +@media (max-width: 840px){ /* Utility */ .vertical-hide { display: none !important; } /* Application Framework */ - #gnav { - display: initial; - } #view { flex: 1; width: initial; border-right: none; } + .nav.mobile { + display: flex; + } #content header > label { padding: 12px; } + .nav.mobile .new-note { + position: fixed; + height: initial; + bottom: 88px; + right: 20px; + z-index: var(--zGlobal); + padding: 24px; + } /* Event */ .pfp { /* TODO sync up with userpic */ diff --git a/css/styles.css b/css/styles.css index 4a2d25e..246342f 100644 --- a/css/styles.css +++ b/css/styles.css @@ -45,13 +45,6 @@ th, td { text-align: right; } -#gsticker { - position: absolute; - top: 0; - left: 0; - padding: 15px; -} - /* Welcome */ #container-busy .loader { @@ -83,98 +76,53 @@ th, td { } /* Navigation */ -#nav { +.nav.full { border-right: 1px solid var(--clrBorder); padding: 10px; } -#nav > div { +.nav.full > div { position: sticky; top: 16px; display: flex; flex-flow: column; } -#nav > div > * { +.nav.full > div > * { margin-bottom: 20px; padding: 10px; position: relative; } -#nav > div[data-active] img.active { +.nav.mobile { + display: none; + background: var(--clrBg); + position: sticky; + bottom: 0; + z-index: var(--zHeader); + flex-direction: row; + border-top: var(--clrBorder) 1px solid; +} +.nav.mobile button { + padding: 18px; + flex: 1; +} +.nav [data-view].active img.inactive, +.nav [data-view] img.active { display: none; } -#nav > div[data-active="home"] [data-view="friends"] img.inactive, -#nav > div[data-active="explore"] [data-view="explore"] img.inactive, -#nav > div[data-active="notifications"] [data-view="notifications"] img.inactive, -#nav > div[data-active="settings"] [data-view="settings"] img.inactive, -#nav > div[data-active="messages"] [data-view="dm"] img.inactive { - display: none; -} -#nav > div[data-active="home"] [data-view="friends"] img.active, -#nav > div[data-active="explore"] [data-view="explore"] img.active, -#nav > div[data-active="notifications"] [data-view="notifications"] img.active, -#nav > div[data-active="settings"] [data-view="settings"] img.active, -#nav > div[data-active="messages"] [data-view="dm"] img.active { +.nav [data-view].active img.active { display: block; } -#new-note { - background: white; - height: 56px; - border-radius: 38px; -} - -#app-icon-logo > img { - width: 36px; - height: 36px; -} button.nav > img.icon { width: 28px; height: 28px; } -#gnav { - display: none; - position: fixed; - bottom: 55px; - right: 55px; - z-index: var(--zGlobal); +.nav button.new-note { + background: white; + height: 56px; + border-radius: 38px; } -#gnav button { - position: absolute; - top: 0; - left: 0; - font-size: 24px; - border-radius: 50%; - background: var(--clrText); - color: var(--clrBg); - padding: 10px; - border: transparent 5px solid; - transition: top 0.05s linear; - transform: translateX(-50%) translateY(-50%); - z-index: calc(var(--zGlobal) - 1); -} -#gnav button > .icon { - width: 28px; - height: 28px; -} -#gnav button[action="toggle-gnav"] { - z-index: var(--zGlobal); - padding: 15px; -} -#gnav.open button[data-view="friends"] { - top: -375px; -} -#gnav.open button[data-view="explore"] { - top: -300px; -} -#gnav.open button[data-view="dm"] { - top: -225px; -} -#gnav.open button[data-view="notifications"] { - top: -150px; -} -#gnav.open button[data-view="notifications"] .new-notifications { - right: 9px; -} -#gnav.open button[data-view="settings"] { - top: -75px; +#app-icon-logo > img { + width: 36px; + height: 36px; } .new-notifications { @@ -195,24 +143,29 @@ button.nav > img.icon { flex-flow: row; } #view { + display: flex; + flex-direction: column; flex-shrink: 0; border-right: 1px solid var(--clrBorder); width: 750px; min-height: 100vh; } -#view > div > header { +#view > header { position: sticky; top: 0; z-index: var(--zHeader); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); } -#view > div > header > label { +#view > header > label { padding: 15px; font-size: 22px; font-weight: 800; display: block; } +#timeline, #settings, #dms { + flex: 1; +} #header-tools { display: flex; position: absolute; @@ -551,7 +504,7 @@ code { } #settings header > label { font-weight: bold; - font-size: var(--fsEnlarged); + font-size: var(--fsLarge); } /* Messaging */ diff --git a/css/vars.css b/css/vars.css index ba11447..381b701 100644 --- a/css/vars.css +++ b/css/vars.css @@ -27,6 +27,7 @@ --fsReduced: 14px; --fsNormal: 16px; --fsEnlarged: 18px; + --fsLarge: 22px; /* Font Families */ --ffDefault: "Noto Sans", sans-serif; diff --git a/index.html b/index.html index 87c3b19..bd58e76 100644 --- a/index.html +++ b/index.html @@ -44,39 +44,18 @@ Yo, Sup? -

The blue bird experience for Nostr.

- +

A minimal experience for Nostr.

+

Please access with a nos2x compatible browser.

- +
-
diff --git a/js/main.js b/js/main.js index 56ee41d..3528f39 100644 --- a/js/main.js +++ b/js/main.js @@ -81,10 +81,6 @@ async function webapp_init() { // Load data from storage await model_load_settings(model); - /*err = await contacts_load(model); - if (err) { - window.alert("Unable to load contacts."); - }*/ init_settings(model); // Create our pool so that event processing functions can work @@ -96,14 +92,6 @@ async function webapp_init() { pool.on("eose", on_pool_eose); pool.on("ok", on_pool_ok); - // Load all events from storage and re-process them so that apply correct - // effects. - /*await model_load_events(model, (ev)=> { - model_process_event(model, undefined, ev); - }); - log_debug("loaded events", Object.keys(model.all_events).length); - */ - var { mode, opts, valid } = parse_url_mode(); view_timeline_apply_mode(model, mode, opts, !valid); on_timer_timestamps(); @@ -171,7 +159,7 @@ function on_timer_save() { setTimeout(() => { const model = DAMUS; //model_save_events(model); - model_save_settings(model); + //model_save_settings(model); on_timer_save(); }, 1 * 1000); } @@ -300,6 +288,7 @@ function fetch_profile(pubkey, pool, relay) { } function fetch_thread_history(evid, pool) { + // TODO look up referenced relays for thread history const sid = `${SID_THREAD}:${evid}` pool.subscribe(sid, [{ kinds: PUBLIC_KINDS, @@ -309,8 +298,7 @@ function fetch_thread_history(evid, pool) { } function fetch_friends_history(friends, pool, relay) { - // TODO only fetch friends history from their desired relay instead of - // pinging all of the relays + // TODO fetch history of each friend by their desired relay pool.subscribe(SID_FRIENDS, [{ kinds: PUBLIC_KINDS, authors: friends, diff --git a/js/ui/settings.js b/js/ui/settings.js index 2827b56..1c3ed43 100644 --- a/js/ui/settings.js +++ b/js/ui/settings.js @@ -1,21 +1,12 @@ function init_settings(model) { const el = find_node("#settings"); find_node("#add-relay", el).addEventListener("click", on_click_add_relay); - find_node("[role='sign-out']", el).addEventListener("click", on_click_sign_out); const rlist = find_node("#relay-list tbody", el); model.relays.forEach((str) => { rlist.appendChild(new_relay_item(str)); }); } -async function on_click_sign_out(ev) { - if (confirm("Are you sure you want to sign out?")) { - localStorage.clear(); - await dbclear(); - window.location.reload(); - } -} - function new_relay_item(str) { const tr = document.createElement('tr'); tr.innerHTML = `${str} diff --git a/js/ui/state.js b/js/ui/state.js index f85300d..2486ed9 100644 --- a/js/ui/state.js +++ b/js/ui/state.js @@ -115,17 +115,14 @@ function view_timeline_apply_mode(model, mode, opts={}, push_state=true) { // Do some visual updates find_node("#show-more").classList.add("hide"); - find_node("#view header > label").innerText = name; - find_node("#nav > div[data-active]").dataset.active = names[mode].toLowerCase(); + find_node("#view header > label").innerText = name; + view_update_navs(mode); find_node("#view [role='profile-info']").classList.toggle("hide", mode != VM_USER); const timeline_el = find_node("#timeline"); timeline_el.classList.toggle("reverse", mode == VM_THREAD); timeline_el.classList.toggle("hide", mode == VM_SETTINGS || mode == VM_DM); find_node("#settings").classList.toggle("hide", mode != VM_SETTINGS); find_node("#dms").classList.toggle("hide", mode != VM_DM); - find_node("#dms-not-available") - .classList.toggle("hide", mode == VM_DM_THREAD || mode == VM_DM ? - dms_available() : true); find_node("#header-tools button[action='mark-all-read']") .classList.toggle("hide", mode != VM_DM); @@ -220,6 +217,12 @@ function view_timeline_refresh(model, mode, opts={}) { } } +function view_update_navs(mode) { + find_nodes("nav.nav button[data-view]").forEach((el)=> { + el.classList.toggle("active", el.dataset.view == mode) + }); +} + function view_show_spinner(show=true) { find_node("#view .loading-events").classList.toggle("hide", !show); } @@ -546,7 +549,6 @@ function get_thread_root_id(damus, id) { function switch_view(mode, opts) { view_timeline_apply_mode(DAMUS, mode, opts); - close_gnav(); } function toggle_hide_replys(el) { @@ -668,9 +670,6 @@ function onclick_any(ev) { const el = ev.target; const action = el.getAttribute("action"); switch (action) { - case "toggle-gnav": - toggle_gnav(el); - break; case "sign-in": signin(); break; diff --git a/js/ui/util.js b/js/ui/util.js index 66c88ea..fccfbb3 100644 --- a/js/ui/util.js +++ b/js/ui/util.js @@ -3,17 +3,6 @@ * this file grows specific UI area code should be migrated to its own file. */ -/* toggle_gnav hides or shows the global navigation's additional buttons based - * on its opened state. - */ -function toggle_gnav(el) { - el.parentElement.classList.toggle("open"); -} - -function close_gnav() { - find_node("#gnav").classList.remove("open"); -} - /* init_message_textareas finds all message textareas and updates their initial * height based on their content (0). This is so there is no jaring affect when * the page loads.