From 5dc974112c886d1f78a1d42eb2ff50efbea0ce33 Mon Sep 17 00:00:00 2001 From: fang Date: Mon, 9 Sep 2024 22:58:11 +0200 Subject: [PATCH 001/157] channel-utils: provide standardized story->manx We implement utility functions for server-side rendering of channel contents into html nodes. The html it generates structurally matches what the frontend currently composes, but otherwise tries to stay as neutral and "clean" as possible, only adding class names where they're necessary for unique identifiability of semantically distinct elements. Not entirely complete yet. --- desk/lib/channel-utils.hoon | 175 ++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index 0341ecef07..797b5dadbe 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -416,4 +416,179 @@ .^($-([ship nest] ?) %gx path) (test her nest) -- +:: +++ en-manx ::NOTE more commonly, marl, but that's just (list manx) + |% + ++ content story + ++ story + |= content=story:c + ^- marl + (zing (turn content verse)) + :: + ++ verse + |= =verse:c + ^- marl + ?- -.verse + %block (block p.verse) + :: + %inline + ;+ + ?: ?=([[%break ~] ~] p.verse) + ;br; + ;p + ;* (turn p.verse inline) + == + == + :: + ++ block + |= =block:c + ^- marl + ?- -.block + %image + ;+ + =/ src=tape (trip src.block) + ;div.image + ;a/"{src}"(target "_blank", rel "noreferrer") + ;img@"{src}" + =height "{(a-co:co height.block)}" + =width "{(a-co:co width.block)}" + =alt "{?:(=('' alt.block) "image" (trip alt.block))}"; + == + == + :: + %cite + ;+ + ;div.cite + ; [reference xx] ::TODO link to /expose if chan ref? + == + :: + %header + ;+ + ?- p.block + %h1 ;h1 ;* (turn q.block inline) == + %h2 ;h2 ;* (turn q.block inline) == + %h3 ;h3 ;* (turn q.block inline) == + %h4 ;h4 ;* (turn q.block inline) == + %h5 ;h5 ;* (turn q.block inline) == + %h6 ;h6 ;* (turn q.block inline) == + == + :: + %listing + ?- -.p.block + %item + |- ^- marl + ?: ?=([[%break ~] ~] p.p.block) + ~ :: filter out trailing newlines + ?~ p.p.block ~ + :- (inline i.p.p.block) + $(p.p.block t.p.p.block) + :: + %list + %+ weld + `marl`(turn r.p.block inline) + ^- marl + ;+ + ?- p.p.block + %ordered + ;ol + ;* %+ turn q.p.block + |= l=listing:c + ;li + ;* (^block %listing l) + == + == + :: + %unordered + ;ul + ;* %+ turn q.p.block + |= l=listing:c + ;li + ;* (^block %listing l) + == + == + :: + %tasklist + ;ul.tasklist + ;* %+ turn q.p.block + |= l=listing:c + ;li + ;* (^block %listing l) + == + == + == + == + :: + %rule + ;+ ;hr; + :: + %code + ;+ + ;pre + ;code:"{(trip code.block)}" + == + == + :: + ++ inline + |= =inline:c + ^- manx + ?@ inline + ;span:"{(trip inline)}" + ?- -.inline + %italics + ;em + ;* (turn p.inline ^inline) + == + :: + %bold + ;strong + ;* (turn p.inline ^inline) + == + :: + %strike + ;s + ;* (turn p.inline ^inline) + == + :: + %blockquote + ;blockquote + ;* (turn p.inline ^inline) + == + :: + %inline-code + ;code.inline-code:"{(trip p.inline)}" + :: + %code + ;pre.code + ;code:"{(trip p.inline)}" + == + :: + %ship + ;span.ship:"{(scow %p p.inline)}" + :: + %block + ;span.block:"[block xx]" + :: + %tag + ;span.tag:"[tag xx]" + :: + %link + ::TODO prefix // if no protocol in url + =/ url=tape (trip p.inline) + ;a/"{url}" + =target "_blank" + =rel "noreferrer" + ; "{?:(=('' q.inline) url (trip q.inline))}" + == + :: + %task + ;div.task + ;+ ?. p.inline ;input(type "checkbox", disabled ""); + ;input(type "checkbox", checked "", disabled ""); + ;* (turn q.inline ^inline) + == + :: + %break + ;br; + == + -- -- From 9f69e045901411ace740155f56c751c627620ebf Mon Sep 17 00:00:00 2001 From: fang Date: Mon, 9 Sep 2024 23:05:43 +0200 Subject: [PATCH 002/157] =?UTF-8?q?expos=C3=A9:=20clearweb=20content=20ren?= =?UTF-8?q?dering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Work in progress, minimal viable clearweb content publishing utility. Poke it with a $cite style reference path, and it'll start responding to http requests on /expose/that/path. Currently only supports notebook posts. No styling, no comments, no nothing. Does have some preliminary metadata headers, importantly including instructions for robots. --- desk/app/expose.hoon | 220 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 desk/app/expose.hoon diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon new file mode 100644 index 0000000000..6722847994 --- /dev/null +++ b/desk/app/expose.hoon @@ -0,0 +1,220 @@ +:: exposé: clearweb content rendering +:: +:: copy a reference to a notebook post, then: +:: :expose [%show /that/reference/with/id/numbers/quoted/'123456789'] +:: then visit in the browser: +:: /expose/that/reference/as/copied/123456789 +:: +/- c=cite, d=channels +/+ sigil, u=channel-utils, + dbug, verb +:: +|% ++$ state-0 + $: %0 + open=(set cite:c) ::TODO could support ranges of msgs? + == +:: ++$ action + $% [%show =path] + [%hide =path] + == +:: ++$ card card:agent:gall +:: +++ hutils :: http request utils + ::NOTE most of the below are also available in /lib/server, but we + :: reimplement them here for independence's sake + |% + +$ order [id=@ta inbound-request:eyre] + +$ query [trail args=(list [key=@t value=@t])] + +$ trail [ext=(unit @ta) site=(list @t)] + +$ reply + $% [%page bod=manx] :: html page + [%xtra hed=header-list:http bod=manx] :: html page w/ heads + == + :: + ++ purse :: url cord to query + |= url=@t + ^- query + (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) + :: + ++ press :: manx to octs + (cork en-xml:html as-octt:mimes:html) + :: + ++ paint :: render response into payload + |= =reply + ^- simple-payload:http + ?- -.reply + %page [[200 ['content-type' 'text/html']~] `(press bod.reply)] + %xtra =? hed.reply ?=(~ (get-header:http 'content-type' hed.reply)) + ['content-type'^'text/html' hed.reply] + [[200 hed.reply] `(press bod.reply)] + == + :: + ++ spout :: build full response cards + |= [eyre-id=@ta simple-payload:http] + ^- (list card) + =/ =path /http-response/[eyre-id] + :~ [%give %fact ~[path] [%http-response-header !>(response-header)]] + [%give %fact ~[path] [%http-response-data !>(data)]] + [%give %kick ~[path] ~] + == + -- +-- +:: +%- agent:dbug +%+ verb | +^- agent:gall +:: +=| state-0 +=* state - +|_ =bowl:gall ++* this . +++ on-init + ^- (quip card _this) + :_ this + [%pass /eyre/connect %arvo %e %connect [~ /expose] dap.bowl]~ +:: +++ on-save !>(state) +++ on-load + |= ole=vase + ^- (quip card _this) + [~ this(state !<(state-0 ole))] +:: +++ on-poke + |= [=mark =vase] + ^- (quip card _this) + ?+ mark !! + %noun + ?+ q.vase !! + [?(%show %hide) *] + =+ !<(act=action vase) + =. open + ?- -.act + %show (~(put in open) (parse:c path.act)) ::TODO populate cache + %hide (~(del in open) (parse:c path.act)) ::TODO update the cache w/ 404 + == + [~ this] + == + :: + %handle-http-request + =+ !<([rid=@ta inbound-request:eyre] vase) + :_ this + =; payload=simple-payload:http + ::TODO re-enable caching + :: :_ + (spout:hutils rid payload) + :: if we handled a request here, make sure it's cached for next time + :: + :: [%pass /eyre/cache %arvo %e %set-response url.request `[| %payload payload]] + =/ ref=(unit cite:c) + (rush url.request (sear purse:c ;~(pfix (jest '/expose') stap))) + ?~ ref + [[400 ~] `(as-octs:mimes:html 'bad request')] + :: + =; bod=(unit manx) + ?~ bod [[404 ~] `(as-octs:mimes:html 'not found')] + (paint:hutils %page u.bod) + :: + ?. (~(has in open) u.ref) + ~ + ?. ?=(%chan -.u.ref) + ~ + ::TODO the whole "deconstruct the ref path" situation is horrendous + ?. ?=([?(%msg %note) @ ~] wer.u.ref) ::TODO support chat msgs, replies + ~ + :: /1/chan/chat/~bolbex-fogdys/watercooler-4926/msg/170141184506984515746528913634457812992 + :: /v2/chat/~bolbex-fogdys/watercooler-4926/posts/post/[id] + :: /v2/chat/~bolbex-fogdys/watercooler-4926/posts/post/id/[id]/replies/[id] + =/ msg=(unit post:d) + :- ~ ::TODO we want to do existence checks first though... + .^ post:d + %gx + (scot %p our.bowl) + %channels + (scot %da now.bowl) + :: + =, u.ref + /v2/[p.nest]/(scot %p p.q.nest)/[q.q.nest]/posts/post/(scot %ud (rash i.t.wer dum:ag))/channel-post-2 + == + ?~ msg + ~ + ::TODO if we render replies then we can "unroll" whole chat threads too (: + |^ ?+ p.nest.u.ref ~ ::TODO support rendering others + %diary + ?> ?=(%diary -.kind-data.u.msg) + =/ title=tape (trip title.kind-data.u.msg) + %- some + ^- manx + ;html + ;+ (heads title (scow %p author.u.msg)) + ;body + ;* (diary-prelude title author.u.msg sent.u.msg) + ;* (story:en-manx:u content.u.msg) + == + == + == + :: + ++ style + ''' + TODO + ''' + :: + ++ heads + |= [title=tape author=tape] + ;head + ;title:"{title}" + ;style:"{(trip style)}" + ;meta(charset "utf-8"); + ;meta(name "viewport", content "width=device-width, initial-scale=1"); + :: + ;meta(name "robots", content "noindex, nofollow, noimageindex"); + :: + ::TODO could get smarter about description, preview image, etc + ;meta(property "og:title", content title); + ;meta(property "twitter:title", content title); + ;meta(property "og:site_name", content "Tlon"); + ;meta(property "og:type", content "article"); + ;meta(property "twitter:card", content "summary"); + ;meta(property "og:article:author:username", content author); + == + :: + ++ diary-prelude + |= [title=tape author=ship sent=@da] + ^- marl + :~ ;h1:"{title}" + ;div.prelude + ;+ (sigil(size 25, icon &) author) + ; {(scow %p author)} + ;em:"{(scow %da (sub sent (mod sent ~s1)))}" + == + == + -- + == +:: +++ on-watch + |= =path + ^- (quip card _this) + ?> ?=([%http-response @ ~] path) + [~ this] +:: +++ on-arvo + |= [=wire sign=sign-arvo] + ^- (quip card _this) + ~| wire + ?+ wire !! + [%eyre %connect ~] + [~ this] ::TODO print if not successful + == +:: +++ on-leave |=(* [~ this]) +++ on-agent |=(* [~ this]) +++ on-peek |=(* ~) +:: +++ on-fail + |= [=term =tang] + ^- (quip card _this) + %. [~ this] + (slog dap.bowl 'on-fail' term tang) +-- From 5ee52c1b4634b02ead4af3b56945487679e9812c Mon Sep 17 00:00:00 2001 From: James Acklin Date: Wed, 11 Sep 2024 11:41:02 -0400 Subject: [PATCH 003/157] expose: text styling for entries --- desk/app/expose.hoon | 1691 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1687 insertions(+), 4 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 6722847994..39ccf6f449 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -152,13 +152,1679 @@ ;body ;* (diary-prelude title author.u.msg sent.u.msg) ;* (story:en-manx:u content.u.msg) + ;+ diary-coda == == == :: ++ style ''' - TODO + :root { + --background-body: #FFFFFF; + --background: #FFFFFF; + --background-alt: #F5F5F5; + --selection: rgba(24, 24, 24, 0.08); + --text-main: #1A1818; + --text-bright: #1A1818; + --text-muted: #666666; + --links: #008EFF; + --focus: #008EFF; + --border: #E5E5E5; + --code: #1A1818; + --animation-duration: 0.1s; + --button-base: #F5F5F5; + --button-hover: #E5E5E5; + --scrollbar-thumb: rgb(244, 244, 244); + --scrollbar-thumb-hover: var(--button-hover); + --form-placeholder: #999999; + --form-text: #1A1818; + --variable: #2AD546; + --highlight: #FADE7A; + --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); + } + + @media (prefers-color-scheme: dark) { + :root { + --background-body: #1A1818; + --background: #1A1818; + --background-alt: #322E2E; + --selection: rgba(255, 255, 255, 0.08); + --text-main: #FFFFFF; + --text-bright: #fff; + --text-muted: #B3B3B3; + --links: #008EFF; + --focus: #008EFF; + --border: #333333; + --code: #FFFFFF; + --animation-duration: 0.1s; + --button-base: #322E2E; + --button-hover: #4C4C4C; + --scrollbar-thumb: var(--button-hover); + --scrollbar-thumb-hover: rgb(0, 0, 0); + --form-placeholder: #808080; + --form-text: #fff; + --variable: #2AD546; + --highlight: #FADE7A; + --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); + } + } + + html { + scrollbar-color: rgb(244, 244, 244) #FFFFFF; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-width: thin; + } + + @media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4C4C4C #1A1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } + } + + @media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4C4C4C #1A1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } + } + + @media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4C4C4C #1A1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } + } + + @media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4C4C4C #1A1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } + } + + @media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4C4C4C #1A1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } + } + + @media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4C4C4C #1A1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } + } + + body { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', sans-serif; + font-size: 20px; + font-weight: 400; + line-height: 1.8; + max-width: 800px; + margin: 20px auto; + padding: 0 10px; + word-wrap: break-word; + color: #1A1818; + color: var(--text-main); + background: #FFFFFF; + background: var(--background-body); + text-rendering: optimizeLegibility; + } + + @media (prefers-color-scheme: dark) { + body { + background: #1A1818; + background: var(--background-body); + } + } + + @media (prefers-color-scheme: dark) { + + body { + color: #FFFFFF; + color: var(--text-main); + } + } + + button { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } + + @media (prefers-color-scheme: dark) { + button { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } + } + + input { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } + + @media (prefers-color-scheme: dark) { + input { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } + } + + textarea { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } + + @media (prefers-color-scheme: dark) { + textarea { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } + } + + h1 { + margin-top: 0; + font-size: 2em; + font-weight: 400; + letter-spacing: -0.025em; + margin-top: 0; + margin-bottom: 12px; + } + + h2, + h3, + h4, + h5, + h6 { + margin-bottom: 12px; + margin-top: 24px; + font-size: 1em; + } + + h1 { + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + h1 { + color: #fff; + color: var(--text-bright); + } + } + + h2 { + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + h2 { + color: #fff; + color: var(--text-bright); + } + } + + h3 { + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + h3 { + color: #fff; + color: var(--text-bright); + } + } + + h4 { + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + h4 { + color: #fff; + color: var(--text-bright); + } + } + + h5 { + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + h5 { + color: #fff; + color: var(--text-bright); + } + } + + h6 { + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + h6 { + color: #fff; + color: var(--text-bright); + } + } + + strong { + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + strong { + color: #fff; + color: var(--text-bright); + } + } + + h2, + h3, + h4, + h5, + h6, + b, + strong, + th { + font-weight: 600; + } + + q::before { + content: none; + } + + q::after { + content: none; + } + + blockquote { + border-left: 4px solid #008EFF; + border-left: 4px solid var(--focus); + margin: 1.5em 0; + padding: 0.5em 1em; + font-style: italic; + } + + @media (prefers-color-scheme: dark) { + blockquote { + border-left: 4px solid #008EFF; + border-left: 4px solid var(--focus); + } + } + + q { + border-left: 4px solid #008EFF; + border-left: 4px solid var(--focus); + margin: 1.5em 0; + padding: 0.5em 1em; + font-style: italic; + } + + @media (prefers-color-scheme: dark) { + + q { + border-left: 4px solid #008EFF; + border-left: 4px solid var(--focus); + } + } + + blockquote > footer { + font-style: normal; + border: 0; + } + + blockquote cite { + font-style: normal; + } + + address { + font-style: normal; + } + + a[href^='mailto\:']::before { + content: '📧 '; + } + + a[href^='tel\:']::before { + content: '📞 '; + } + + a[href^='sms\:']::before { + content: '💬 '; + } + + mark { + background-color: #FADE7A; + background-color: var(--highlight); + border-radius: 2px; + padding: 0 2px 0 2px; + color: #000; + } + + @media (prefers-color-scheme: dark) { + + mark { + background-color: #FADE7A; + background-color: var(--highlight); + } + } + + a > code, + a > strong { + color: inherit; + } + + button, + select, + input[type='submit'], + input[type='reset'], + input[type='button'], + input[type='checkbox'], + input[type='range'], + input[type='radio'] { + cursor: pointer; + } + + input, + select { + display: block; + } + + [type='checkbox'], + [type='radio'] { + display: initial; + } + + input { + color: #1A1818; + color: var(--form-text); + background-color: #FFFFFF; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 6px; + margin-bottom: 6px; + padding: 10px; + border: none; + border-radius: 6px; + outline: none; + } + + @media (prefers-color-scheme: dark) { + input { + background-color: #1A1818; + background-color: var(--background); + } + } + + @media (prefers-color-scheme: dark) { + input { + color: #fff; + color: var(--form-text); + } + } + + button { + color: #1A1818; + color: var(--form-text); + background-color: #FFFFFF; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 6px; + margin-bottom: 6px; + padding: 10px; + border: none; + border-radius: 6px; + outline: none; + } + + @media (prefers-color-scheme: dark) { + button { + background-color: #1A1818; + background-color: var(--background); + } + } + + @media (prefers-color-scheme: dark) { + button { + color: #fff; + color: var(--form-text); + } + } + + textarea { + color: #1A1818; + color: var(--form-text); + background-color: #FFFFFF; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 6px; + margin-bottom: 6px; + padding: 10px; + border: none; + border-radius: 6px; + outline: none; + } + + @media (prefers-color-scheme: dark) { + textarea { + background-color: #1A1818; + background-color: var(--background); + } + } + + @media (prefers-color-scheme: dark) { + textarea { + color: #fff; + color: var(--form-text); + } + } + + select { + color: #1A1818; + color: var(--form-text); + background-color: #FFFFFF; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 6px; + margin-bottom: 6px; + padding: 10px; + border: none; + border-radius: 6px; + outline: none; + } + + @media (prefers-color-scheme: dark) { + select { + background-color: #1A1818; + background-color: var(--background); + } + } + + @media (prefers-color-scheme: dark) { + select { + color: #fff; + color: var(--form-text); + } + } + + button { + background-color: #F5F5F5; + background-color: var(--button-base); + padding-right: 30px; + padding-left: 30px; + } + + @media (prefers-color-scheme: dark) { + button { + background-color: #322E2E; + background-color: var(--button-base); + } + } + + input[type='submit'] { + background-color: #F5F5F5; + background-color: var(--button-base); + padding-right: 30px; + padding-left: 30px; + } + + @media (prefers-color-scheme: dark) { + input[type='submit'] { + background-color: #322E2E; + background-color: var(--button-base); + } + } + + input[type='reset'] { + background-color: #F5F5F5; + background-color: var(--button-base); + padding-right: 30px; + padding-left: 30px; + } + + @media (prefers-color-scheme: dark) { + input[type='reset'] { + background-color: #322E2E; + background-color: var(--button-base); + } + } + + input[type='button'] { + background-color: #F5F5F5; + background-color: var(--button-base); + padding-right: 30px; + padding-left: 30px; + } + + @media (prefers-color-scheme: dark) { + input[type='button'] { + background-color: #322E2E; + background-color: var(--button-base); + } + } + + button:hover { + background: #E5E5E5; + background: var(--button-hover); + } + + @media (prefers-color-scheme: dark) { + button:hover { + background: #4C4C4C; + background: var(--button-hover); + } + } + + input[type='submit']:hover { + background: #E5E5E5; + background: var(--button-hover); + } + + @media (prefers-color-scheme: dark) { + input[type='submit']:hover { + background: #4C4C4C; + background: var(--button-hover); + } + } + + input[type='reset']:hover { + background: #E5E5E5; + background: var(--button-hover); + } + + @media (prefers-color-scheme: dark) { + input[type='reset']:hover { + background: #4C4C4C; + background: var(--button-hover); + } + } + + input[type='button']:hover { + background: #E5E5E5; + background: var(--button-hover); + } + + @media (prefers-color-scheme: dark) { + input[type='button']:hover { + background: #4C4C4C; + background: var(--button-hover); + } + } + + input[type='color'] { + min-height: 2rem; + padding: 8px; + cursor: pointer; + } + + input[type='checkbox'], + input[type='radio'] { + height: 1em; + width: 1em; + } + + input[type='radio'] { + border-radius: 100%; + } + + input { + vertical-align: top; + } + + label { + vertical-align: middle; + margin-bottom: 4px; + display: inline-block; + } + + input:not([type='checkbox']):not([type='radio']), + input[type='range'], + select, + button, + textarea { + -webkit-appearance: none; + } + + textarea { + display: block; + margin-right: 0; + box-sizing: border-box; + resize: vertical; + } + + textarea:not([cols]) { + width: 100%; + } + + textarea:not([rows]) { + min-height: 40px; + height: 140px; + } + + select { + background: #FFFFFF url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + padding-right: 35px; + } + + @media (prefers-color-scheme: dark) { + select { + background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + } + } + + @media (prefers-color-scheme: dark) { + select { + background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + } + } + + @media (prefers-color-scheme: dark) { + select { + background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + } + } + + @media (prefers-color-scheme: dark) { + select { + background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + } + } + + select::-ms-expand { + display: none; + } + + select[multiple] { + padding-right: 10px; + background-image: none; + overflow-y: auto; + } + + input:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + + @media (prefers-color-scheme: dark) { + input:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + } + + select:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + + @media (prefers-color-scheme: dark) { + + select:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + } + + button:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + + @media (prefers-color-scheme: dark) { + + button:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + } + + textarea:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + + @media (prefers-color-scheme: dark) { + + textarea:focus { + box-shadow: 0 0 0 2px #008EFF; + box-shadow: 0 0 0 2px var(--focus); + } + } + + input[type='checkbox']:active, + input[type='radio']:active, + input[type='submit']:active, + input[type='reset']:active, + input[type='button']:active, + input[type='range']:active, + button:active { + transform: translateY(2px); + } + + input:disabled, + select:disabled, + button:disabled, + textarea:disabled { + cursor: not-allowed; + opacity: 0.5; + } + + ::-moz-placeholder { + color: #999999; + color: var(--form-placeholder); + } + + :-ms-input-placeholder { + color: #999999; + color: var(--form-placeholder); + } + + ::-ms-input-placeholder { + color: #999999; + color: var(--form-placeholder); + } + + ::placeholder { + color: #999999; + color: var(--form-placeholder); + } + + @media (prefers-color-scheme: dark) { + ::-moz-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + :-ms-input-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + ::-ms-input-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + ::placeholder { + color: #808080; + color: var(--form-placeholder); + } + } + + fieldset { + border: 1px #008EFF solid; + border: 1px var(--focus) solid; + border-radius: 6px; + margin: 0; + margin-bottom: 12px; + padding: 10px; + } + + @media (prefers-color-scheme: dark) { + + fieldset { + border: 1px #008EFF solid; + border: 1px var(--focus) solid; + } + } + + legend { + font-size: 0.9em; + font-weight: 600; + } + + input[type='range'] { + margin: 10px 0; + padding: 10px 0; + background: transparent; + } + + input[type='range']:focus { + outline: none; + } + + input[type='range']::-webkit-slider-runnable-track { + width: 100%; + height: 9.5px; + -webkit-transition: 0.2s; + transition: 0.2s; + background: #FFFFFF; + background: var(--background); + border-radius: 3px; + } + + @media (prefers-color-scheme: dark) { + input[type='range']::-webkit-slider-runnable-track { + background: #1A1818; + background: var(--background); + } + } + + input[type='range']::-webkit-slider-thumb { + box-shadow: 0 1px 1px #000, 0 0 1px #0d0d0d; + height: 20px; + width: 20px; + border-radius: 50%; + background: #E5E5E5; + background: var(--border); + -webkit-appearance: none; + margin-top: -7px; + } + + @media (prefers-color-scheme: dark) { + input[type='range']::-webkit-slider-thumb { + background: #333333; + background: var(--border); + } + } + + input[type='range']:focus::-webkit-slider-runnable-track { + background: #FFFFFF; + background: var(--background); + } + + @media (prefers-color-scheme: dark) { + input[type='range']:focus::-webkit-slider-runnable-track { + background: #1A1818; + background: var(--background); + } + } + + input[type='range']::-moz-range-track { + width: 100%; + height: 9.5px; + -moz-transition: 0.2s; + transition: 0.2s; + background: #FFFFFF; + background: var(--background); + border-radius: 3px; + } + + @media (prefers-color-scheme: dark) { + input[type='range']::-moz-range-track { + background: #1A1818; + background: var(--background); + } + } + + input[type='range']::-moz-range-thumb { + box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; + height: 20px; + width: 20px; + border-radius: 50%; + background: #E5E5E5; + background: var(--border); + } + + @media (prefers-color-scheme: dark) { + input[type='range']::-moz-range-thumb { + background: #333333; + background: var(--border); + } + } + + input[type='range']::-ms-track { + width: 100%; + height: 9.5px; + background: transparent; + border-color: transparent; + border-width: 16px 0; + color: transparent; + } + + input[type='range']::-ms-fill-lower { + background: #FFFFFF; + background: var(--background); + border: 0.2px solid #010101; + border-radius: 3px; + box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; + } + + @media (prefers-color-scheme: dark) { + input[type='range']::-ms-fill-lower { + background: #1A1818; + background: var(--background); + } + } + + input[type='range']::-ms-fill-upper { + background: #FFFFFF; + background: var(--background); + border: 0.2px solid #010101; + border-radius: 3px; + box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; + } + + @media (prefers-color-scheme: dark) { + input[type='range']::-ms-fill-upper { + background: #1A1818; + background: var(--background); + } + } + + input[type='range']::-ms-thumb { + box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; + border: 1px solid #000; + height: 20px; + width: 20px; + border-radius: 50%; + background: #E5E5E5; + background: var(--border); + } + + @media (prefers-color-scheme: dark) { + input[type='range']::-ms-thumb { + background: #333333; + background: var(--border); + } + } + + input[type='range']:focus::-ms-fill-lower { + background: #FFFFFF; + background: var(--background); + } + + @media (prefers-color-scheme: dark) { + + input[type='range']:focus::-ms-fill-lower { + background: #1A1818; + background: var(--background); + } + } + + input[type='range']:focus::-ms-fill-upper { + background: #FFFFFF; + background: var(--background); + } + + @media (prefers-color-scheme: dark) { + + input[type='range']:focus::-ms-fill-upper { + background: #1A1818; + background: var(--background); + } + } + + a { + text-decoration: underline; + color: #008EFF; + color: var(--links); + } + + @media (prefers-color-scheme: dark) { + + a { + color: #008EFF; + color: var(--links); + } + } + + a:hover { + text-decoration: underline; + } + + code { + background: #FFFFFF; + background: var(--background); + color: #1A1818; + color: var(--code); + padding: 2.5px 5px; + border-radius: 6px; + font-size: 1em; + } + + @media (prefers-color-scheme: dark) { + + code { + color: #FFFFFF; + color: var(--code); + } + } + + @media (prefers-color-scheme: dark) { + + code { + background: #1A1818; + background: var(--background); + } + } + + samp { + background: #FFFFFF; + background: var(--background); + color: #1A1818; + color: var(--code); + padding: 2.5px 5px; + border-radius: 6px; + font-size: 1em; + } + + @media (prefers-color-scheme: dark) { + + samp { + color: #FFFFFF; + color: var(--code); + } + } + + @media (prefers-color-scheme: dark) { + + samp { + background: #1A1818; + background: var(--background); + } + } + + time { + background: #FFFFFF; + background: var(--background); + color: #1A1818; + color: var(--code); + padding: 2.5px 5px; + border-radius: 6px; + font-size: 1em; + } + + @media (prefers-color-scheme: dark) { + + time { + color: #FFFFFF; + color: var(--code); + } + } + + @media (prefers-color-scheme: dark) { + + time { + background: #1A1818; + background: var(--background); + } + } + + pre > code { + padding: 10px; + display: block; + overflow-x: auto; + } + + var { + color: #2AD546; + color: var(--variable); + font-style: normal; + font-family: monospace; + } + + @media (prefers-color-scheme: dark) { + + var { + color: #2AD546; + color: var(--variable); + } + } + + kbd { + background: #FFFFFF; + background: var(--background); + border: 1px solid #E5E5E5; + border: 1px solid var(--border); + border-radius: 2px; + color: #1A1818; + color: var(--text-main); + padding: 2px 4px 2px 4px; + } + + @media (prefers-color-scheme: dark) { + + kbd { + color: #FFFFFF; + color: var(--text-main); + } + } + + @media (prefers-color-scheme: dark) { + + kbd { + border: 1px solid #333333; + border: 1px solid var(--border); + } + } + + @media (prefers-color-scheme: dark) { + + kbd { + background: #1A1818; + background: var(--background); + } + } + + img, + video { + max-width: 100%; + height: auto; + } + + hr { + border: none; + border-top: 1px solid #E5E5E5; + border-top: 1px solid var(--border); + } + + @media (prefers-color-scheme: dark) { + + hr { + border-top: 1px solid #333333; + border-top: 1px solid var(--border); + } + } + + table { + border-collapse: collapse; + margin-bottom: 10px; + width: 100%; + table-layout: fixed; + } + + table caption { + text-align: left; + } + + td, + th { + padding: 6px; + text-align: left; + vertical-align: top; + word-wrap: break-word; + } + + thead { + border-bottom: 1px solid #E5E5E5; + border-bottom: 1px solid var(--border); + } + + @media (prefers-color-scheme: dark) { + + thead { + border-bottom: 1px solid #333333; + border-bottom: 1px solid var(--border); + } + } + + tfoot { + border-top: 1px solid #E5E5E5; + border-top: 1px solid var(--border); + } + + @media (prefers-color-scheme: dark) { + + tfoot { + border-top: 1px solid #333333; + border-top: 1px solid var(--border); + } + } + + tbody tr:nth-child(even) { + background-color: #FFFFFF; + background-color: var(--background); + } + + @media (prefers-color-scheme: dark) { + + tbody tr:nth-child(even) { + background-color: #1A1818; + background-color: var(--background); + } + } + + tbody tr:nth-child(even) button { + background-color: #F5F5F5; + background-color: var(--background-alt); + } + + @media (prefers-color-scheme: dark) { + + tbody tr:nth-child(even) button { + background-color: #322E2E; + background-color: var(--background-alt); + } + } + + tbody tr:nth-child(even) button:hover { + background-color: #FFFFFF; + background-color: var(--background-body); + } + + @media (prefers-color-scheme: dark) { + + tbody tr:nth-child(even) button:hover { + background-color: #1A1818; + background-color: var(--background-body); + } + } + + ::-webkit-scrollbar { + height: 10px; + width: 10px; + } + + ::-webkit-scrollbar-track { + background: #FFFFFF; + background: var(--background); + border-radius: 6px; + } + + @media (prefers-color-scheme: dark) { + + ::-webkit-scrollbar-track { + background: #1A1818; + background: var(--background); + } + } + + ::-webkit-scrollbar-thumb { + background: rgb(244, 244, 244); + background: var(--scrollbar-thumb); + border-radius: 6px; + } + + @media (prefers-color-scheme: dark) { + + ::-webkit-scrollbar-thumb { + background: #4C4C4C; + background: var(--scrollbar-thumb); + } + } + + @media (prefers-color-scheme: dark) { + + ::-webkit-scrollbar-thumb { + background: #4C4C4C; + background: var(--scrollbar-thumb); + } + } + + ::-webkit-scrollbar-thumb:hover { + background: #E5E5E5; + background: var(--scrollbar-thumb-hover); + } + + @media (prefers-color-scheme: dark) { + + ::-webkit-scrollbar-thumb:hover { + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); + } + } + + @media (prefers-color-scheme: dark) { + + ::-webkit-scrollbar-thumb:hover { + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); + } + } + + ::-moz-selection { + background-color: rgba(24, 24, 24, 0.08); + background-color: var(--selection); + color: #1A1818; + color: var(--text-bright); + } + + ::selection { + background-color: rgba(24, 24, 24, 0.08); + background-color: var(--selection); + color: #1A1818; + color: var(--text-bright); + } + + @media (prefers-color-scheme: dark) { + + ::-moz-selection { + color: #fff; + color: var(--text-bright); + } + + ::selection { + color: #fff; + color: var(--text-bright); + } + } + + @media (prefers-color-scheme: dark) { + + ::-moz-selection { + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); + } + + ::selection { + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); + } + } + + details { + display: flex; + flex-direction: column; + align-items: flex-start; + background-color: #F5F5F5; + background-color: var(--background-alt); + padding: 10px 10px 0; + margin: 1em 0; + border-radius: 6px; + overflow: hidden; + } + + @media (prefers-color-scheme: dark) { + + details { + background-color: #322E2E; + background-color: var(--background-alt); + } + } + + details[open] { + padding: 10px; + } + + details > :last-child { + margin-bottom: 0; + } + + details[open] summary { + margin-bottom: 10px; + } + + summary { + display: list-item; + background-color: #FFFFFF; + background-color: var(--background); + padding: 10px; + margin: -10px -10px 0; + cursor: pointer; + outline: none; + } + + @media (prefers-color-scheme: dark) { + + summary { + background-color: #1A1818; + background-color: var(--background); + } + } + + summary:hover, + summary:focus { + text-decoration: underline; + } + + details > :not(summary) { + margin-top: 0; + } + + summary::-webkit-details-marker { + color: #1A1818; + color: var(--text-main); + } + + @media (prefers-color-scheme: dark) { + + summary::-webkit-details-marker { + color: #FFFFFF; + color: var(--text-main); + } + } + + dialog { + background-color: #F5F5F5; + background-color: var(--background-alt); + color: #1A1818; + color: var(--text-main); + border: none; + border-radius: 6px; + border-color: #E5E5E5; + border-color: var(--border); + padding: 10px 30px; + } + + @media (prefers-color-scheme: dark) { + + dialog { + border-color: #333333; + border-color: var(--border); + } + } + + @media (prefers-color-scheme: dark) { + + dialog { + color: #FFFFFF; + color: var(--text-main); + } + } + + @media (prefers-color-scheme: dark) { + + dialog { + background-color: #322E2E; + background-color: var(--background-alt); + } + } + + dialog > header:first-child { + background-color: #FFFFFF; + background-color: var(--background); + border-radius: 6px 6px 0 0; + margin: -10px -30px 10px; + padding: 10px; + text-align: center; + } + + @media (prefers-color-scheme: dark) { + + dialog > header:first-child { + background-color: #1A1818; + background-color: var(--background); + } + } + + dialog::-webkit-backdrop { + background: #0000009c; + -webkit-backdrop-filter: blur(4px); + backdrop-filter: blur(4px); + } + + dialog::backdrop { + background: #0000009c; + -webkit-backdrop-filter: blur(4px); + backdrop-filter: blur(4px); + } + + footer { + border-top: 1px solid #E5E5E5; + border-top: 1px solid var(--border); + padding-top: 10px; + color: #666666; + color: var(--text-muted); + } + + @media (prefers-color-scheme: dark) { + + footer { + color: #B3B3B3; + color: var(--text-muted); + } + } + + @media (prefers-color-scheme: dark) { + + footer { + border-top: 1px solid #333333; + border-top: 1px solid var(--border); + } + } + + body > footer { + margin-top: 40px; + } + + @media print { + body, + pre, + code, + summary, + details, + button, + input, + textarea { + background-color: #fff; + } + + button, + input, + textarea { + border: 1px solid #000; + } + + body, + h1, + h2, + h3, + h4, + h5, + h6, + pre, + code, + button, + input, + textarea, + footer, + summary, + strong { + color: #000; + } + + summary::marker { + color: #000; + } + + summary::-webkit-details-marker { + color: #000; + } + + tbody tr:nth-child(even) { + background-color: #f2f2f2; + } + + a { + color: #00f; + text-decoration: underline; + } + } + .prelude { + display: flex; + flex-direction: column; + gap: 8px; + border-bottom: 1px solid var(--border); + padding-bottom: 1em; + } + .prelude .author { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.8em; + font-weight: 500; + color: var(--text-muted); + } + .prelude .author .avatar { + border-radius: 4px; + overflow: hidden; + } + .prelude time { + color: var(--text-muted); + font-size: 0.8em; + } + footer p { + font-size: 0.8em; + } ''' :: ++ heads @@ -178,6 +1844,17 @@ ;meta(property "og:type", content "article"); ;meta(property "twitter:card", content "summary"); ;meta(property "og:article:author:username", content author); + ;link(rel "preconnect", href "https://rsms.me"); + ;link(rel "stylesheet", href "https://rsms.me/inter/inter.css"); + == + :: + ++ diary-coda + ;footer + ;p + ; Powered by + ;a/"https://tlon.io":"Tlon" + ; , a messenger you can trust. + == == :: ++ diary-prelude @@ -185,9 +1862,15 @@ ^- marl :~ ;h1:"{title}" ;div.prelude - ;+ (sigil(size 25, icon &) author) - ; {(scow %p author)} - ;em:"{(scow %da (sub sent (mod sent ~s1)))}" + ;div.published + ;time:"{(scow %da (sub sent (mod sent ~s1)))}" + == + ;div.author + ;div.avatar + ;+ (sigil(size 20, icon &) author) + == + ; {(scow %p author)} + == == == -- From a1b5f7c1114dd5adc12dc1d51cccc89135babd1d Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 11 Sep 2024 21:30:57 +0200 Subject: [PATCH 004/157] expose: include note cover image, author profile Put the cover image, if there is any, in front of the notebook post's title. If the author has a profile, include the nickname, color, avatar as appropriate. --- desk/app/expose.hoon | 91 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 16 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 39ccf6f449..6fd6362135 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -5,7 +5,7 @@ :: then visit in the browser: :: /expose/that/reference/as/copied/123456789 :: -/- c=cite, d=channels +/- c=cite, d=channels, co=contacts /+ sigil, u=channel-utils, dbug, verb :: @@ -140,17 +140,37 @@ == ?~ msg ~ + :: + =/ aco=(unit contact:co) + =/ base=path /(scot %p our.bowl)/contacts/(scot %da now.bowl) + ?. .^(? %gu (weld base /$)) + ~ + =+ .^(rol=rolodex:co %gx (weld base /all/contact-rolodex)) + ?~ for=(~(get by rol) author.u.msg) + ~ + ?. ?=([[@ ^] *] u.for) + ~ + `con.for.u.for + :: ::TODO if we render replies then we can "unroll" whole chat threads too (: |^ ?+ p.nest.u.ref ~ ::TODO support rendering others %diary ?> ?=(%diary -.kind-data.u.msg) - =/ title=tape (trip title.kind-data.u.msg) + =* kd kind-data.u.msg + =/ title=tape (trip title.kd) %- some ^- manx ;html - ;+ (heads title (scow %p author.u.msg)) + ;+ %: heads + title + (scow %p author.u.msg) + ?:(=('' image.kd) ~ `image.kd) + == ;body - ;* (diary-prelude title author.u.msg sent.u.msg) + ;* ?: =('' image.kd) ~ + :_ ~ + ;img.cover@"{(trip image.kd)}"(alt "cover image"); + ;* (diary-prelude title) ;* (story:en-manx:u content.u.msg) ;+ diary-coda == @@ -1828,24 +1848,42 @@ ''' :: ++ heads - |= [title=tape author=tape] + |= [title=tape author=tape img=(unit @t)] ;head ;title:"{title}" ;style:"{(trip style)}" + ;link(rel "preconnect", href "https://rsms.me"); + ;link(rel "stylesheet", href "https://rsms.me/inter/inter.css"); + :: ;meta(charset "utf-8"); ;meta(name "viewport", content "width=device-width, initial-scale=1"); :: ;meta(name "robots", content "noindex, nofollow, noimageindex"); + :: + ::TODO make sure this is the right/new app id + ;meta(property "apple-itunes-app", content "app-id=6451392109"); + ::NOTE at the time of writing, android supports no such thing :: ::TODO could get smarter about description, preview image, etc ;meta(property "og:title", content title); ;meta(property "twitter:title", content title); ;meta(property "og:site_name", content "Tlon"); ;meta(property "og:type", content "article"); - ;meta(property "twitter:card", content "summary"); ;meta(property "og:article:author:username", content author); - ;link(rel "preconnect", href "https://rsms.me"); - ;link(rel "stylesheet", href "https://rsms.me/inter/inter.css"); + :: + ;* ?~ img + :_ ~ + ;meta(property "twitter:card", content "summary"); + =/ img=tape (trip u.img) + :~ ;meta(property "twitter:card", content "summary_large_image"); + ;meta(property "og:image", content img); + ;meta(property "twitter:image", content img); + == + :: + ;* ?~ aco ~ + ?: =('' nickname.u.aco) ~ + :_ ~ + ;meta(property "og:article:author:first_name", content (trip nickname.u.aco)); == :: ++ diary-coda @@ -1858,20 +1896,41 @@ == :: ++ diary-prelude - |= [title=tape author=ship sent=@da] + |= title=tape ^- marl :~ ;h1:"{title}" ;div.prelude ;div.published - ;time:"{(scow %da (sub sent (mod sent ~s1)))}" - == - ;div.author - ;div.avatar - ;+ (sigil(size 20, icon &) author) - == - ; {(scow %p author)} + ;time:"{(scow %da (sub sent.u.msg (mod sent.u.msg ~s1)))}" ::TODO nicer format == + ;+ author-node + == + == + :: + ++ author-node + ^- manx + =* author author.u.msg + ;div.author + ;div.avatar + ;+ + ?: &(?=(^ aco) ?=(^ avatar.u.aco) !=('' u.avatar.u.aco)) + ;img@"{(trip u.avatar.u.aco)}"(alt "author's avatar"); + =/ val=@ux ?~(aco 0x0 color.u.aco) + =/ col=tape ((x-co:^co 6) val) + %. author + %_ sigil + size 25 + icon & + bg '#'^col + fg ?:((gth (div (roll (rip 3 val) add) 3) 127) "black" "white") ::REVIEW == + == + :: + ;+ + =* nom ;span.author:"{(scow %p author)}" + ?~ aco nom + ?: =('' nickname.u.aco) nom + ;span.author(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" == -- == From a006148bdc3fb93035a77fdfa6c4d083ee150354 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 12 Sep 2024 10:23:13 -0400 Subject: [PATCH 005/157] expose: better sizing for all elements, use relative CSS units --- desk/app/expose.hoon | 1148 ++++++++++++++++++++---------------------- 1 file changed, 554 insertions(+), 594 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 6fd6362135..36ae793686 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -167,11 +167,15 @@ ?:(=('' image.kd) ~ `image.kd) == ;body - ;* ?: =('' image.kd) ~ - :_ ~ - ;img.cover@"{(trip image.kd)}"(alt "cover image"); - ;* (diary-prelude title) - ;* (story:en-manx:u content.u.msg) + ;header + ;* ?: =('' image.kd) ~ + :_ ~ + ;img.cover@"{(trip image.kd)}"(alt "Cover image"); + ;* (diary-prelude title) + == + ;article + ;* (story:en-manx:u content.u.msg) + == ;+ diary-coda == == @@ -180,143 +184,140 @@ ++ style ''' :root { - --background-body: #FFFFFF; - --background: #FFFFFF; - --background-alt: #F5F5F5; + --background-body: #ffffff; + --background: #ffffff; + --background-alt: #f5f5f5; --selection: rgba(24, 24, 24, 0.08); - --text-main: #1A1818; - --text-bright: #1A1818; + --text-main: #1a1818; + --text-bright: #1a1818; --text-muted: #666666; - --links: #008EFF; - --focus: #008EFF; - --border: #E5E5E5; - --code: #1A1818; + --links: #008eff; + --focus: #008eff; + --border: #e5e5e5; + --code: #1a1818; --animation-duration: 0.1s; - --button-base: #F5F5F5; - --button-hover: #E5E5E5; + --button-base: #f5f5f5; + --button-hover: #e5e5e5; --scrollbar-thumb: rgb(244, 244, 244); --scrollbar-thumb-hover: var(--button-hover); --form-placeholder: #999999; - --form-text: #1A1818; - --variable: #2AD546; - --highlight: #FADE7A; + --form-text: #1a1818; + --variable: #2ad546; + --highlight: #fade7a; --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); } @media (prefers-color-scheme: dark) { - :root { - --background-body: #1A1818; - --background: #1A1818; - --background-alt: #322E2E; - --selection: rgba(255, 255, 255, 0.08); - --text-main: #FFFFFF; - --text-bright: #fff; - --text-muted: #B3B3B3; - --links: #008EFF; - --focus: #008EFF; - --border: #333333; - --code: #FFFFFF; - --animation-duration: 0.1s; - --button-base: #322E2E; - --button-hover: #4C4C4C; - --scrollbar-thumb: var(--button-hover); - --scrollbar-thumb-hover: rgb(0, 0, 0); - --form-placeholder: #808080; - --form-text: #fff; - --variable: #2AD546; - --highlight: #FADE7A; - --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); - } + :root { + --background-body: #1a1818; + --background: #1a1818; + --background-alt: #322e2e; + --selection: rgba(255, 255, 255, 0.08); + --text-main: #ffffff; + --text-bright: #fff; + --text-muted: #b3b3b3; + --links: #008eff; + --focus: #008eff; + --border: #333333; + --code: #ffffff; + --animation-duration: 0.1s; + --button-base: #322e2e; + --button-hover: #4c4c4c; + --scrollbar-thumb: var(--button-hover); + --scrollbar-thumb-hover: rgb(0, 0, 0); + --form-placeholder: #808080; + --form-text: #fff; + --variable: #2ad546; + --highlight: #fade7a; + --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); + } } html { - scrollbar-color: rgb(244, 244, 244) #FFFFFF; + scrollbar-color: rgb(244, 244, 244) #ffffff; scrollbar-color: var(--scrollbar-thumb) var(--background-body); scrollbar-width: thin; } @media (prefers-color-scheme: dark) { html { - scrollbar-color: #4C4C4C #1A1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); } } @media (prefers-color-scheme: dark) { html { - scrollbar-color: #4C4C4C #1A1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); } } @media (prefers-color-scheme: dark) { html { - scrollbar-color: #4C4C4C #1A1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); } } @media (prefers-color-scheme: dark) { html { - scrollbar-color: #4C4C4C #1A1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); } } @media (prefers-color-scheme: dark) { html { - scrollbar-color: #4C4C4C #1A1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); } } @media (prefers-color-scheme: dark) { html { - scrollbar-color: #4C4C4C #1A1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); } } body { - font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', sans-serif; + font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", + "Helvetica Neue", "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", + sans-serif; font-size: 20px; font-weight: 400; line-height: 1.8; - max-width: 800px; - margin: 20px auto; - padding: 0 10px; + max-width: 40em; + margin: 1em auto; + padding: 0 0.5em; word-wrap: break-word; - color: #1A1818; + color: #1a1818; color: var(--text-main); - background: #FFFFFF; + background: #ffffff; background: var(--background-body); text-rendering: optimizeLegibility; } @media (prefers-color-scheme: dark) { body { - background: #1A1818; - background: var(--background-body); + background: #1a1818; + background: var(--background-body); } } @media (prefers-color-scheme: dark) { - body { - color: #FFFFFF; - color: var(--text-main); + color: #ffffff; + color: var(--text-main); } } button { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, border-color var(--animation-duration) linear, color var(--animation-duration) linear, box-shadow var(--animation-duration) linear, @@ -325,30 +326,20 @@ @media (prefers-color-scheme: dark) { button { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; } } input { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, border-color var(--animation-duration) linear, color var(--animation-duration) linear, box-shadow var(--animation-duration) linear, @@ -357,30 +348,20 @@ @media (prefers-color-scheme: dark) { input { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; } } textarea { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, border-color var(--animation-duration) linear, color var(--animation-duration) linear, box-shadow var(--animation-duration) linear, @@ -389,18 +370,13 @@ @media (prefers-color-scheme: dark) { textarea { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; } } @@ -409,8 +385,9 @@ font-size: 2em; font-weight: 400; letter-spacing: -0.025em; + line-height: 1; margin-top: 0; - margin-bottom: 12px; + margin-bottom: 0.6em; } h2, @@ -418,92 +395,92 @@ h4, h5, h6 { - margin-bottom: 12px; - margin-top: 24px; + margin-bottom: 0.6em; + margin-top: 1.25em; font-size: 1em; } h1 { - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { h1 { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } h2 { - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { h2 { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } h3 { - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { h3 { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } h4 { - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { h4 { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } h5 { - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { h5 { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } h6 { - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { h6 { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } strong { - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { strong { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } @@ -527,8 +504,8 @@ } blockquote { - border-left: 4px solid #008EFF; - border-left: 4px solid var(--focus); + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); margin: 1.5em 0; padding: 0.5em 1em; font-style: italic; @@ -536,24 +513,23 @@ @media (prefers-color-scheme: dark) { blockquote { - border-left: 4px solid #008EFF; - border-left: 4px solid var(--focus); + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); } } q { - border-left: 4px solid #008EFF; - border-left: 4px solid var(--focus); + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); margin: 1.5em 0; padding: 0.5em 1em; font-style: italic; } @media (prefers-color-scheme: dark) { - q { - border-left: 4px solid #008EFF; - border-left: 4px solid var(--focus); + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); } } @@ -570,31 +546,30 @@ font-style: normal; } - a[href^='mailto\:']::before { - content: '📧 '; + a[href^="mailto\:"]::before { + content: "📧 "; } - a[href^='tel\:']::before { - content: '📞 '; + a[href^="tel\:"]::before { + content: "📞 "; } - a[href^='sms\:']::before { - content: '💬 '; + a[href^="sms\:"]::before { + content: "💬 "; } mark { - background-color: #FADE7A; + background-color: #fade7a; background-color: var(--highlight); - border-radius: 2px; - padding: 0 2px 0 2px; + border-radius: 0.125em; + padding: 0 0.125em 0 0.125em; color: #000; } @media (prefers-color-scheme: dark) { - mark { - background-color: #FADE7A; - background-color: var(--highlight); + background-color: #fade7a; + background-color: var(--highlight); } } @@ -605,12 +580,12 @@ button, select, - input[type='submit'], - input[type='reset'], - input[type='button'], - input[type='checkbox'], - input[type='range'], - input[type='radio'] { + input[type="submit"], + input[type="reset"], + input[type="button"], + input[type="checkbox"], + input[type="range"], + input[type="radio"] { cursor: pointer; } @@ -619,244 +594,244 @@ display: block; } - [type='checkbox'], - [type='radio'] { + [type="checkbox"], + [type="radio"] { display: initial; } input { - color: #1A1818; + color: #1a1818; color: var(--form-text); - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 6px; - margin-bottom: 6px; - padding: 10px; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; border: none; - border-radius: 6px; + border-radius: 0.25em; outline: none; } @media (prefers-color-scheme: dark) { input { - background-color: #1A1818; - background-color: var(--background); + background-color: #1a1818; + background-color: var(--background); } } @media (prefers-color-scheme: dark) { input { - color: #fff; - color: var(--form-text); + color: #fff; + color: var(--form-text); } } button { - color: #1A1818; + color: #1a1818; color: var(--form-text); - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 6px; - margin-bottom: 6px; - padding: 10px; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; border: none; - border-radius: 6px; + border-radius: 0.25em; outline: none; } @media (prefers-color-scheme: dark) { button { - background-color: #1A1818; - background-color: var(--background); + background-color: #1a1818; + background-color: var(--background); } } @media (prefers-color-scheme: dark) { button { - color: #fff; - color: var(--form-text); + color: #fff; + color: var(--form-text); } } textarea { - color: #1A1818; + color: #1a1818; color: var(--form-text); - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 6px; - margin-bottom: 6px; - padding: 10px; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; border: none; - border-radius: 6px; + border-radius: 0.25em; outline: none; } @media (prefers-color-scheme: dark) { textarea { - background-color: #1A1818; - background-color: var(--background); + background-color: #1a1818; + background-color: var(--background); } } @media (prefers-color-scheme: dark) { textarea { - color: #fff; - color: var(--form-text); + color: #fff; + color: var(--form-text); } } select { - color: #1A1818; + color: #1a1818; color: var(--form-text); - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 6px; - margin-bottom: 6px; - padding: 10px; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; border: none; - border-radius: 6px; + border-radius: 0.25em; outline: none; } @media (prefers-color-scheme: dark) { select { - background-color: #1A1818; - background-color: var(--background); + background-color: #1a1818; + background-color: var(--background); } } @media (prefers-color-scheme: dark) { select { - color: #fff; - color: var(--form-text); + color: #fff; + color: var(--form-text); } } button { - background-color: #F5F5F5; + background-color: #f5f5f5; background-color: var(--button-base); - padding-right: 30px; - padding-left: 30px; + padding-right: 1.5em; + padding-left: 1.5em; } @media (prefers-color-scheme: dark) { button { - background-color: #322E2E; - background-color: var(--button-base); + background-color: #322e2e; + background-color: var(--button-base); } } - input[type='submit'] { - background-color: #F5F5F5; + input[type="submit"] { + background-color: #f5f5f5; background-color: var(--button-base); - padding-right: 30px; - padding-left: 30px; + padding-right: 1.5em; + padding-left: 1.5em; } @media (prefers-color-scheme: dark) { - input[type='submit'] { - background-color: #322E2E; - background-color: var(--button-base); + input[type="submit"] { + background-color: #322e2e; + background-color: var(--button-base); } } - input[type='reset'] { - background-color: #F5F5F5; + input[type="reset"] { + background-color: #f5f5f5; background-color: var(--button-base); - padding-right: 30px; - padding-left: 30px; + padding-right: 1.5em; + padding-left: 1.5em; } @media (prefers-color-scheme: dark) { - input[type='reset'] { - background-color: #322E2E; - background-color: var(--button-base); + input[type="reset"] { + background-color: #322e2e; + background-color: var(--button-base); } } - input[type='button'] { - background-color: #F5F5F5; + input[type="button"] { + background-color: #f5f5f5; background-color: var(--button-base); - padding-right: 30px; - padding-left: 30px; + padding-right: 1.5em; + padding-left: 1.5em; } @media (prefers-color-scheme: dark) { - input[type='button'] { - background-color: #322E2E; - background-color: var(--button-base); + input[type="button"] { + background-color: #322e2e; + background-color: var(--button-base); } } button:hover { - background: #E5E5E5; + background: #e5e5e5; background: var(--button-hover); } @media (prefers-color-scheme: dark) { button:hover { - background: #4C4C4C; - background: var(--button-hover); + background: #4c4c4c; + background: var(--button-hover); } } - input[type='submit']:hover { - background: #E5E5E5; + input[type="submit"]:hover { + background: #e5e5e5; background: var(--button-hover); } @media (prefers-color-scheme: dark) { - input[type='submit']:hover { - background: #4C4C4C; - background: var(--button-hover); + input[type="submit"]:hover { + background: #4c4c4c; + background: var(--button-hover); } } - input[type='reset']:hover { - background: #E5E5E5; + input[type="reset"]:hover { + background: #e5e5e5; background: var(--button-hover); } @media (prefers-color-scheme: dark) { - input[type='reset']:hover { - background: #4C4C4C; - background: var(--button-hover); + input[type="reset"]:hover { + background: #4c4c4c; + background: var(--button-hover); } } - input[type='button']:hover { - background: #E5E5E5; + input[type="button"]:hover { + background: #e5e5e5; background: var(--button-hover); } @media (prefers-color-scheme: dark) { - input[type='button']:hover { - background: #4C4C4C; - background: var(--button-hover); + input[type="button"]:hover { + background: #4c4c4c; + background: var(--button-hover); } } - input[type='color'] { + input[type="color"] { min-height: 2rem; - padding: 8px; + padding: 0.4em; cursor: pointer; } - input[type='checkbox'], - input[type='radio'] { + input[type="checkbox"], + input[type="radio"] { height: 1em; width: 1em; } - input[type='radio'] { + input[type="radio"] { border-radius: 100%; } @@ -866,12 +841,12 @@ label { vertical-align: middle; - margin-bottom: 4px; + margin-bottom: 0.25em; display: inline-block; } - input:not([type='checkbox']):not([type='radio']), - input[type='range'], + input:not([type="checkbox"]):not([type="radio"]), + input[type="range"], select, button, textarea { @@ -890,41 +865,56 @@ } textarea:not([rows]) { - min-height: 40px; - height: 140px; + min-height: 2em; + height: 12em; } select { - background: #FFFFFF url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; - padding-right: 35px; + background: #ffffff + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / 10.125em + no-repeat; + padding-right: 1.75em; } @media (prefers-color-scheme: dark) { select { - background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; } } @media (prefers-color-scheme: dark) { select { - background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; } } @media (prefers-color-scheme: dark) { select { - background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; } } @media (prefers-color-scheme: dark) { select { - background: #1A1818 url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 12px) 50% / 12px no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 12px) 50% / 12px no-repeat; + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; } } @@ -933,70 +923,67 @@ } select[multiple] { - padding-right: 10px; + padding-right: 0.5em; background-image: none; overflow-y: auto; } input:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } @media (prefers-color-scheme: dark) { input:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } } select:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } @media (prefers-color-scheme: dark) { - select:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } } button:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } @media (prefers-color-scheme: dark) { - button:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } } textarea:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } @media (prefers-color-scheme: dark) { - textarea:focus { - box-shadow: 0 0 0 2px #008EFF; - box-shadow: 0 0 0 2px var(--focus); + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); } } - input[type='checkbox']:active, - input[type='radio']:active, - input[type='submit']:active, - input[type='reset']:active, - input[type='button']:active, - input[type='range']:active, + input[type="checkbox"]:active, + input[type="radio"]:active, + input[type="submit"]:active, + input[type="reset"]:active, + input[type="button"]:active, + input[type="range"]:active, button:active { - transform: translateY(2px); + transform: translateY(0.125em); } input:disabled, @@ -1029,40 +1016,39 @@ @media (prefers-color-scheme: dark) { ::-moz-placeholder { - color: #808080; - color: var(--form-placeholder); + color: #808080; + color: var(--form-placeholder); } :-ms-input-placeholder { - color: #808080; - color: var(--form-placeholder); + color: #808080; + color: var(--form-placeholder); } ::-ms-input-placeholder { - color: #808080; - color: var(--form-placeholder); + color: #808080; + color: var(--form-placeholder); } ::placeholder { - color: #808080; - color: var(--form-placeholder); + color: #808080; + color: var(--form-placeholder); } } fieldset { - border: 1px #008EFF solid; - border: 1px var(--focus) solid; - border-radius: 6px; + border: 0.125em #008eff solid; + border: 0.125em var(--focus) solid; + border-radius: 0.25em; margin: 0; - margin-bottom: 12px; - padding: 10px; + margin-bottom: 10.125em; + padding: 0.5em; } @media (prefers-color-scheme: dark) { - fieldset { - border: 1px #008EFF solid; - border: 1px var(--focus) solid; + border: 0.125em #008eff solid; + border: 0.125em var(--focus) solid; } } @@ -1071,189 +1057,186 @@ font-weight: 600; } - input[type='range'] { - margin: 10px 0; - padding: 10px 0; + input[type="range"] { + margin: 0.5em 0; + padding: 0.5em 0; background: transparent; } - input[type='range']:focus { + input[type="range"]:focus { outline: none; } - input[type='range']::-webkit-slider-runnable-track { + input[type="range"]::-webkit-slider-runnable-track { width: 100%; - height: 9.5px; + height: 0.5em; -webkit-transition: 0.2s; transition: 0.2s; - background: #FFFFFF; + background: #ffffff; background: var(--background); - border-radius: 3px; + border-radius: 0.25em; } @media (prefers-color-scheme: dark) { - input[type='range']::-webkit-slider-runnable-track { - background: #1A1818; - background: var(--background); + input[type="range"]::-webkit-slider-runnable-track { + background: #1a1818; + background: var(--background); } } - input[type='range']::-webkit-slider-thumb { - box-shadow: 0 1px 1px #000, 0 0 1px #0d0d0d; - height: 20px; - width: 20px; + input[type="range"]::-webkit-slider-thumb { + box-shadow: 0 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; + height: 1em; + width: 1em; border-radius: 50%; - background: #E5E5E5; + background: #e5e5e5; background: var(--border); -webkit-appearance: none; - margin-top: -7px; + margin-top: -0.3em; } @media (prefers-color-scheme: dark) { - input[type='range']::-webkit-slider-thumb { - background: #333333; - background: var(--border); + input[type="range"]::-webkit-slider-thumb { + background: #333333; + background: var(--border); } } - input[type='range']:focus::-webkit-slider-runnable-track { - background: #FFFFFF; + input[type="range"]:focus::-webkit-slider-runnable-track { + background: #ffffff; background: var(--background); } @media (prefers-color-scheme: dark) { - input[type='range']:focus::-webkit-slider-runnable-track { - background: #1A1818; - background: var(--background); + input[type="range"]:focus::-webkit-slider-runnable-track { + background: #1a1818; + background: var(--background); } } - input[type='range']::-moz-range-track { + input[type="range"]::-moz-range-track { width: 100%; - height: 9.5px; + height: 1em; -moz-transition: 0.2s; transition: 0.2s; - background: #FFFFFF; + background: #ffffff; background: var(--background); - border-radius: 3px; + border-radius: 0.25em; } @media (prefers-color-scheme: dark) { - input[type='range']::-moz-range-track { - background: #1A1818; - background: var(--background); + input[type="range"]::-moz-range-track { + background: #1a1818; + background: var(--background); } } - input[type='range']::-moz-range-thumb { - box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; - height: 20px; - width: 20px; + input[type="range"]::-moz-range-thumb { + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; + height: 1em; + width: 1em; border-radius: 50%; - background: #E5E5E5; + background: #e5e5e5; background: var(--border); } @media (prefers-color-scheme: dark) { - input[type='range']::-moz-range-thumb { - background: #333333; - background: var(--border); + input[type="range"]::-moz-range-thumb { + background: #333333; + background: var(--border); } } - input[type='range']::-ms-track { + input[type="range"]::-ms-track { width: 100%; - height: 9.5px; + height: 0.5em; background: transparent; border-color: transparent; - border-width: 16px 0; + border-width: 10.25em 0; color: transparent; } - input[type='range']::-ms-fill-lower { - background: #FFFFFF; + input[type="range"]::-ms-fill-lower { + background: #ffffff; background: var(--background); - border: 0.2px solid #010101; - border-radius: 3px; - box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; + border: 0.0.125em solid #010101; + border-radius: 0.25em; + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; } @media (prefers-color-scheme: dark) { - input[type='range']::-ms-fill-lower { - background: #1A1818; - background: var(--background); + input[type="range"]::-ms-fill-lower { + background: #1a1818; + background: var(--background); } } - input[type='range']::-ms-fill-upper { - background: #FFFFFF; + input[type="range"]::-ms-fill-upper { + background: #ffffff; background: var(--background); - border: 0.2px solid #010101; - border-radius: 3px; - box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; + border: 0.0.125em solid #010101; + border-radius: 0.25em; + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; } @media (prefers-color-scheme: dark) { - input[type='range']::-ms-fill-upper { - background: #1A1818; - background: var(--background); + input[type="range"]::-ms-fill-upper { + background: #1a1818; + background: var(--background); } } - input[type='range']::-ms-thumb { - box-shadow: 1px 1px 1px #000, 0 0 1px #0d0d0d; - border: 1px solid #000; - height: 20px; - width: 20px; + input[type="range"]::-ms-thumb { + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; + border: 0.125em solid #000; + height: 1em; + width: 1em; border-radius: 50%; - background: #E5E5E5; + background: #e5e5e5; background: var(--border); } @media (prefers-color-scheme: dark) { - input[type='range']::-ms-thumb { - background: #333333; - background: var(--border); + input[type="range"]::-ms-thumb { + background: #333333; + background: var(--border); } } - input[type='range']:focus::-ms-fill-lower { - background: #FFFFFF; + input[type="range"]:focus::-ms-fill-lower { + background: #ffffff; background: var(--background); } @media (prefers-color-scheme: dark) { - - input[type='range']:focus::-ms-fill-lower { - background: #1A1818; - background: var(--background); + input[type="range"]:focus::-ms-fill-lower { + background: #1a1818; + background: var(--background); } } - input[type='range']:focus::-ms-fill-upper { - background: #FFFFFF; + input[type="range"]:focus::-ms-fill-upper { + background: #ffffff; background: var(--background); } @media (prefers-color-scheme: dark) { - - input[type='range']:focus::-ms-fill-upper { - background: #1A1818; - background: var(--background); + input[type="range"]:focus::-ms-fill-upper { + background: #1a1818; + background: var(--background); } } a { text-decoration: underline; - color: #008EFF; + color: #008eff; color: var(--links); } @media (prefers-color-scheme: dark) { - a { - color: #008EFF; - color: var(--links); + color: #008eff; + color: var(--links); } } @@ -1262,136 +1245,126 @@ } code { - background: #FFFFFF; + background: #ffffff; background: var(--background); - color: #1A1818; + color: #1a1818; color: var(--code); - padding: 2.5px 5px; - border-radius: 6px; + padding: 0.125em 0.25em; + border-radius: 0.25em; font-size: 1em; } @media (prefers-color-scheme: dark) { - code { - color: #FFFFFF; - color: var(--code); + color: #ffffff; + color: var(--code); } } @media (prefers-color-scheme: dark) { - code { - background: #1A1818; - background: var(--background); + background: #1a1818; + background: var(--background); } } samp { - background: #FFFFFF; + background: #ffffff; background: var(--background); - color: #1A1818; + color: #1a1818; color: var(--code); - padding: 2.5px 5px; - border-radius: 6px; + padding: 0.125em 0.25em; + border-radius: 0.25em; font-size: 1em; } @media (prefers-color-scheme: dark) { - samp { - color: #FFFFFF; - color: var(--code); + color: #ffffff; + color: var(--code); } } @media (prefers-color-scheme: dark) { - samp { - background: #1A1818; - background: var(--background); + background: #1a1818; + background: var(--background); } } time { - background: #FFFFFF; + background: #ffffff; background: var(--background); - color: #1A1818; + color: #1a1818; color: var(--code); - padding: 2.5px 5px; - border-radius: 6px; + padding: 0.125em 0.25em; + border-radius: 0.25em; font-size: 1em; } @media (prefers-color-scheme: dark) { - time { - color: #FFFFFF; - color: var(--code); + color: #ffffff; + color: var(--code); } } @media (prefers-color-scheme: dark) { - time { - background: #1A1818; - background: var(--background); + background: #1a1818; + background: var(--background); } } pre > code { - padding: 10px; + padding: 0.5em; display: block; overflow-x: auto; } var { - color: #2AD546; + color: #2ad546; color: var(--variable); font-style: normal; font-family: monospace; } @media (prefers-color-scheme: dark) { - var { - color: #2AD546; - color: var(--variable); + color: #2ad546; + color: var(--variable); } } kbd { - background: #FFFFFF; + background: #ffffff; background: var(--background); - border: 1px solid #E5E5E5; - border: 1px solid var(--border); - border-radius: 2px; - color: #1A1818; + border: 0.125em solid #e5e5e5; + border: 0.125em solid var(--border); + border-radius: 0.125em; + color: #1a1818; color: var(--text-main); - padding: 2px 4px 2px 4px; + padding: 0.125em 0.25em 0.125em 0.25em; } @media (prefers-color-scheme: dark) { - kbd { - color: #FFFFFF; - color: var(--text-main); + color: #ffffff; + color: var(--text-main); } } @media (prefers-color-scheme: dark) { - kbd { - border: 1px solid #333333; - border: 1px solid var(--border); + border: 0.125em solid #333333; + border: 0.125em solid var(--border); } } @media (prefers-color-scheme: dark) { - kbd { - background: #1A1818; - background: var(--background); + background: #1a1818; + background: var(--background); } } @@ -1403,21 +1376,20 @@ hr { border: none; - border-top: 1px solid #E5E5E5; - border-top: 1px solid var(--border); + border-top: 0.125em solid #e5e5e5; + border-top: 0.125em solid var(--border); } @media (prefers-color-scheme: dark) { - hr { - border-top: 1px solid #333333; - border-top: 1px solid var(--border); + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); } } table { border-collapse: collapse; - margin-bottom: 10px; + margin-bottom: 0.5em; width: 100%; table-layout: fixed; } @@ -1428,176 +1400,164 @@ td, th { - padding: 6px; + padding: 0.25em; text-align: left; vertical-align: top; word-wrap: break-word; } thead { - border-bottom: 1px solid #E5E5E5; - border-bottom: 1px solid var(--border); + border-bottom: 0.125em solid #e5e5e5; + border-bottom: 0.125em solid var(--border); } @media (prefers-color-scheme: dark) { - thead { - border-bottom: 1px solid #333333; - border-bottom: 1px solid var(--border); + border-bottom: 0.125em solid #333333; + border-bottom: 0.125em solid var(--border); } } tfoot { - border-top: 1px solid #E5E5E5; - border-top: 1px solid var(--border); + border-top: 0.125em solid #e5e5e5; + border-top: 0.125em solid var(--border); } @media (prefers-color-scheme: dark) { - tfoot { - border-top: 1px solid #333333; - border-top: 1px solid var(--border); + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); } } tbody tr:nth-child(even) { - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background); } @media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) { - background-color: #1A1818; - background-color: var(--background); + background-color: #1a1818; + background-color: var(--background); } } tbody tr:nth-child(even) button { - background-color: #F5F5F5; + background-color: #f5f5f5; background-color: var(--background-alt); } @media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) button { - background-color: #322E2E; - background-color: var(--background-alt); + background-color: #322e2e; + background-color: var(--background-alt); } } tbody tr:nth-child(even) button:hover { - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background-body); } @media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) button:hover { - background-color: #1A1818; - background-color: var(--background-body); + background-color: #1a1818; + background-color: var(--background-body); } } ::-webkit-scrollbar { - height: 10px; - width: 10px; + height: 0.5em; + width: 0.5em; } ::-webkit-scrollbar-track { - background: #FFFFFF; + background: #ffffff; background: var(--background); - border-radius: 6px; + border-radius: 0.25em; } @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-track { - background: #1A1818; - background: var(--background); + background: #1a1818; + background: var(--background); } } ::-webkit-scrollbar-thumb { background: rgb(244, 244, 244); background: var(--scrollbar-thumb); - border-radius: 6px; + border-radius: 0.25em; } @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb { - background: #4C4C4C; - background: var(--scrollbar-thumb); + background: #4c4c4c; + background: var(--scrollbar-thumb); } } @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb { - background: #4C4C4C; - background: var(--scrollbar-thumb); + background: #4c4c4c; + background: var(--scrollbar-thumb); } } ::-webkit-scrollbar-thumb:hover { - background: #E5E5E5; + background: #e5e5e5; background: var(--scrollbar-thumb-hover); } @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); - background: var(--scrollbar-thumb-hover); + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); } } @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); - background: var(--scrollbar-thumb-hover); + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); } } ::-moz-selection { background-color: rgba(24, 24, 24, 0.08); background-color: var(--selection); - color: #1A1818; + color: #1a1818; color: var(--text-bright); } ::selection { background-color: rgba(24, 24, 24, 0.08); background-color: var(--selection); - color: #1A1818; + color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { - ::-moz-selection { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } ::selection { - color: #fff; - color: var(--text-bright); + color: #fff; + color: var(--text-bright); } } @media (prefers-color-scheme: dark) { - ::-moz-selection { - background-color: rgba(255, 255, 255, 0.08); - background-color: var(--selection); + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); } ::selection { - background-color: rgba(255, 255, 255, 0.08); - background-color: var(--selection); + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); } } @@ -1605,24 +1565,23 @@ display: flex; flex-direction: column; align-items: flex-start; - background-color: #F5F5F5; + background-color: #f5f5f5; background-color: var(--background-alt); - padding: 10px 10px 0; + padding: 0.5em 0.5em 0; margin: 1em 0; - border-radius: 6px; + border-radius: 0.25em; overflow: hidden; } @media (prefers-color-scheme: dark) { - details { - background-color: #322E2E; - background-color: var(--background-alt); + background-color: #322e2e; + background-color: var(--background-alt); } } details[open] { - padding: 10px; + padding: 0.5em; } details > :last-child { @@ -1630,24 +1589,23 @@ } details[open] summary { - margin-bottom: 10px; + margin-bottom: 0.5em; } summary { display: list-item; - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background); - padding: 10px; - margin: -10px -10px 0; + padding: 0.5em; + margin: -0.5em -0.5em 0; cursor: pointer; outline: none; } @media (prefers-color-scheme: dark) { - summary { - background-color: #1A1818; - background-color: var(--background); + background-color: #1a1818; + background-color: var(--background); } } @@ -1661,109 +1619,93 @@ } summary::-webkit-details-marker { - color: #1A1818; + color: #1a1818; color: var(--text-main); } @media (prefers-color-scheme: dark) { - summary::-webkit-details-marker { - color: #FFFFFF; - color: var(--text-main); + color: #ffffff; + color: var(--text-main); } } dialog { - background-color: #F5F5F5; + background-color: #f5f5f5; background-color: var(--background-alt); - color: #1A1818; + color: #1a1818; color: var(--text-main); border: none; - border-radius: 6px; - border-color: #E5E5E5; + border-radius: 0.25em; + border-color: #e5e5e5; border-color: var(--border); - padding: 10px 30px; + padding: 0.5em 1.5em; } @media (prefers-color-scheme: dark) { - dialog { - border-color: #333333; - border-color: var(--border); + border-color: #333333; + border-color: var(--border); } } @media (prefers-color-scheme: dark) { - dialog { - color: #FFFFFF; - color: var(--text-main); + color: #ffffff; + color: var(--text-main); } } @media (prefers-color-scheme: dark) { - dialog { - background-color: #322E2E; - background-color: var(--background-alt); + background-color: #322e2e; + background-color: var(--background-alt); } } dialog > header:first-child { - background-color: #FFFFFF; + background-color: #ffffff; background-color: var(--background); - border-radius: 6px 6px 0 0; - margin: -10px -30px 10px; - padding: 10px; + border-radius: 0.25em 0.25em 0 0; + margin: -0.5em -1.5em 0.5em; + padding: 0.5em; text-align: center; } @media (prefers-color-scheme: dark) { - dialog > header:first-child { - background-color: #1A1818; - background-color: var(--background); + background-color: #1a1818; + background-color: var(--background); } } dialog::-webkit-backdrop { background: #0000009c; - -webkit-backdrop-filter: blur(4px); - backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(0.25em); + backdrop-filter: blur(0.25em); } dialog::backdrop { background: #0000009c; - -webkit-backdrop-filter: blur(4px); - backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(0.25em); + backdrop-filter: blur(0.25em); } footer { - border-top: 1px solid #E5E5E5; - border-top: 1px solid var(--border); - padding-top: 10px; - color: #666666; - color: var(--text-muted); - } - - @media (prefers-color-scheme: dark) { - - footer { - color: #B3B3B3; - color: var(--text-muted); - } + border-top: 0.125em solid #e5e5e5; + border-top: 0.125em solid var(--border); + padding-top: 1em; } @media (prefers-color-scheme: dark) { - footer { - border-top: 1px solid #333333; - border-top: 1px solid var(--border); + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); } } body > footer { - margin-top: 40px; + margin-top: 2em; } @media print { @@ -1781,7 +1723,7 @@ button, input, textarea { - border: 1px solid #000; + border: 0.125em solid #000; } body, @@ -1819,32 +1761,49 @@ text-decoration: underline; } } + .cover { + border-radius: 1em; + margin-bottom: 1.5em; + } .prelude { display: flex; flex-direction: column; - gap: 8px; - border-bottom: 1px solid var(--border); - padding-bottom: 1em; + gap: 0.4em; + border-bottom: 0.125em solid var(--border); + padding-bottom: 1.5em; + margin-bottom: 1.5em; } .prelude .author { display: flex; align-items: center; - gap: 8px; + gap: 0.4em; font-size: 0.8em; font-weight: 500; color: var(--text-muted); } .prelude .author .avatar { - border-radius: 4px; + border-radius: 0.25em; overflow: hidden; } + .prelude .author .avatar img { + width: 1.5625em; + height: 1.5625em; + object-fit: cover; + } .prelude time { color: var(--text-muted); font-size: 0.8em; + padding: 0; + } + footer { + display: flex; + flex-direction: column; + align-items: center; } footer p { - font-size: 0.8em; + font-size: 0.6em; } + ''' :: ++ heads @@ -1888,10 +1847,11 @@ :: ++ diary-coda ;footer + ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); ;p ; Powered by ;a/"https://tlon.io":"Tlon" - ; , a messenger you can trust. + ; , a communication tool you can trust. == == :: @@ -1914,7 +1874,7 @@ ;div.avatar ;+ ?: &(?=(^ aco) ?=(^ avatar.u.aco) !=('' u.avatar.u.aco)) - ;img@"{(trip u.avatar.u.aco)}"(alt "author's avatar"); + ;img@"{(trip u.avatar.u.aco)}"(alt "Author's avatar"); =/ val=@ux ?~(aco 0x0 color.u.aco) =/ col=tape ((x-co:^co 6) val) %. author @@ -1927,10 +1887,10 @@ == :: ;+ - =* nom ;span.author:"{(scow %p author)}" + =* nom ;span:"{(scow %p author)}" ?~ aco nom ?: =('' nickname.u.aco) nom - ;span.author(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" + ;span(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" == -- == From a06c9114a38d540086f6b3826ce0dc6d988e0cd0 Mon Sep 17 00:00:00 2001 From: fang Date: Thu, 12 Sep 2024 16:48:40 +0200 Subject: [PATCH 006/157] channel-utils: +flatten-inline into cord Does a (lossy) conversion of an inline node into a plaintext cord. --- desk/lib/channel-utils.hoon | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index 797b5dadbe..c37859dc34 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -417,6 +417,20 @@ (test her nest) -- :: +++ flatten-inline + |= i=inline:c + ^- cord + ?@ i i + ?- -.i + ?(%italics %bold %strike %blockquote) (rap 3 (turn p.i flatten-inline)) + ?(%inline-code %code %tag) p.i + %ship (scot %p p.i) + %block q.i + %link q.i + %task (rap 3 (turn q.i flatten-inline)) + %break '\0a' + == +:: :: ++ en-manx ::NOTE more commonly, marl, but that's just (list manx) |% ++ content story From 22d2692eed5454bf27b6a8068026ff7f643d0789 Mon Sep 17 00:00:00 2001 From: fang Date: Thu, 12 Sep 2024 16:50:47 +0200 Subject: [PATCH 007/157] expose: support chat and heap content For chat, we flatten the first paragraph in the message to get the title. May or may not be what we want. Still only top-level content, and does not render replies. --- desk/app/expose.hoon | 113 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 36ae793686..cae464acc5 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -122,11 +122,8 @@ ?. ?=(%chan -.u.ref) ~ ::TODO the whole "deconstruct the ref path" situation is horrendous - ?. ?=([?(%msg %note) @ ~] wer.u.ref) ::TODO support chat msgs, replies + ?. ?=([?(%msg %note %curio) @ ~] wer.u.ref) ~ - :: /1/chan/chat/~bolbex-fogdys/watercooler-4926/msg/170141184506984515746528913634457812992 - :: /v2/chat/~bolbex-fogdys/watercooler-4926/posts/post/[id] - :: /v2/chat/~bolbex-fogdys/watercooler-4926/posts/post/id/[id]/replies/[id] =/ msg=(unit post:d) :- ~ ::TODO we want to do existence checks first though... .^ post:d @@ -153,7 +150,26 @@ `con.for.u.for :: ::TODO if we render replies then we can "unroll" whole chat threads too (: - |^ ?+ p.nest.u.ref ~ ::TODO support rendering others + |^ ?+ p.nest.u.ref ~ + %chat + ?> ?=(%chat -.kind-data.u.msg) + =/ title=tape + (trip (rap 3 (turn (first-inline content.u.msg) flatten-inline:u))) + %- some + ^- manx + ;html + ;+ (heads title ~) + ;body.chat + ;header + ;+ chat-prelude + == + ;article + ;* (story:en-manx:u content.u.msg) + == + ;+ footer + == + == + :: %diary ?> ?=(%diary -.kind-data.u.msg) =* kd kind-data.u.msg @@ -161,12 +177,8 @@ %- some ^- manx ;html - ;+ %: heads - title - (scow %p author.u.msg) - ?:(=('' image.kd) ~ `image.kd) - == - ;body + ;+ (heads title ?:(=('' image.kd) ~ `image.kd)) + ;body.diary ;header ;* ?: =('' image.kd) ~ :_ ~ @@ -176,7 +188,31 @@ ;article ;* (story:en-manx:u content.u.msg) == - ;+ diary-coda + ;+ footer + == + == + :: + %heap + ?> ?=(%heap -.kind-data.u.msg) + =/ title=tape + ?: &(?=(^ title.kind-data.u.msg) !=('' u.title.kind-data.u.msg)) + (trip u.title.kind-data.u.msg) + ::NOTE could flatten the first-inline, but we don't. showing that + :: as both h1 and content is strange + "" + %- some + ^- manx + ;html + ::REVIEW + ;+ (heads ?:(=("" title) "collection item" title) ~) + ;body.chat + ;header + ;* (heap-prelude title) + == + ;article + ;* (story:en-manx:u content.u.msg) + == + ;+ footer == == == @@ -1807,7 +1843,7 @@ ''' :: ++ heads - |= [title=tape author=tape img=(unit @t)] + |= [title=tape img=(unit @t)] ;head ;title:"{title}" ;style:"{(trip style)}" @@ -1828,7 +1864,7 @@ ;meta(property "twitter:title", content title); ;meta(property "og:site_name", content "Tlon"); ;meta(property "og:type", content "article"); - ;meta(property "og:article:author:username", content author); + ;meta(property "og:article:author:username", content (scow %p author.u.msg)); :: ;* ?~ img :_ ~ @@ -1845,7 +1881,7 @@ ;meta(property "og:article:author:first_name", content (trip nickname.u.aco)); == :: - ++ diary-coda + ++ footer ;footer ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); ;p @@ -1855,6 +1891,15 @@ == == :: + ++ chat-prelude + ^- manx + ;div.prelude + ;div.published + ;time:"{(scow %da (sub sent.u.msg (mod sent.u.msg ~s1)))}" ::TODO nicer format + == + ;+ author-node + == + :: ++ diary-prelude |= title=tape ^- marl @@ -1867,6 +1912,19 @@ == == :: + ++ heap-prelude + |= title=tape + ^- marl + =- ?: =("" title) [-]~ + :- ;h1:"{title}" + [-]~ + ;div.prelude + ;div.published + ;time:"{(scow %da (sub sent.u.msg (mod sent.u.msg ~s1)))}" ::TODO nicer format + == + ;+ author-node + == + :: ++ author-node ^- manx =* author author.u.msg @@ -1882,7 +1940,7 @@ size 25 icon & bg '#'^col - fg ?:((gth (div (roll (rip 3 val) add) 3) 127) "black" "white") ::REVIEW + fg ?:((gth (div (roll (rip 3 val) add) 3) 180) "black" "white") ::REVIEW == == :: @@ -1892,6 +1950,29 @@ ?: =('' nickname.u.aco) nom ;span(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" == + :: + ++ first-inline + |= content=story:d + ^- (list inline:d) + ?~ content ~ + ?: ?=(%inline -.i.content) + p.i.content + ?+ -.p.i.content $(content t.content) + %header q.p.i.content ::REVIEW questionable + :: + %listing + |- + ?- -.p.p.i.content + %list ::TODO or check listing first? + ?. =(~ r.p.p.i.content) + r.p.p.i.content + ?~ q.p.p.i.content ~ + =/ r $(p.p.i.content i.q.p.p.i.content) + ?. =(~ r) r + $(q.p.p.i.content t.q.p.p.i.content) + %item p.p.p.i.content + == + == -- == :: From f1d8001c6495855b07d699a2cf9cc4c3e61e1364 Mon Sep 17 00:00:00 2001 From: fang Date: Thu, 12 Sep 2024 17:00:35 +0200 Subject: [PATCH 008/157] expose: refactor page building into +build This fixes the overal structure but lets us pass in the parts that change. --- desk/app/expose.hoon | 72 ++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index cae464acc5..799d3392b7 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -156,18 +156,10 @@ =/ title=tape (trip (rap 3 (turn (first-inline content.u.msg) flatten-inline:u))) %- some - ^- manx - ;html - ;+ (heads title ~) - ;body.chat - ;header - ;+ chat-prelude - == - ;article - ;* (story:en-manx:u content.u.msg) - == - ;+ footer - == + %: build "chat" + (heads title ~) + [chat-prelude]~ + (story:en-manx:u content.u.msg) == :: %diary @@ -175,21 +167,14 @@ =* kd kind-data.u.msg =/ title=tape (trip title.kd) %- some - ^- manx - ;html - ;+ (heads title ?:(=('' image.kd) ~ `image.kd)) - ;body.diary - ;header - ;* ?: =('' image.kd) ~ - :_ ~ - ;img.cover@"{(trip image.kd)}"(alt "Cover image"); - ;* (diary-prelude title) - == - ;article - ;* (story:en-manx:u content.u.msg) - == - ;+ footer - == + %: build "diary" + (heads title ?:(=('' image.kd) ~ `image.kd)) + :: + ?: =('' image.kd) (diary-prelude title) + :- ;img.cover@"{(trip image.kd)}"(alt "Cover image"); + (diary-prelude title) + :: + (story:en-manx:u content.u.msg) == :: %heap @@ -201,22 +186,29 @@ :: as both h1 and content is strange "" %- some - ^- manx - ;html - ::REVIEW - ;+ (heads ?:(=("" title) "collection item" title) ~) - ;body.chat - ;header - ;* (heap-prelude title) - == - ;article - ;* (story:en-manx:u content.u.msg) - == - ;+ footer - == + %: build "chat" + (heads ?:(=("" title) "collection item" title) ~) + (heap-prelude title) + (story:en-manx:u content.u.msg) == == :: + ++ build + |= [tag=tape hes=manx pre=marl bod=marl] + ^- manx + ;html + ;+ hes + ;body(class tag) + ;header + ;* pre + == + ;article + ;* bod + == + ;+ footer + == + == + :: ++ style ''' :root { From 4e9d484c747eae6b61e8662c39eafad35f8a9280 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Thu, 12 Sep 2024 13:08:26 -0500 Subject: [PATCH 009/157] new web: omnibus db-related fixes --- apps/tlon-web-new/src/app.tsx | 265 ++++++++++++++++---------- apps/tlon-web-new/src/lib/triggers.ts | 20 +- apps/tlon-web-new/src/lib/webDb.ts | 19 +- packages/shared/src/api/chatApi.ts | 2 +- packages/shared/src/db/keyValue.ts | 2 +- packages/shared/src/store/sync.ts | 1 - 6 files changed, 188 insertions(+), 121 deletions(-) diff --git a/apps/tlon-web-new/src/app.tsx b/apps/tlon-web-new/src/app.tsx index 4687a18a05..1eebfd8e88 100644 --- a/apps/tlon-web-new/src/app.tsx +++ b/apps/tlon-web-new/src/app.tsx @@ -1,9 +1,11 @@ // Copyright 2024, Tlon Corporation import { TooltipProvider } from '@radix-ui/react-tooltip'; +import { useCurrentUserId } from '@tloncorp/app/hooks/useCurrentUser'; import { Provider as TamaguiProvider } from '@tloncorp/app/provider'; import { AppDataProvider } from '@tloncorp/app/provider/AppDataProvider'; import { sync } from '@tloncorp/shared'; import * as api from '@tloncorp/shared/dist/api'; +import * as store from '@tloncorp/shared/dist/store'; import cookies from 'browser-cookies'; import { usePostHog } from 'posthog-js/react'; import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react'; @@ -25,7 +27,7 @@ import ImageViewerScreenController from '@/controllers/ImageViewerScreenControll import { PostScreenController } from '@/controllers/PostScreenController'; import { ProfileScreenController } from '@/controllers/ProfileScreenController'; import EyrieMenu from '@/eyrie/EyrieMenu'; -import { useMigrations } from '@/lib/webDb'; +import { checkDb, useMigrations } from '@/lib/webDb'; import { ANALYTICS_DEFAULT_PROPERTIES } from '@/logic/analytics'; import useAppUpdates, { AppUpdateContext } from '@/logic/useAppUpdates'; import useErrorHandler from '@/logic/useErrorHandler'; @@ -97,99 +99,128 @@ function handleGridRedirect(navigate: NavigateFunction) { } } -function AppRoutes() { +function AppRoutes({ isLoaded }: { isLoaded: boolean }) { + const contactsQuery = store.useContacts(); + const currentUserId = useCurrentUserId(); + const calmSettingsQuery = store.useCalmSettings({ userId: currentUserId }); + + useEffect(() => { + const { data, refetch, isRefetching, isFetching } = contactsQuery; + + if (isLoaded && data?.length === 0 && !isRefetching && !isFetching) { + refetch(); + } + }, [contactsQuery, isLoaded]); + + useEffect(() => { + const { data, refetch, isRefetching, isFetching } = calmSettingsQuery; + + if (isLoaded && !data && !isRefetching && !isFetching) { + refetch(); + } + }, [calmSettingsQuery, isLoaded]); + + if (!isLoaded) { + return null; + } + return ( - - } /> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } /> - } - /> - } /> - } /> - } /> - } - /> - } - /> - } - /> - } - /> - } /> - + + + } /> + } /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } /> + } + /> + } /> + } /> + } + /> + } + /> + } + /> + } + /> + } + /> + } /> + + ); } @@ -208,6 +239,9 @@ const App = React.memo(function AppComponent() { const navigate = useNavigate(); const handleError = useErrorHandler(); const isDarkMode = useIsDark(); + const currentUserId = useCurrentUserId(); + const [dbIsLoaded, setDbIsLoaded] = useState(false); + const [startedSync, setStartedSync] = useState(false); useEffect(() => { handleError(() => { @@ -218,24 +252,49 @@ const App = React.memo(function AppComponent() { useEffect(() => { api.configureClient({ - shipName: window.our, + shipName: currentUserId, shipUrl: '', onReset: () => sync.syncStart(), onChannelReset: () => sync.handleDiscontinuity(), }); - sync.syncStart(); - }, []); + const syncStart = async () => { + await sync.syncStart(startedSync); + setStartedSync(true); + + // we need to check the size of the database here to see if it's not zero + // if it's not zero, set the dbIsLoaded to true + // this is necessary because we load a fresh db on every load and we + // can't be sure of when data has been loaded + + for (let i = 0; i < 10; i++) { + if (dbIsLoaded) { + break; + } + + const { databaseSizeBytes } = (await checkDb()) || { + databaseSizeBytes: 0, + }; + + if (databaseSizeBytes && databaseSizeBytes > 0) { + setDbIsLoaded(true); + break; + } + + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + }; + + syncStart(); + }, [dbIsLoaded, currentUserId, startedSync]); return (
- - - - - - - + + + + +
); diff --git a/apps/tlon-web-new/src/lib/triggers.ts b/apps/tlon-web-new/src/lib/triggers.ts index 3e9893eae0..91dad7acfb 100644 --- a/apps/tlon-web-new/src/lib/triggers.ts +++ b/apps/tlon-web-new/src/lib/triggers.ts @@ -54,8 +54,8 @@ BEGIN VALUES ( 'post_reactions', 'INSERT', - NEW.id, - json_object('id', NEW.id, 'post_id', NEW.post_id) + json_object('contact_id', NEW.contact_id, 'post_id', NEW.post_id), + json_object('contact_id', NEW.contact_id, 'post_id', NEW.post_id) ); END; @@ -66,8 +66,8 @@ BEGIN VALUES ( 'post_reactions', 'UPDATE', - NEW.id, - json_object('id', NEW.id, 'post_id', NEW.post_id) + json_object('contact_id', NEW.contact_id, 'post_id', NEW.post_id), + json_object('contact_id', NEW.contact_id, 'post_id', NEW.post_id) ); END; @@ -91,8 +91,8 @@ BEGIN VALUES ( 'thread_unreads', 'INSERT', - NEW.id, - json_object('id', NEW.id, 'thread_id', NEW.thread_id) + json_object('channel_id', NEW.channel_id, 'thread_id', NEW.thread_id), + json_object('channel_id', NEW.channel_id, 'thread_id', NEW.thread_id) ); END; @@ -103,8 +103,8 @@ BEGIN VALUES ( 'thread_unreads', 'UPDATE', - NEW.id, - json_object('id', NEW.id, 'thread_id', NEW.thread_id) + json_object('channel_id', NEW.channel_id, 'thread_id', NEW.thread_id), + json_object('channel_id', NEW.channel_id, 'thread_id', NEW.thread_id) ); END; @@ -115,8 +115,8 @@ BEGIN VALUES ( 'thread_unreads', 'DELETE', - OLD.id, - json_object('id', OLD.id, 'thread_id', OLD.thread_id) + json_object('channel_id', OLD.channel_id, 'thread_id', OLD.thread_id), + json_object('channel_id', OLD.channel_id, 'thread_id', OLD.thread_id) ); END; `; diff --git a/apps/tlon-web-new/src/lib/webDb.ts b/apps/tlon-web-new/src/lib/webDb.ts index d51091a576..47b51f9936 100644 --- a/apps/tlon-web-new/src/lib/webDb.ts +++ b/apps/tlon-web-new/src/lib/webDb.ts @@ -1,4 +1,4 @@ -import { createDevLogger, escapeLog } from '@tloncorp/shared'; +import { createDevLogger } from '@tloncorp/shared'; import type { Schema } from '@tloncorp/shared/dist/db'; import { handleChange, schema, setClient } from '@tloncorp/shared/dist/db'; import { migrations } from '@tloncorp/shared/dist/db/migrations'; @@ -37,7 +37,6 @@ export async function setupDb() { // await sqlocal.sql('PRAGMA journal_mode=MEMORY'); await sqlocal.sql('PRAGMA synchronous=OFF'); await sqlocal.sql('PRAGMA journal_mode=WAL'); - // await sqlocal.sql(TRIGGER_SETUP); const { driver } = sqlocal; @@ -46,7 +45,7 @@ export async function setupDb() { logger: enableLogger ? { logQuery(query, params) { - logger.log(escapeLog(query), params); + logger.log(query, params); }, } : undefined, @@ -56,13 +55,21 @@ export async function setupDb() { logger.log('SQLite database opened:', dbInfo); setClient(client); - - // startChangePolling(); } catch (e) { logger.error('Failed to setup SQLite db', e); } } +export async function checkDb() { + if (!sqlocal) { + logger.warn('checkDb called before setupDb, ignoring'); + return; + } + const dbInfo = await sqlocal.getDatabaseInfo(); + logger.log('SQLite database info:', dbInfo); + return dbInfo; +} + let isPolling = false; function startChangePolling() { @@ -162,6 +169,8 @@ async function runMigrations() { logger.log('runMigrations: starting migration'); await migrate(client, migrations, sqlocal); logger.log('runMigrations: migrations succeeded'); + await sqlocal.sql(TRIGGER_SETUP); + startChangePolling(); return; } catch (e) { logger.log('migrations failed, purging db and retrying', e); diff --git a/packages/shared/src/api/chatApi.ts b/packages/shared/src/api/chatApi.ts index a977a9e2d8..e88d51911f 100644 --- a/packages/shared/src/api/chatApi.ts +++ b/packages/shared/src/api/chatApi.ts @@ -11,7 +11,7 @@ import { import { toPostData, toPostReplyData, toReplyMeta } from './postsApi'; import { getCurrentUserId, poke, scry, subscribe, trackedPoke } from './urbit'; -const logger = createDevLogger('chatApi', true); +const logger = createDevLogger('chatApi', false); export const markChatRead = (whom: string) => poke({ diff --git a/packages/shared/src/db/keyValue.ts b/packages/shared/src/db/keyValue.ts index 85bdb0d626..10343212ca 100644 --- a/packages/shared/src/db/keyValue.ts +++ b/packages/shared/src/db/keyValue.ts @@ -9,7 +9,7 @@ import { import { createDevLogger } from '../debug'; import * as ub from '../urbit'; -const logger = createDevLogger('keyValueStore', true); +const logger = createDevLogger('keyValueStore', false); export const ACTIVITY_SEEN_MARKER_QUERY_KEY = [ 'activity', diff --git a/packages/shared/src/store/sync.ts b/packages/shared/src/store/sync.ts index 7ef76d5633..0ffa7b0b4e 100644 --- a/packages/shared/src/store/sync.ts +++ b/packages/shared/src/store/sync.ts @@ -680,7 +680,6 @@ export const handleContactUpdate = async (update: api.ContactsUpdate) => { }; export const handleStorageUpdate = async (update: api.StorageUpdate) => { - console.log('storage update', update); switch (update.type) { case 'storageCredentialsChanged': { await db.setStorageCredentials(update.credentials); From 31ff577b34a225b0a6a3f09c8d08808a40423c3c Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 12 Sep 2024 16:20:17 -0400 Subject: [PATCH 010/157] expose: styling changes for chat, gallery --- desk/app/expose.hoon | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 799d3392b7..5ec7bff711 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -187,7 +187,7 @@ "" %- some %: build "chat" - (heads ?:(=("" title) "collection item" title) ~) + (heads ?:(=("" title) "Gallery item" title) ~) (heap-prelude title) (story:en-manx:u content.u.msg) == @@ -1789,18 +1789,34 @@ text-decoration: underline; } } + + body.chat { + min-height: 100vh; + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: center; + } + .cover { border-radius: 1em; - margin-bottom: 1.5em; + margin-bottom: 1em; } + .prelude { display: flex; flex-direction: column; gap: 0.4em; border-bottom: 0.125em solid var(--border); - padding-bottom: 1.5em; - margin-bottom: 1.5em; + padding-bottom: 1em; + margin-bottom: 1em; + } + + .chat .prelude { + flex-direction: row-reverse; + justify-content: space-between; } + .prelude .author { display: flex; align-items: center; @@ -1809,29 +1825,33 @@ font-weight: 500; color: var(--text-muted); } + .prelude .author .avatar { border-radius: 0.25em; overflow: hidden; } + .prelude .author .avatar img { width: 1.5625em; height: 1.5625em; object-fit: cover; } + .prelude time { color: var(--text-muted); font-size: 0.8em; padding: 0; } + footer { display: flex; flex-direction: column; align-items: center; } + footer p { font-size: 0.6em; } - ''' :: ++ heads From 65100eedb6b6781ab2e5539cba57c5ae9c45c96d Mon Sep 17 00:00:00 2001 From: fang Date: Thu, 12 Sep 2024 22:29:32 +0200 Subject: [PATCH 011/157] expose: pretty-print dates & upgrade to local time Render datetimes in wide-form text representation. Add a tiny bit of javascript to re-render those UTC timestamps in the local timezone on pageload. --- desk/app/expose.hoon | 50 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 799d3392b7..fe39f77cb4 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -207,8 +207,21 @@ == ;+ footer == + ;script(type "text/javascript"):"{(trip time-script)}" == :: + ++ time-script + ''' + const a = document.getElementsByClassName('timestamp-utc'); + for (const e of a) { + const t = new Date(Number(e.attributes['ms'].value)); + e.innerText = t.toLocaleString('en-US', {month: 'long'}) + ' ' + + (a=>a+=[,"st","nd","rd"][a.match`1?.$`]||"th")(''+t.getDate()) + ', ' + + t.getFullYear() + ', ' + + t.toLocaleString('en-US', {hour: '2-digit', minute: '2-digit', hour12: false}); + }; + ''' + :: ++ style ''' :root { @@ -1887,7 +1900,7 @@ ^- manx ;div.prelude ;div.published - ;time:"{(scow %da (sub sent.u.msg (mod sent.u.msg ~s1)))}" ::TODO nicer format + ;+ (render-datetime sent.u.msg) == ;+ author-node == @@ -1898,7 +1911,7 @@ :~ ;h1:"{title}" ;div.prelude ;div.published - ;time:"{(scow %da (sub sent.u.msg (mod sent.u.msg ~s1)))}" ::TODO nicer format + ;+ (render-datetime sent.u.msg) == ;+ author-node == @@ -1912,7 +1925,7 @@ [-]~ ;div.prelude ;div.published - ;time:"{(scow %da (sub sent.u.msg (mod sent.u.msg ~s1)))}" ::TODO nicer format + ;+ (render-datetime sent.u.msg) == ;+ author-node == @@ -1943,6 +1956,37 @@ ;span(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" == :: + ++ render-datetime + |= =time + ^- manx + =, chrono:userlib + =; utc=tape + ::NOTE timestamp-utc class and ms attr used by +time-script, + :: which replaces this rendering with the local time + ;time.timestamp-utc(ms (a-co:^co (unm time))) + ; {utc} + == + =/ =date (yore time) + |^ "{(snag (dec m.date) mon:yu)} ". + "{(num d.t.date)}{(ith d.t.date)}, ". + "{(num y.date)}, ". + "{(dum h.t.date)}:{(dum m.t.date)} (UTC)" + ++ num a-co:^co + ++ dum (d-co:^co 2) + ++ ith + |= n=@ud + ?- n + %1 "st" + %2 "nd" + %3 "rd" + ?(%11 %12 %13) "th" + :: + @ + ?: (lth n 10) "th" + $(n (mod n 10)) + == + -- + :: ++ first-inline |= content=story:d ^- (list inline:d) From 00ce0e1705863d31593bd567e754c7d07c4655a7 Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Thu, 12 Sep 2024 17:21:12 -0400 Subject: [PATCH 012/157] bump rn to 0.73.9 (#3914) --- .../ios/Landscape.xcodeproj/project.pbxproj | 8 + .../ios/Landscape/PrivacyInfo.xcprivacy | 50 + apps/tlon-mobile/ios/Podfile.lock | 476 +++++----- apps/tlon-mobile/package.json | 2 +- pnpm-lock.yaml | 853 +++++++++--------- 5 files changed, 710 insertions(+), 679 deletions(-) create mode 100644 apps/tlon-mobile/ios/Landscape/PrivacyInfo.xcprivacy diff --git a/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj b/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj index 98a9fcb2a3..559c2a30ad 100644 --- a/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj +++ b/apps/tlon-mobile/ios/Landscape.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 3DC46BC1B669DF8F5F059CA8 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACB38DC4FF4D6377774BF5F /* ExpoModulesProvider.swift */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; + 4ACCD7281520ED84DDC2759A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 74D2BEF1344CF71A07C17F5E /* PrivacyInfo.xcprivacy */; }; 630DE0B72C51A0F80053603B /* Error+isAFTimeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632793BD2C4AE4B500F942B1 /* Error+isAFTimeout.swift */; }; 630DE0C52C51A8780053603B /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70D386702A60A3E600AFB46E /* UIColor+Extension.swift */; }; 630DE0C62C51A8780053603B /* Contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700B635C2A71DFE90017F40F /* Contact.swift */; }; @@ -156,6 +157,7 @@ 70F99AAA2B2D337600D77256 /* SettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700B635F2A71E0810017F40F /* SettingsStore.swift */; }; 70F99AAB2B2D337600D77256 /* UserDefaultsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7036E3532ACD0FC90020A9FB /* UserDefaultsStore.swift */; }; 70F99AAC2B2D338E00D77256 /* UrbitModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70EAEAB42A57A99100FE96E4 /* UrbitModule.swift */; }; + 855125DEE814E6B0DFE6229D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 9D6B23EFDEDD5888235A22BB /* PrivacyInfo.xcprivacy */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; C184662C2ABBDFF1008EA8C0 /* GoogleService-Info-io.tlon.groups.plist in Resources */ = {isa = PBXBuildFile; fileRef = C184662B2ABBDFF1008EA8C0 /* GoogleService-Info-io.tlon.groups.plist */; }; C83014822C7BA74C00D9A5CA /* UIFont+SystemDesign.m in Sources */ = {isa = PBXBuildFile; fileRef = C83014812C7BA74C00D9A5CA /* UIFont+SystemDesign.m */; }; @@ -267,9 +269,11 @@ 70F99A8E2B2D2B5700D77256 /* LandscapeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LandscapeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 70F99A972B2D2B6E00D77256 /* YarnTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YarnTests.swift; sourceTree = ""; }; 74633B6584F10A6AA2FEE339 /* Pods-Landscape.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Landscape.release.xcconfig"; path = "Target Support Files/Pods-Landscape/Pods-Landscape.release.xcconfig"; sourceTree = ""; }; + 74D2BEF1344CF71A07C17F5E /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Landscape/PrivacyInfo.xcprivacy; sourceTree = ""; }; 79BDD5DCE15A385BB361AED1 /* Pods_Landscape.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Landscape.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7A4D352CD337FB3A3BF06240 /* Pods-Landscape.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Landscape.release.xcconfig"; path = "Target Support Files/Pods-Landscape/Pods-Landscape.release.xcconfig"; sourceTree = ""; }; 8ACB38DC4FF4D6377774BF5F /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Landscape-preview/ExpoModulesProvider.swift"; sourceTree = ""; }; + 9D6B23EFDEDD5888235A22BB /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Landscape/PrivacyInfo.xcprivacy; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Landscape/SplashScreen.storyboard; sourceTree = ""; }; B35AC6CF758CAFC9D112A69F /* Pods-Landscape.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Landscape.debug.xcconfig"; path = "Target Support Files/Pods-Landscape/Pods-Landscape.debug.xcconfig"; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; @@ -353,6 +357,7 @@ 70EAEAB62A57AAF200FE96E4 /* UrbitModule.m */, 63E27E122C5AF5B8008ACB45 /* HTTPCookieStorage+forwardChanges.swift */, 70D386652A60A37000AFB46E /* Extensions */, + 74D2BEF1344CF71A07C17F5E /* PrivacyInfo.xcprivacy */, ); name = Landscape; sourceTree = ""; @@ -495,6 +500,7 @@ 2D16E6871FA4F8E400B85C8A /* Frameworks */, D65327D7A22EEC0BE12398D9 /* Pods */, D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */, + 9D6B23EFDEDD5888235A22BB /* PrivacyInfo.xcprivacy */, ); indentWidth = 2; sourceTree = ""; @@ -737,6 +743,7 @@ C184662C2ABBDFF1008EA8C0 /* GoogleService-Info-io.tlon.groups.plist in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, 70D386722A61F2E400AFB46E /* main.jsbundle in Resources */, + 4ACCD7281520ED84DDC2759A /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -763,6 +770,7 @@ 70DBC00B2B7C60B50021EA96 /* SplashScreen.storyboard in Resources */, 70DBC0182B7C61130021EA96 /* GoogleService-Info-io.tlon.groups.preview.plist in Resources */, 70DBC00C2B7C60B50021EA96 /* main.jsbundle in Resources */, + 855125DEE814E6B0DFE6229D /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/apps/tlon-mobile/ios/Landscape/PrivacyInfo.xcprivacy b/apps/tlon-mobile/ios/Landscape/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..5ca66329e4 --- /dev/null +++ b/apps/tlon-mobile/ios/Landscape/PrivacyInfo.xcprivacy @@ -0,0 +1,50 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + 0A2A.1 + 3B52.1 + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + 85F4.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + 1C8F.1 + C56D.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/apps/tlon-mobile/ios/Podfile.lock b/apps/tlon-mobile/ios/Podfile.lock index 8207fd9ee5..f6e8a99cc4 100644 --- a/apps/tlon-mobile/ios/Podfile.lock +++ b/apps/tlon-mobile/ios/Podfile.lock @@ -149,14 +149,14 @@ PODS: - React-Core - sqlite3 (~> 3.42.0) - EXUpdatesInterface (0.15.3) - - FBLazyVector (0.73.4) - - FBReactNativeSpec (0.73.4): + - FBLazyVector (0.73.9) + - FBReactNativeSpec (0.73.9): - RCT-Folly (= 2022.05.16.00) - - RCTRequired (= 0.73.4) - - RCTTypeSafety (= 0.73.4) - - React-Core (= 0.73.4) - - React-jsi (= 0.73.4) - - ReactCommon/turbomodule/core (= 0.73.4) + - RCTRequired (= 0.73.9) + - RCTTypeSafety (= 0.73.9) + - React-Core (= 0.73.9) + - React-jsi (= 0.73.9) + - ReactCommon/turbomodule/core (= 0.73.9) - Firebase/CoreOnly (10.24.0): - FirebaseCore (= 10.24.0) - Firebase/Crashlytics (10.24.0): @@ -241,9 +241,9 @@ PODS: - GoogleUtilities/UserDefaults (7.13.3): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - hermes-engine (0.73.4): - - hermes-engine/Pre-built (= 0.73.4) - - hermes-engine/Pre-built (0.73.4) + - hermes-engine (0.73.9): + - hermes-engine/Pre-built (= 0.73.9) + - hermes-engine/Pre-built (0.73.9) - libaom (3.0.0): - libvmaf (>= 2.2.0) - libavif (0.11.1): @@ -300,27 +300,27 @@ PODS: - fmt (~> 6.2.1) - glog - libevent - - RCTRequired (0.73.4) - - RCTTypeSafety (0.73.4): - - FBLazyVector (= 0.73.4) - - RCTRequired (= 0.73.4) - - React-Core (= 0.73.4) + - RCTRequired (0.73.9) + - RCTTypeSafety (0.73.9): + - FBLazyVector (= 0.73.9) + - RCTRequired (= 0.73.9) + - React-Core (= 0.73.9) - ReachabilitySwift (5.2.3) - - React (0.73.4): - - React-Core (= 0.73.4) - - React-Core/DevSupport (= 0.73.4) - - React-Core/RCTWebSocket (= 0.73.4) - - React-RCTActionSheet (= 0.73.4) - - React-RCTAnimation (= 0.73.4) - - React-RCTBlob (= 0.73.4) - - React-RCTImage (= 0.73.4) - - React-RCTLinking (= 0.73.4) - - React-RCTNetwork (= 0.73.4) - - React-RCTSettings (= 0.73.4) - - React-RCTText (= 0.73.4) - - React-RCTVibration (= 0.73.4) - - React-callinvoker (0.73.4) - - React-Codegen (0.73.4): + - React (0.73.9): + - React-Core (= 0.73.9) + - React-Core/DevSupport (= 0.73.9) + - React-Core/RCTWebSocket (= 0.73.9) + - React-RCTActionSheet (= 0.73.9) + - React-RCTAnimation (= 0.73.9) + - React-RCTBlob (= 0.73.9) + - React-RCTImage (= 0.73.9) + - React-RCTLinking (= 0.73.9) + - React-RCTNetwork (= 0.73.9) + - React-RCTSettings (= 0.73.9) + - React-RCTText (= 0.73.9) + - React-RCTVibration (= 0.73.9) + - React-callinvoker (0.73.9) + - React-Codegen (0.73.9): - DoubleConversion - FBReactNativeSpec - glog @@ -335,11 +335,11 @@ PODS: - React-rncore - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-Core (0.73.4): + - React-Core (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-Core/Default (= 0.73.4) + - React-Core/Default (= 0.73.9) - React-cxxreact - React-hermes - React-jsi @@ -349,7 +349,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/CoreModulesHeaders (0.73.4): + - React-Core/CoreModulesHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -363,7 +363,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/Default (0.73.4): + - React-Core/Default (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -376,23 +376,23 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/DevSupport (0.73.4): + - React-Core/DevSupport (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-Core/Default (= 0.73.4) - - React-Core/RCTWebSocket (= 0.73.4) + - React-Core/Default (= 0.73.9) + - React-Core/RCTWebSocket (= 0.73.9) - React-cxxreact - React-hermes - React-jsi - React-jsiexecutor - - React-jsinspector (= 0.73.4) + - React-jsinspector (= 0.73.9) - React-perflogger - React-runtimescheduler - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTActionSheetHeaders (0.73.4): + - React-Core/RCTActionSheetHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -406,7 +406,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTAnimationHeaders (0.73.4): + - React-Core/RCTAnimationHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -420,7 +420,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTBlobHeaders (0.73.4): + - React-Core/RCTBlobHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -434,7 +434,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTImageHeaders (0.73.4): + - React-Core/RCTImageHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -448,7 +448,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTLinkingHeaders (0.73.4): + - React-Core/RCTLinkingHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -462,7 +462,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTNetworkHeaders (0.73.4): + - React-Core/RCTNetworkHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -476,7 +476,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTSettingsHeaders (0.73.4): + - React-Core/RCTSettingsHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -490,7 +490,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTTextHeaders (0.73.4): + - React-Core/RCTTextHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -504,7 +504,7 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTVibrationHeaders (0.73.4): + - React-Core/RCTVibrationHeaders (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -518,11 +518,11 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTWebSocket (0.73.4): + - React-Core/RCTWebSocket (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-Core/Default (= 0.73.4) + - React-Core/Default (= 0.73.9) - React-cxxreact - React-hermes - React-jsi @@ -532,33 +532,33 @@ PODS: - React-utils - SocketRocket (= 0.6.1) - Yoga - - React-CoreModules (0.73.4): + - React-CoreModules (0.73.9): - RCT-Folly (= 2022.05.16.00) - - RCTTypeSafety (= 0.73.4) + - RCTTypeSafety (= 0.73.9) - React-Codegen - - React-Core/CoreModulesHeaders (= 0.73.4) - - React-jsi (= 0.73.4) + - React-Core/CoreModulesHeaders (= 0.73.9) + - React-jsi (= 0.73.9) - React-NativeModulesApple - React-RCTBlob - - React-RCTImage (= 0.73.4) + - React-RCTImage (= 0.73.9) - ReactCommon - SocketRocket (= 0.6.1) - - React-cxxreact (0.73.4): + - React-cxxreact (0.73.9): - boost (= 1.83.0) - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4) - - React-debug (= 0.73.4) - - React-jsi (= 0.73.4) - - React-jsinspector (= 0.73.4) - - React-logger (= 0.73.4) - - React-perflogger (= 0.73.4) - - React-runtimeexecutor (= 0.73.4) - - React-debug (0.73.4) - - React-Fabric (0.73.4): + - React-callinvoker (= 0.73.9) + - React-debug (= 0.73.9) + - React-jsi (= 0.73.9) + - React-jsinspector (= 0.73.9) + - React-logger (= 0.73.9) + - React-perflogger (= 0.73.9) + - React-runtimeexecutor (= 0.73.9) + - React-debug (0.73.9) + - React-Fabric (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -569,20 +569,20 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/animations (= 0.73.4) - - React-Fabric/attributedstring (= 0.73.4) - - React-Fabric/componentregistry (= 0.73.4) - - React-Fabric/componentregistrynative (= 0.73.4) - - React-Fabric/components (= 0.73.4) - - React-Fabric/core (= 0.73.4) - - React-Fabric/imagemanager (= 0.73.4) - - React-Fabric/leakchecker (= 0.73.4) - - React-Fabric/mounting (= 0.73.4) - - React-Fabric/scheduler (= 0.73.4) - - React-Fabric/telemetry (= 0.73.4) - - React-Fabric/templateprocessor (= 0.73.4) - - React-Fabric/textlayoutmanager (= 0.73.4) - - React-Fabric/uimanager (= 0.73.4) + - React-Fabric/animations (= 0.73.9) + - React-Fabric/attributedstring (= 0.73.9) + - React-Fabric/componentregistry (= 0.73.9) + - React-Fabric/componentregistrynative (= 0.73.9) + - React-Fabric/components (= 0.73.9) + - React-Fabric/core (= 0.73.9) + - React-Fabric/imagemanager (= 0.73.9) + - React-Fabric/leakchecker (= 0.73.9) + - React-Fabric/mounting (= 0.73.9) + - React-Fabric/scheduler (= 0.73.9) + - React-Fabric/telemetry (= 0.73.9) + - React-Fabric/templateprocessor (= 0.73.9) + - React-Fabric/textlayoutmanager (= 0.73.9) + - React-Fabric/uimanager (= 0.73.9) - React-graphics - React-jsi - React-jsiexecutor @@ -591,7 +591,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/animations (0.73.4): + - React-Fabric/animations (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -610,7 +610,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (0.73.4): + - React-Fabric/attributedstring (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -629,7 +629,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (0.73.4): + - React-Fabric/componentregistry (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -648,7 +648,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (0.73.4): + - React-Fabric/componentregistrynative (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -667,7 +667,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components (0.73.4): + - React-Fabric/components (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -678,17 +678,17 @@ PODS: - React-Core - React-cxxreact - React-debug - - React-Fabric/components/inputaccessory (= 0.73.4) - - React-Fabric/components/legacyviewmanagerinterop (= 0.73.4) - - React-Fabric/components/modal (= 0.73.4) - - React-Fabric/components/rncore (= 0.73.4) - - React-Fabric/components/root (= 0.73.4) - - React-Fabric/components/safeareaview (= 0.73.4) - - React-Fabric/components/scrollview (= 0.73.4) - - React-Fabric/components/text (= 0.73.4) - - React-Fabric/components/textinput (= 0.73.4) - - React-Fabric/components/unimplementedview (= 0.73.4) - - React-Fabric/components/view (= 0.73.4) + - React-Fabric/components/inputaccessory (= 0.73.9) + - React-Fabric/components/legacyviewmanagerinterop (= 0.73.9) + - React-Fabric/components/modal (= 0.73.9) + - React-Fabric/components/rncore (= 0.73.9) + - React-Fabric/components/root (= 0.73.9) + - React-Fabric/components/safeareaview (= 0.73.9) + - React-Fabric/components/scrollview (= 0.73.9) + - React-Fabric/components/text (= 0.73.9) + - React-Fabric/components/textinput (= 0.73.9) + - React-Fabric/components/unimplementedview (= 0.73.9) + - React-Fabric/components/view (= 0.73.9) - React-graphics - React-jsi - React-jsiexecutor @@ -697,7 +697,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/inputaccessory (0.73.4): + - React-Fabric/components/inputaccessory (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -716,7 +716,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (0.73.4): + - React-Fabric/components/legacyviewmanagerinterop (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -735,7 +735,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/modal (0.73.4): + - React-Fabric/components/modal (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -754,7 +754,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/rncore (0.73.4): + - React-Fabric/components/rncore (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -773,7 +773,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/root (0.73.4): + - React-Fabric/components/root (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -792,7 +792,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/safeareaview (0.73.4): + - React-Fabric/components/safeareaview (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -811,7 +811,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/scrollview (0.73.4): + - React-Fabric/components/scrollview (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -830,7 +830,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/text (0.73.4): + - React-Fabric/components/text (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -849,7 +849,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/textinput (0.73.4): + - React-Fabric/components/textinput (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -868,7 +868,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/unimplementedview (0.73.4): + - React-Fabric/components/unimplementedview (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -887,7 +887,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/components/view (0.73.4): + - React-Fabric/components/view (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -907,7 +907,7 @@ PODS: - React-utils - ReactCommon/turbomodule/core - Yoga - - React-Fabric/core (0.73.4): + - React-Fabric/core (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -926,7 +926,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (0.73.4): + - React-Fabric/imagemanager (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -945,7 +945,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (0.73.4): + - React-Fabric/leakchecker (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -964,7 +964,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/mounting (0.73.4): + - React-Fabric/mounting (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -983,7 +983,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/scheduler (0.73.4): + - React-Fabric/scheduler (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -1002,7 +1002,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/telemetry (0.73.4): + - React-Fabric/telemetry (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -1021,7 +1021,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (0.73.4): + - React-Fabric/templateprocessor (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -1040,7 +1040,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/textlayoutmanager (0.73.4): + - React-Fabric/textlayoutmanager (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -1060,7 +1060,7 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-Fabric/uimanager (0.73.4): + - React-Fabric/uimanager (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog @@ -1079,42 +1079,42 @@ PODS: - React-runtimescheduler - React-utils - ReactCommon/turbomodule/core - - React-FabricImage (0.73.4): + - React-FabricImage (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired (= 0.73.4) - - RCTTypeSafety (= 0.73.4) + - RCTRequired (= 0.73.9) + - RCTTypeSafety (= 0.73.9) - React-Fabric - React-graphics - React-ImageManager - React-jsi - - React-jsiexecutor (= 0.73.4) + - React-jsiexecutor (= 0.73.9) - React-logger - React-rendererdebug - React-utils - ReactCommon - Yoga - - React-graphics (0.73.4): + - React-graphics (0.73.9): - glog - RCT-Folly/Fabric (= 2022.05.16.00) - - React-Core/Default (= 0.73.4) + - React-Core/Default (= 0.73.9) - React-utils - - React-hermes (0.73.4): + - React-hermes (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - RCT-Folly/Futures (= 2022.05.16.00) - - React-cxxreact (= 0.73.4) + - React-cxxreact (= 0.73.9) - React-jsi - - React-jsiexecutor (= 0.73.4) - - React-jsinspector (= 0.73.4) - - React-perflogger (= 0.73.4) - - React-ImageManager (0.73.4): + - React-jsiexecutor (= 0.73.9) + - React-jsinspector (= 0.73.9) + - React-perflogger (= 0.73.9) + - React-ImageManager (0.73.9): - glog - RCT-Folly/Fabric - React-Core/Default @@ -1123,31 +1123,31 @@ PODS: - React-graphics - React-rendererdebug - React-utils - - React-jserrorhandler (0.73.4): + - React-jserrorhandler (0.73.9): - RCT-Folly/Fabric (= 2022.05.16.00) - React-debug - React-jsi - React-Mapbuffer - - React-jsi (0.73.4): + - React-jsi (0.73.9): - boost (= 1.83.0) - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-jsiexecutor (0.73.4): + - React-jsiexecutor (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-cxxreact (= 0.73.4) - - React-jsi (= 0.73.4) - - React-perflogger (= 0.73.4) - - React-jsinspector (0.73.4) - - React-logger (0.73.4): + - React-cxxreact (= 0.73.9) + - React-jsi (= 0.73.9) + - React-perflogger (= 0.73.9) + - React-jsinspector (0.73.9) + - React-logger (0.73.9): - glog - - React-Mapbuffer (0.73.4): + - React-Mapbuffer (0.73.9): - glog - React-debug - react-native-branch (5.9.2): @@ -1165,8 +1165,8 @@ PODS: - glog - RCT-Folly (= 2022.05.16.00) - React-Core - - React-nativeconfig (0.73.4) - - React-NativeModulesApple (0.73.4): + - React-nativeconfig (0.73.9) + - React-NativeModulesApple (0.73.9): - glog - hermes-engine - React-callinvoker @@ -1176,10 +1176,10 @@ PODS: - React-runtimeexecutor - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - React-perflogger (0.73.4) - - React-RCTActionSheet (0.73.4): - - React-Core/RCTActionSheetHeaders (= 0.73.4) - - React-RCTAnimation (0.73.4): + - React-perflogger (0.73.9) + - React-RCTActionSheet (0.73.9): + - React-Core/RCTActionSheetHeaders (= 0.73.9) + - React-RCTAnimation (0.73.9): - RCT-Folly (= 2022.05.16.00) - RCTTypeSafety - React-Codegen @@ -1187,7 +1187,7 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTAppDelegate (0.73.4): + - React-RCTAppDelegate (0.73.9): - RCT-Folly - RCTRequired - RCTTypeSafety @@ -1201,7 +1201,7 @@ PODS: - React-RCTNetwork - React-runtimescheduler - ReactCommon - - React-RCTBlob (0.73.4): + - React-RCTBlob (0.73.9): - hermes-engine - RCT-Folly (= 2022.05.16.00) - React-Codegen @@ -1211,7 +1211,7 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTFabric (0.73.4): + - React-RCTFabric (0.73.9): - glog - hermes-engine - RCT-Folly/Fabric (= 2022.05.16.00) @@ -1229,7 +1229,7 @@ PODS: - React-runtimescheduler - React-utils - Yoga - - React-RCTImage (0.73.4): + - React-RCTImage (0.73.9): - RCT-Folly (= 2022.05.16.00) - RCTTypeSafety - React-Codegen @@ -1238,14 +1238,14 @@ PODS: - React-NativeModulesApple - React-RCTNetwork - ReactCommon - - React-RCTLinking (0.73.4): + - React-RCTLinking (0.73.9): - React-Codegen - - React-Core/RCTLinkingHeaders (= 0.73.4) - - React-jsi (= 0.73.4) + - React-Core/RCTLinkingHeaders (= 0.73.9) + - React-jsi (= 0.73.9) - React-NativeModulesApple - ReactCommon - - ReactCommon/turbomodule/core (= 0.73.4) - - React-RCTNetwork (0.73.4): + - ReactCommon/turbomodule/core (= 0.73.9) + - React-RCTNetwork (0.73.9): - RCT-Folly (= 2022.05.16.00) - RCTTypeSafety - React-Codegen @@ -1253,7 +1253,7 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTSettings (0.73.4): + - React-RCTSettings (0.73.9): - RCT-Folly (= 2022.05.16.00) - RCTTypeSafety - React-Codegen @@ -1261,25 +1261,25 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTText (0.73.4): - - React-Core/RCTTextHeaders (= 0.73.4) + - React-RCTText (0.73.9): + - React-Core/RCTTextHeaders (= 0.73.9) - Yoga - - React-RCTVibration (0.73.4): + - React-RCTVibration (0.73.9): - RCT-Folly (= 2022.05.16.00) - React-Codegen - React-Core/RCTVibrationHeaders - React-jsi - React-NativeModulesApple - ReactCommon - - React-rendererdebug (0.73.4): + - React-rendererdebug (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - RCT-Folly (= 2022.05.16.00) - React-debug - - React-rncore (0.73.4) - - React-runtimeexecutor (0.73.4): - - React-jsi (= 0.73.4) - - React-runtimescheduler (0.73.4): + - React-rncore (0.73.9) + - React-runtimeexecutor (0.73.9): + - React-jsi (= 0.73.9) + - React-runtimescheduler (0.73.9): - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) @@ -1290,48 +1290,48 @@ PODS: - React-rendererdebug - React-runtimeexecutor - React-utils - - React-utils (0.73.4): + - React-utils (0.73.9): - glog - RCT-Folly (= 2022.05.16.00) - React-debug - - ReactCommon (0.73.4): - - React-logger (= 0.73.4) - - ReactCommon/turbomodule (= 0.73.4) - - ReactCommon/turbomodule (0.73.4): + - ReactCommon (0.73.9): + - React-logger (= 0.73.9) + - ReactCommon/turbomodule (= 0.73.9) + - ReactCommon/turbomodule (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4) - - React-cxxreact (= 0.73.4) - - React-jsi (= 0.73.4) - - React-logger (= 0.73.4) - - React-perflogger (= 0.73.4) - - ReactCommon/turbomodule/bridging (= 0.73.4) - - ReactCommon/turbomodule/core (= 0.73.4) - - ReactCommon/turbomodule/bridging (0.73.4): + - React-callinvoker (= 0.73.9) + - React-cxxreact (= 0.73.9) + - React-jsi (= 0.73.9) + - React-logger (= 0.73.9) + - React-perflogger (= 0.73.9) + - ReactCommon/turbomodule/bridging (= 0.73.9) + - ReactCommon/turbomodule/core (= 0.73.9) + - ReactCommon/turbomodule/bridging (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4) - - React-cxxreact (= 0.73.4) - - React-jsi (= 0.73.4) - - React-logger (= 0.73.4) - - React-perflogger (= 0.73.4) - - ReactCommon/turbomodule/core (0.73.4): + - React-callinvoker (= 0.73.9) + - React-cxxreact (= 0.73.9) + - React-jsi (= 0.73.9) + - React-logger (= 0.73.9) + - React-perflogger (= 0.73.9) + - ReactCommon/turbomodule/core (0.73.9): - DoubleConversion - fmt (~> 6.2.1) - glog - hermes-engine - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4) - - React-cxxreact (= 0.73.4) - - React-jsi (= 0.73.4) - - React-logger (= 0.73.4) - - React-perflogger (= 0.73.4) + - React-callinvoker (= 0.73.9) + - React-cxxreact (= 0.73.9) + - React-jsi (= 0.73.9) + - React-logger (= 0.73.9) + - React-perflogger (= 0.73.9) - recaptcha-enterprise-react-native (18.4.0): - React-Core - RecaptchaEnterprise (= 18.4.0) @@ -1614,7 +1614,7 @@ EXTERNAL SOURCES: :podspec: "../../../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2023-11-17-RNv0.73.0-21043a3fc062be445e56a2c10ecd8be028dd9cc5 + :tag: hermes-2024-04-29-RNv0.73.8-644c8be78af1eae7c138fa4093fb87f0f4f8db85 op-sqlite: :path: "../../../node_modules/@op-engineering/op-sqlite" RCT-Folly: @@ -1743,7 +1743,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 BranchSDK: cb046c2714b03e573484ce9e349e2ddbad7016e8 - DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 + DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 EASClient: a42ee8bf36c93b3128352faf2ae49405ab4f80bd EXApplication: 137189a3f149b4e8e546884629392c3efc94cbd3 EXAV: e4f6137431ddc4cb025895046bfefa9612025c35 @@ -1755,7 +1755,7 @@ SPEC CHECKSUMS: EXNotifications: e11f0e9a5b657c064a481a5d522f3bc5a07bf7cd Expo: fb745b3074989670b6641f9f20463e8ee56a69ca expo-dev-client: dbc8e8a81d17a9d92e083a2856d056ba9a58984d - expo-dev-launcher: fcdb0f3e91c34778f0816b0f590c7a57f052a87c + expo-dev-launcher: 346abfe8bd102bdcf6605dee01380a97635b63dc expo-dev-menu: 166fc9c7b82641cdead1dc26d958d20a127ee97b expo-dev-menu-interface: 7ba029c9d1a82ac22b9b584c00514860b060553e ExpoBattery: 60bf880aea8f769fe39f709a920442542c1bfd62 @@ -1772,13 +1772,13 @@ SPEC CHECKSUMS: ExpoLocalization: f5f5d71dc0c9514d3d77b2771144f6fed6398d04 ExpoModulesCore: 2346e83abf90c2b2c16a54c3fc4a1169ae12816c ExpoSecureStore: c84ae37d1c36f38524d289c67c3a2e3fc56f1108 - EXSplashScreen: e12df5ed7e7880913a3e4bad91ba252596993b90 + EXSplashScreen: 6bd596128cd52fac91997ebc64f3d394c843a8f9 EXStructuredHeaders: 5b0f47259db047dc1fdfa84752e292c2bfa68ecd EXTaskManager: 3e446dbf75cd662aa6e7d6828be5bc26265241c3 - EXUpdates: f23be6de81d1c0ca07ef03995c253efe46abaa52 + EXUpdates: 40e069f2987861a6d39101c4496e816561f3e167 EXUpdatesInterface: 3e444e2093e25b7ca0999a7d8c16e8392dee70c3 - FBLazyVector: 84f6edbe225f38aebd9deaf1540a4160b1f087d7 - FBReactNativeSpec: 4b31c1954525bc2e3a5df4cbbd06fc7ae9191b11 + FBLazyVector: 98c189b92292d4bfeac13ffa8df3ce3d84e2fc5b + FBReactNativeSpec: f40d89f4be3e854b08cf9b66cba9e9d6c68d863d Firebase: 91fefd38712feb9186ea8996af6cbdef41473442 FirebaseABTesting: d87f56707159bae64e269757a6e963d490f2eebe FirebaseCore: 11dc8a16dfb7c5e3c3f45ba0e191a33ac4f50894 @@ -1792,10 +1792,10 @@ SPEC CHECKSUMS: FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 + glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 - hermes-engine: b2669ce35fc4ac14f523b307aff8896799829fe2 + hermes-engine: ed62e0dcd013bf4a3b487f164feec1c4e705b5b5 libaom: 144606b1da4b5915a1054383c3a4459ccdb3c661 libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 @@ -1806,53 +1806,53 @@ SPEC CHECKSUMS: PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0 - RCTRequired: ab7f915c15569f04a49669e573e6e319a53f9faa - RCTTypeSafety: 63b97ced7b766865057e7154db0e81ce4ee6cf1e + RCTRequired: d362a61864a64315aee00faea8dee6cf5b3f4aad + RCTTypeSafety: 09baf60faeab02492dc8bf04ce5af1dda645b86d ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979 - React: 1c87497e50fa40ba9c54e5ea5e53483a0f8eecc0 - React-callinvoker: e3a52a9a93e3eb004d7282c26a4fb27003273fe6 - React-Codegen: 6d326180cf9c4bf272f3c07ffc014df7c3e6f501 - React-Core: d0ecde72894b792cb8922efaa0990199cbe85169 - React-CoreModules: 2ff1684dd517f0c441495d90a704d499f05e9d0a - React-cxxreact: d9be2fac926741052395da0a6d0bab8d71e2f297 - React-debug: dfef758b88be3124182fb7f9b70f009cd3598feb - React-Fabric: 4004cd4af7eca7b4074d1b4f6709861869b57885 - React-FabricImage: 389dfe3111a50ac9ada46063532134b0fca72604 - React-graphics: 461a34ad990db297940fb7989bb0c246dff1c088 - React-hermes: b9ac2f7b0c1eeb206eb883583cab7a973d570a6e - React-ImageManager: 92cb6923e53e7a50864c78de714aabc1e1419cbf - React-jserrorhandler: 577ced2d6a0b5280057845de68b4147e2bc3f31d - React-jsi: 380cd24dd81a705dd042c18989fb10b07182210c - React-jsiexecutor: 8ed7a18b9f119440efdcd424c8257dc7e18067e2 - React-jsinspector: 9ac353eccf6ab54d1e0a33862ba91221d1e88460 - React-logger: 0a57b68dd2aec7ff738195f081f0520724b35dab - React-Mapbuffer: 114be72666d7e3e317c1a36fb566cd3ad712fcc8 + React: b87c7c7c12f8232bd7cfdc4a00bf687144c17e30 + React-callinvoker: 67de0bc05ecb7e690345a53a1661cea9b24670b0 + React-Codegen: dd60c11cfeb20cdd8b5a787603346d4e04f95499 + React-Core: 4c87a1873c6d11c6d3843582fbc266ba9ea304ce + React-CoreModules: 29ad1cbe757a70575913457bb7c646b7f4d4edf0 + React-cxxreact: 08ffaf2def6fe1ec8ef7f16e208587c96a87978e + React-debug: 30d97e8abfec9b6ba0ad111b92ac749d3ace2dd5 + React-Fabric: 8da8e4c0ed603fbed5208f5045cd1582a96ffa3d + React-FabricImage: f0cf2b8a823e98dae20013910bbdd0889bc719c2 + React-graphics: 6dc8dba8a095a962204dd8bd4ca8264a1ed718db + React-hermes: fdaab14cc289d8d9cd45ffd9a3f8aa11157d4c7e + React-ImageManager: 4a39d60f9163fce83474c2b79acec8336742983d + React-jserrorhandler: 81e19a227a75741456c8f7b1240fc8926fb48c7e + React-jsi: 2253621cb2fb5d43a78fec4db8989cf9711039df + React-jsiexecutor: 5e4620a87fbc4ab174d75220f06ba8b53ae8317b + React-jsinspector: aee04d04ef553d5e30e52a4de2af958cb060069f + React-logger: 87a4232dd55485435edfa6803ff0de0b5c9eea1a + React-Mapbuffer: 94db5977cd64330f9e715c19026c9a267d8358a5 react-native-branch: 021b9c261f732d0950e9a304284779d48bf81109 react-native-context-menu-view: dcec18eb8882e20596dbb75802e7d19cb87dac02 react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06 react-native-netinfo: 3aa5637c18834966e0c932de8ae1ae56fea20a97 react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b - react-native-webview: 1f6c37318115b6e96285e0b06770307d9a751bb8 - React-nativeconfig: ca8b90c736cf3be019cb332ca42d93dd95b32e05 - React-NativeModulesApple: 1fdffcce7772e274baeab33a1900f45feba86cbd - React-perflogger: 8a1e1af5733004bdd91258dcefbde21e0d1faccd - React-RCTActionSheet: 64bbff3a3963664c2d0146f870fe8e0264aee4c4 - React-RCTAnimation: b698168a7269265a4694727196484342d695f0c1 - React-RCTAppDelegate: dcd8e955116eb1d1908dfaf08b4c970812e6a1e6 - React-RCTBlob: 47f8c3b2b4b7fa2c5f19c43f0b7f77f57fb9d953 - React-RCTFabric: 1885187a31ad82c7270ea47f9ecd087a1df7ee3f - React-RCTImage: ac0e77a44c290b20db783649b2b9cddc93e3eb99 - React-RCTLinking: e626fd2900913fe5d25922ea1be394b7aafa09c9 - React-RCTNetwork: d3114bce3977dafe8bd06421b29812f5a8527ba0 - React-RCTSettings: a53511f90d8df637a1a11ac729179a4d2f734481 - React-RCTText: f0176f5f5952f9a4a2c7354f5ae71f7c420aaf34 - React-RCTVibration: 8160223c6eda5b187079fec204f80eca8b8f3177 - React-rendererdebug: 9a0563e113d2c80060eaef92063e259051328d2b - React-rncore: 65c642dc73eed5573dc373822f1f91dc66670851 - React-runtimeexecutor: e6ab6bb083dbdbdd489cff426ed0bce0652e6edf - React-runtimescheduler: 1c054b58fef2ce74cdcbdcd70db190e10f56a617 - React-utils: 21a798438d45e70ed9c2e2fe0894ee32ba7b7c5b - ReactCommon: dcc65c813041388dead6c8b477444757425ce961 + react-native-webview: ad868affda04ff2b204de83546193bc0325a8280 + React-nativeconfig: 44cd3076b158c39cb6758f238cd3e65953e989c0 + React-NativeModulesApple: af58ca346cf42c3bb4dab9173b6edd0c83c2a21c + React-perflogger: c93b6a895eca3f9196656bb20ce0e15ad597a4e9 + React-RCTActionSheet: 258842f426709dccbc2af31ca42b0a1807d76ad7 + React-RCTAnimation: 78c40269e35864f541b7486d17bd82a353c99fbc + React-RCTAppDelegate: d98ec2cdfb161d7a8496990e9649f94018025922 + React-RCTBlob: 593a5dbc58c45e3cccc37ad4498b10766ace26e6 + React-RCTFabric: c589babe0224cb137f64bfa4770e92d752c18012 + React-RCTImage: a0cdbb81db012ebc42c7dbaabdcb15f488c7c391 + React-RCTLinking: 82b6b0a5b2d5c8d3a28997e70bda46bac4be4c6e + React-RCTNetwork: 45e30079bcb987724028c9a93c0110b6d82e4a1f + React-RCTSettings: e67cbe694fe45b080b96b1394d4e45dff1b3ae04 + React-RCTText: 14a54686a1fa0b51b76660c7700980fdec6c3093 + React-RCTVibration: 00561f3d12dca44ed55af9060752bf8cf3fb0bfc + React-rendererdebug: 9e62f84756f080d88ed01b8063355c3a042934ed + React-rncore: e7f10bc6dbd75fa137583bd3b2bc4880203dbc1d + React-runtimeexecutor: bf98e8973ed4c45139fbbaf2c34af44053acc9a9 + React-runtimescheduler: 7a4ccc1854c5918dee508536fad93172b4c49629 + React-utils: 59bbef368c9ba8b6e27416b46933cd03a35f2841 + ReactCommon: 656e520d76937c8d781ef82a7186a4af7160d814 recaptcha-enterprise-react-native: 7d63c5bdde3b48996b984a86ac2b536a1d8f5f16 RecaptchaEnterprise: dc302910b77963a0cc6f6908407e30b35268a755 RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 @@ -1862,9 +1862,9 @@ SPEC CHECKSUMS: RNFBApp: 91311b27bc9a33e23b76a62825afd1635501018a RNFBCrashlytics: c3219ef7a0c779f2428236215781c38e7892f6f9 RNFBPerf: 2c926ff255c704a644dd53572008cba47c67ada0 - RNGestureHandler: f42730cc5dc0b50e2b6409e259e8c238356ec0d0 - RNReanimated: fb34efce9255966f5d71bd0fc65e14042c4b88a9 - RNScreens: 2b73f5eb2ac5d94fbd61fa4be0bfebd345716825 + RNGestureHandler: 9a413b04f827e0169f0f9f97a845c266da61f87c + RNReanimated: 4b1bce37b188450e3a2d03c925edd5fb11d4bb2d + RNScreens: a4d9ce8f68f833f4e42410140eafd88e38bba163 RNSVG: 3f65a03e0c61a8495dee92bf82545ed9041cbf3b SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9 SDWebImageAVIFCoder: 8348fef6d0ec69e129c66c9fe4d74fbfbf366112 @@ -1872,9 +1872,9 @@ SPEC CHECKSUMS: SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 sqlite3: f163dbbb7aa3339ad8fc622782c2d9d7b72f7e9c - tentap: f86059c95e32751ffdab4c513494b477b760c609 + tentap: 4871503ab3d587fe80fe13f6fe712fde98b57525 UMAppLoader: 5df85360d65cabaef544be5424ac64672e648482 - Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312 + Yoga: fb61b2337c7688c81a137e5560b3cbb515289f91 PODFILE CHECKSUM: 0cb7a78e5777e69c86c1bf4bb5135fd660376dbe diff --git a/apps/tlon-mobile/package.json b/apps/tlon-mobile/package.json index 4803052e9a..d1ac49f981 100644 --- a/apps/tlon-mobile/package.json +++ b/apps/tlon-mobile/package.json @@ -91,7 +91,7 @@ "posthog-react-native": "^2.7.1", "react": "^18.2.0", "react-hook-form": "^7.52.0", - "react-native": "0.73.4", + "react-native": "0.73.9", "react-native-branch": "^5.9.0", "react-native-context-menu-view": "^1.15.0", "react-native-country-codes-picker": "^2.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c92f77042e..48fe38950d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,7 +104,7 @@ importers: dependencies: '@10play/tentap-editor': specifier: 0.5.11 - version: 0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@aws-sdk/client-s3': specifier: ^3.190.0 version: 3.190.0 @@ -113,7 +113,7 @@ importers: version: 3.190.0 '@dev-plugins/async-storage': specifier: ^0.0.3 - version: 0.0.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) + version: 0.0.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) '@dev-plugins/react-navigation': specifier: ^0.0.6 version: 0.0.6(@react-navigation/core@6.4.10(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react@18.2.0) @@ -122,40 +122,40 @@ importers: version: 0.0.6(@tanstack/react-query@5.32.1(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) '@google-cloud/recaptcha-enterprise-react-native': specifier: ^18.3.0 - version: 18.4.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 18.4.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@gorhom/bottom-sheet': specifier: ^4.5.1 - version: 4.6.0(@types/react-native@0.73.0(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(@types/react@18.2.55)(react-native-gesture-handler@2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 4.6.0(@types/react-native@0.73.0(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(@types/react@18.2.55)(react-native-gesture-handler@2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@op-engineering/op-sqlite': specifier: 5.0.5 - version: 5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 5.0.5(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@react-native-async-storage/async-storage': specifier: 1.21.0 - version: 1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) '@react-native-clipboard/clipboard': specifier: ^1.14.0 - version: 1.14.0(react-native-macos@0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 1.14.0(react-native-macos@0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@react-native-community/netinfo': specifier: 11.1.0 - version: 11.1.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 11.1.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) '@react-native-firebase/app': specifier: ^19.2.2 - version: 19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@react-native-firebase/crashlytics': specifier: ^19.2.2 - version: 19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) + version: 19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) '@react-native-firebase/perf': specifier: 19.2.2 - version: 19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) + version: 19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) '@react-navigation/bottom-tabs': specifier: ^6.5.12 - version: 6.5.12(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 6.5.12(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@react-navigation/native': specifier: ^6.1.7 - version: 6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@react-navigation/native-stack': specifier: ^6.9.13 - version: 6.9.18(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 6.9.18(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tanstack/react-query': specifier: ~5.32.1 version: 5.32.1(react@18.2.0) @@ -260,7 +260,7 @@ importers: version: 4.17.21 posthog-react-native: specifier: ^2.7.1 - version: 2.11.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo-application@5.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-device@5.9.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-file-system@16.0.9(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-localization@14.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(react-native-device-info@10.12.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))) + version: 2.11.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo-application@5.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-device@5.9.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-file-system@16.0.9(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-localization@14.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(react-native-device-info@10.12.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))) react: specifier: ^18.2.0 version: 18.2.0 @@ -268,44 +268,44 @@ importers: specifier: ^7.52.0 version: 7.52.0(react@18.2.0) react-native: - specifier: 0.73.4 - version: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + specifier: 0.73.9 + version: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) react-native-branch: specifier: ^5.9.0 - version: 5.9.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 5.9.2(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) react-native-context-menu-view: specifier: ^1.15.0 - version: 1.15.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 1.15.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-country-codes-picker: specifier: ^2.3.3 - version: 2.3.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 2.3.5(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-device-info: specifier: ^10.8.0 - version: 10.12.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 10.12.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) react-native-fetch-api: specifier: ^3.0.0 version: 3.0.0 react-native-gesture-handler: specifier: ~2.18.0 - version: 2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-get-random-values: specifier: ^1.11.0 - version: 1.11.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 1.11.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) react-native-phone-input: specifier: ^1.3.7 - version: 1.3.7(@react-native-picker/picker@2.6.1(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 1.3.7(@react-native-picker/picker@2.6.1(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) react-native-polyfill-globals: specifier: ^3.1.0 - version: 3.1.0(base-64@1.0.0)(react-native-fetch-api@3.0.0)(react-native-get-random-values@1.11.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(react-native-url-polyfill@2.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(text-encoding@0.7.0)(web-streams-polyfill@3.3.3) + version: 3.1.0(base-64@1.0.0)(react-native-fetch-api@3.0.0)(react-native-get-random-values@1.11.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(react-native-url-polyfill@2.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(text-encoding@0.7.0)(web-streams-polyfill@3.3.3) react-native-reanimated: specifier: ^3.8.1 - version: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-safe-area-context: specifier: ^4.9.0 - version: 4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-screens: specifier: ~3.29.0 - version: 3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-sse: specifier: ^1.2.1 version: 1.2.1 @@ -314,19 +314,19 @@ importers: version: 1.0.1(patch_hash=pragjrandpl4ksuijrhddz3ljq) react-native-svg: specifier: ^15.0.0 - version: 15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 15.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-url-polyfill: specifier: ^2.0.0 - version: 2.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 2.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) react-native-webview: specifier: 13.6.4 - version: 13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 13.6.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) seedrandom: specifier: ^3.0.5 version: 3.0.5 tailwind-rn: specifier: ^4.2.0 - version: 4.2.0(patch_hash=huubbq5aurym44djogb6gnyabq)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.1) + version: 4.2.0(patch_hash=huubbq5aurym44djogb6gnyabq)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.1) text-encoding: specifier: ^0.7.0 version: 0.7.0 @@ -351,7 +351,7 @@ importers: version: 1.101.3(encoding@0.1.13)(react@18.2.0) '@testing-library/react-native': specifier: ^12.5.2 - version: 12.5.3(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react-test-renderer@18.2.0(react@18.2.0))(react@18.2.0) + version: 12.5.3(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react-test-renderer@18.2.0(react@18.2.0))(react@18.2.0) '@trivago/prettier-plugin-sort-imports': specifier: ^4.2.0 version: 4.3.0(prettier@3.2.5) @@ -420,7 +420,7 @@ importers: version: 6.1.1 react-native-svg-transformer: specifier: ^1.3.0 - version: 1.3.0(react-native-svg@15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(typescript@5.4.5) + version: 1.3.0(react-native-svg@15.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(typescript@5.4.5) react-test-renderer: specifier: 18.2.0 version: 18.2.0(react@18.2.0) @@ -483,13 +483,13 @@ importers: version: 1.101.3(encoding@0.1.13)(react@18.2.0) '@tanstack/react-query': specifier: ^4.28.0 - version: 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tanstack/react-query-devtools': specifier: ^4.28.0 - version: 4.29.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 4.29.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tanstack/react-query-persist-client': specifier: ^4.28.0 - version: 4.28.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)) + version: 4.28.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)) '@tanstack/react-virtual': specifier: ^3.0.0-beta.60 version: 3.0.0-beta.65(react@18.2.0) @@ -708,7 +708,7 @@ importers: version: 18.2.0 react-beautiful-dnd: specifier: ^13.1.1 - version: 13.1.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 13.1.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-colorful: specifier: ^5.5.1 version: 5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -744,7 +744,7 @@ importers: version: https://codeload.github.com/stefkampen/react-oembed-container/tar.gz/802eee0dba7986faa9c931b1c016acba5369d5f9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-qr-code: specifier: ^2.0.12 - version: 2.0.12(react-native-svg@15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) + version: 2.0.12(react-native-svg@15.0.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) react-router: specifier: ^6.22.1 version: 6.22.2(react@18.2.0) @@ -1024,13 +1024,13 @@ importers: version: 1.101.3(encoding@0.1.13)(react@18.2.0) '@tanstack/react-query': specifier: ^4.28.0 - version: 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tanstack/react-query-devtools': specifier: ^4.28.0 - version: 4.29.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 4.29.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tanstack/react-query-persist-client': specifier: ^4.28.0 - version: 4.28.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)) + version: 4.28.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)) '@tanstack/react-virtual': specifier: ^3.0.0-beta.60 version: 3.0.0-beta.65(react@18.2.0) @@ -1249,7 +1249,7 @@ importers: version: 18.2.0 react-beautiful-dnd: specifier: ^13.1.1 - version: 13.1.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 13.1.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-colorful: specifier: ^5.5.1 version: 5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -1288,7 +1288,7 @@ importers: version: https://codeload.github.com/stefkampen/react-oembed-container/tar.gz/802eee0dba7986faa9c931b1c016acba5369d5f9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-qr-code: specifier: ^2.0.12 - version: 2.0.12(react-native-svg@15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) + version: 2.0.12(react-native-svg@15.0.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) react-router: specifier: ^6.22.1 version: 6.22.2(react@18.2.0) @@ -1315,7 +1315,7 @@ importers: version: 1.8.1 sqlocal: specifier: ^0.11.1 - version: 0.11.1(drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0)) + version: 0.11.1(drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0)) tailwindcss-opentype: specifier: ^1.1.0 version: 1.1.0(tailwindcss@3.4.1) @@ -1529,7 +1529,7 @@ importers: dependencies: '@react-native-firebase/perf': specifier: 19.2.2 - version: 19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) + version: 19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) '@tloncorp/shared': specifier: workspace:* version: link:../shared @@ -1541,7 +1541,7 @@ importers: version: 4.17.21 react-native-device-info: specifier: ^10.8.0 - version: 10.12.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 10.12.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) zustand: specifier: ^3.7.2 version: 3.7.2(react@18.2.0) @@ -1557,7 +1557,7 @@ importers: dependencies: '@10play/tentap-editor': specifier: 0.5.11 - version: 0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tiptap/core': specifier: ^2.6.6 version: 2.6.6(@tiptap/pm@2.6.6) @@ -1606,7 +1606,7 @@ importers: dependencies: '@react-native-async-storage/async-storage': specifier: 1.21.0 - version: 1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + version: 1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) '@tanstack/react-query': specifier: ^5.32.1 version: 5.32.1(react@18.2.0) @@ -1633,7 +1633,7 @@ importers: version: 1.6.52 drizzle-orm: specifier: 0.30.9 - version: 0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0) + version: 0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0) exponential-backoff: specifier: ^3.1.1 version: 3.1.1 @@ -1674,19 +1674,19 @@ importers: dependencies: '@10play/tentap-editor': specifier: 0.5.11 - version: 0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@emoji-mart/data': specifier: ^1.1.2 version: 1.1.2 '@likashefqet/react-native-image-zoom': specifier: ^3.0.0 - version: 3.0.0(patch_hash=zkdmh374s554i7qlaqtqn2nhfm)(react-native-gesture-handler@2.16.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 3.0.0(patch_hash=zkdmh374s554i7qlaqtqn2nhfm)(react-native-gesture-handler@2.16.2(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@react-native-clipboard/clipboard': specifier: ^1.14.0 - version: 1.14.0(react-native-macos@0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 1.14.0(react-native-macos@0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/animations-moti': specifier: 1.101.3 - version: 1.101.3(moti@0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0)) + version: 1.101.3(moti@0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0)) '@tamagui/get-token': specifier: 1.101.3 version: 1.101.3(react@18.2.0) @@ -1734,7 +1734,7 @@ importers: version: 4.17.21 moti: specifier: ^0.28.1 - version: 0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) + version: 0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) react: specifier: '*' version: 18.2.0 @@ -1743,25 +1743,25 @@ importers: version: 7.52.0(react@18.2.0) react-native-context-menu-view: specifier: ^1.15.0 - version: 1.15.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 1.15.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-gesture-handler: specifier: ~2.16.2 - version: 2.16.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 2.16.2(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-reanimated: specifier: '*' - version: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-safe-area-context: specifier: ^4.9.0 - version: 4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 4.9.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-native-svg: specifier: ^15.0.0 - version: 15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 15.0.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react-tweet: specifier: ^3.0.4 version: 3.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) tamagui: specifier: 1.101.3 - version: 1.101.3(@types/react@18.2.55)(immer@9.0.21)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + version: 1.101.3(@types/react@18.2.55)(immer@9.0.21)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) urbit-ob: specifier: ^5.0.1 version: 5.0.1 @@ -4543,82 +4543,82 @@ packages: react-native-macos: ^0.73.0 react-native-windows: ^0.73.0 - '@react-native-community/cli-clean@12.3.2': - resolution: {integrity: sha512-90k2hCX0ddSFPT7EN7h5SZj0XZPXP0+y/++v262hssoey3nhurwF57NGWN0XAR0o9BSW7+mBfeInfabzDraO6A==} - '@react-native-community/cli-clean@12.3.6': resolution: {integrity: sha512-gUU29ep8xM0BbnZjwz9MyID74KKwutq9x5iv4BCr2im6nly4UMf1B1D+V225wR7VcDGzbgWjaezsJShLLhC5ig==} - '@react-native-community/cli-config@12.3.2': - resolution: {integrity: sha512-UUCzDjQgvAVL/57rL7eOuFUhd+d+6qfM7V8uOegQFeFEmSmvUUDLYoXpBa5vAK9JgQtSqMBJ1Shmwao+/oElxQ==} + '@react-native-community/cli-clean@12.3.7': + resolution: {integrity: sha512-BCYW77QqyxfhiMEBOoHyciJRNV6Rhz1RvclReIKnCA9wAwmoJBeu4Mu+AwiECA2bUITX16fvPt3NwDsSd1jwfQ==} '@react-native-community/cli-config@12.3.6': resolution: {integrity: sha512-JGWSYQ9EAK6m2v0abXwFLEfsqJ1zkhzZ4CV261QZF9MoUNB6h57a274h1MLQR9mG6Tsh38wBUuNfEPUvS1vYew==} - '@react-native-community/cli-debugger-ui@12.3.2': - resolution: {integrity: sha512-nSWQUL+51J682DlfcC1bjkUbQbGvHCC25jpqTwHIjmmVjYCX1uHuhPSqQKgPNdvtfOkrkACxczd7kVMmetxY2Q==} + '@react-native-community/cli-config@12.3.7': + resolution: {integrity: sha512-IU2UhO9yj1rEBNhHWGzIXpPDzha4hizLP/PUOrhR4BUf6RVPUWEp+e1PXNGR0qjIf6esu7OC7t6mLOhH0NUJEw==} '@react-native-community/cli-debugger-ui@12.3.6': resolution: {integrity: sha512-SjUKKsx5FmcK9G6Pb6UBFT0s9JexVStK5WInmANw75Hm7YokVvHEgtprQDz2Uvy5znX5g2ujzrkIU//T15KQzA==} - '@react-native-community/cli-doctor@12.3.2': - resolution: {integrity: sha512-GrAabdY4qtBX49knHFvEAdLtCjkmndjTeqhYO6BhsbAeKOtspcLT/0WRgdLIaKODRa61ADNB3K5Zm4dU0QrZOg==} + '@react-native-community/cli-debugger-ui@12.3.7': + resolution: {integrity: sha512-UHUFrRdcjWSCdWG9KIp2QjuRIahBQnb9epnQI7JCq6NFbFHYfEI4rI7msjMn+gG8/tSwKTV2PTPuPmZ5wWlE7Q==} '@react-native-community/cli-doctor@12.3.6': resolution: {integrity: sha512-fvBDv2lTthfw4WOQKkdTop2PlE9GtfrlNnpjB818MhcdEnPjfQw5YaTUcnNEGsvGomdCs1MVRMgYXXwPSN6OvQ==} - '@react-native-community/cli-hermes@12.3.2': - resolution: {integrity: sha512-SL6F9O8ghp4ESBFH2YAPLtIN39jdnvGBKnK4FGKpDCjtB3DnUmDsGFlH46S+GGt5M6VzfG2eeKEOKf3pZ6jUzA==} + '@react-native-community/cli-doctor@12.3.7': + resolution: {integrity: sha512-gCamZztRoAyhciuQPqdz4Xe4t3gOdNsaADNd+rva+Rx8W2PoPeNv60i7/et06wlsn6B6Sh0/hMiAftJbiHDFkg==} '@react-native-community/cli-hermes@12.3.6': resolution: {integrity: sha512-sNGwfOCl8OAIjWCkwuLpP8NZbuO0dhDI/2W7NeOGDzIBsf4/c4MptTrULWtGIH9okVPLSPX0NnRyGQ+mSwWyuQ==} - '@react-native-community/cli-platform-android@12.3.2': - resolution: {integrity: sha512-MZ5nO8yi/N+Fj2i9BJcJ9C/ez+9/Ir7lQt49DWRo9YDmzye66mYLr/P2l/qxsixllbbDi7BXrlLpxaEhMrDopg==} + '@react-native-community/cli-hermes@12.3.7': + resolution: {integrity: sha512-ezzeiSKjRXK2+i1AAe7NhhN9CEHrgtRmTn2MAdBpE++N8fH5EQZgxFcGgGdwGvns2fm9ivyyeVnI5eAYwvM+jg==} '@react-native-community/cli-platform-android@12.3.6': resolution: {integrity: sha512-DeDDAB8lHpuGIAPXeeD9Qu2+/wDTFPo99c8uSW49L0hkmZJixzvvvffbGQAYk32H0TmaI7rzvzH+qzu7z3891g==} - '@react-native-community/cli-platform-ios@12.3.2': - resolution: {integrity: sha512-OcWEAbkev1IL6SUiQnM6DQdsvfsKZhRZtoBNSj9MfdmwotVZSOEZJ+IjZ1FR9ChvMWayO9ns/o8LgoQxr1ZXeg==} + '@react-native-community/cli-platform-android@12.3.7': + resolution: {integrity: sha512-mOltF3cpjNdJb3WSFwEHc1GH4ibCcnOvQ34OdWyblKy9ijuvG5SjNTlYR/UW/CURaDi3OUKAhxQMTY5d27bzGQ==} '@react-native-community/cli-platform-ios@12.3.6': resolution: {integrity: sha512-3eZ0jMCkKUO58wzPWlvAPRqezVKm9EPZyaPyHbRPWU8qw7JqkvnRlWIaYDGpjCJgVW4k2hKsEursLtYKb188tg==} - '@react-native-community/cli-plugin-metro@12.3.2': - resolution: {integrity: sha512-FpFBwu+d2E7KRhYPTkKvQsWb2/JKsJv+t1tcqgQkn+oByhp+qGyXBobFB8/R3yYvRRDCSDhS+atWTJzk9TjM8g==} + '@react-native-community/cli-platform-ios@12.3.7': + resolution: {integrity: sha512-2WnVsMH4ORZIhBm/5nCms1NeeKG4KarNC7PMLmrXWXB/bibDcaNsjrJiqnmCUcpTEvTQTokRfoO7Aj6NM0Cqow==} '@react-native-community/cli-plugin-metro@12.3.6': resolution: {integrity: sha512-3jxSBQt4fkS+KtHCPSyB5auIT+KKIrPCv9Dk14FbvOaEh9erUWEm/5PZWmtboW1z7CYeNbFMeXm9fM2xwtVOpg==} - '@react-native-community/cli-server-api@12.3.2': - resolution: {integrity: sha512-iwa7EO9XFA/OjI5pPLLpI/6mFVqv8L73kNck3CNOJIUCCveGXBKK0VMyOkXaf/BYnihgQrXh+x5cxbDbggr7+Q==} + '@react-native-community/cli-plugin-metro@12.3.7': + resolution: {integrity: sha512-ahEw0Vfnv2Nv/jdZ2QDuGjQ9l2SczO4lXjb3ubu5vEYNLyTw3jYsLMK6iES7YQ/ApQmKdG476HU1O9uZdpaYPg==} '@react-native-community/cli-server-api@12.3.6': resolution: {integrity: sha512-80NIMzo8b2W+PL0Jd7NjiJW9mgaT8Y8wsIT/lh6mAvYH7mK0ecDJUYUTAAv79Tbo1iCGPAr3T295DlVtS8s4yQ==} - '@react-native-community/cli-tools@12.3.2': - resolution: {integrity: sha512-nDH7vuEicHI2TI0jac/DjT3fr977iWXRdgVAqPZFFczlbs7A8GQvEdGnZ1G8dqRUmg+kptw0e4hwczAOG89JzQ==} + '@react-native-community/cli-server-api@12.3.7': + resolution: {integrity: sha512-LYETs3CCjrLn1ZU0kYv44TywiIl5IPFHZGeXhAh2TtgOk4mo3kvXxECDil9CdO3bmDra6qyiG61KHvzr8IrHdg==} '@react-native-community/cli-tools@12.3.6': resolution: {integrity: sha512-FPEvZn19UTMMXUp/piwKZSh8cMEfO8G3KDtOwo53O347GTcwNrKjgZGtLSPELBX2gr+YlzEft3CoRv2Qmo83fQ==} - '@react-native-community/cli-types@12.3.2': - resolution: {integrity: sha512-9D0UEFqLW8JmS16mjHJxUJWX8E+zJddrHILSH8AJHZ0NNHv4u2DXKdb0wFLMobFxGNxPT+VSOjc60fGvXzWHog==} + '@react-native-community/cli-tools@12.3.7': + resolution: {integrity: sha512-7NL/1/i+wzd4fBr/FSr3ypR05tiU/Kv9l/M1sL1c6jfcDtWXAL90R161gQkQFK7shIQ8Idp0dQX1rq49tSyfQw==} '@react-native-community/cli-types@12.3.6': resolution: {integrity: sha512-xPqTgcUtZowQ8WKOkI9TLGBwH2bGggOC4d2FFaIRST3gTcjrEeGRNeR5aXCzJFIgItIft8sd7p2oKEdy90+01Q==} - '@react-native-community/cli@12.3.2': - resolution: {integrity: sha512-WgoUWwLDcf/G1Su2COUUVs3RzAwnV/vUTdISSpAUGgSc57mPabaAoUctKTnfYEhCnE3j02k3VtaVPwCAFRO3TQ==} - engines: {node: '>=18'} - hasBin: true + '@react-native-community/cli-types@12.3.7': + resolution: {integrity: sha512-NFtUMyIrNfi3A5C1cjVKDVvYHvvOF7MnOMwdD8jm2NQKewQJrehKBh1eMuykKdqhWyZmuemD4KKhL8f4FxgG0w==} '@react-native-community/cli@12.3.6': resolution: {integrity: sha512-647OSi6xBb8FbwFqX9zsJxOzu685AWtrOUWHfOkbKD+5LOpGORw+GQo0F9rWZnB68rLQyfKUZWJeaD00pGv5fw==} engines: {node: '>=18'} hasBin: true + '@react-native-community/cli@12.3.7': + resolution: {integrity: sha512-7+mOhk+3+X3BjSJZZvYrDJynA00gPYTlvT28ZjiLlbuVGfqfNiBKaxuF7rty+gjjpch4iKGvLhIhSN5cuOsdHQ==} + engines: {node: '>=18'} + hasBin: true + '@react-native-community/hooks@2.8.1': resolution: {integrity: sha512-DCmCIC0Gn9m6K0Mlg2MwNmTxMEpBu5lTLsI6b/XUAv/vLGa6o+X7RhCai4FWeqkjCU36+ZOwaLzDo4NBWMXaoQ==} peerDependencies: @@ -4719,20 +4719,16 @@ packages: peerDependencies: '@babel/preset-env': ^7.1.6 - '@react-native/community-cli-plugin@0.73.16': - resolution: {integrity: sha512-eNH3v3qJJF6f0n/Dck90qfC9gVOR4coAXMTdYECO33GfgjTi+73vf/SBqlXw9HICH/RNZYGPM3wca4FRF7TYeQ==} - engines: {node: '>=18'} - '@react-native/community-cli-plugin@0.73.17': resolution: {integrity: sha512-F3PXZkcHg+1ARIr6FRQCQiB7ZAA+MQXGmq051metRscoLvgYJwj7dgC8pvgy0kexzUkHu5BNKrZeySzUft3xuQ==} engines: {node: '>=18'} - '@react-native/debugger-frontend@0.73.3': - resolution: {integrity: sha512-RgEKnWuoo54dh7gQhV7kvzKhXZEhpF9LlMdZolyhGxHsBqZ2gXdibfDlfcARFFifPIiaZ3lXuOVVa4ei+uPgTw==} + '@react-native/community-cli-plugin@0.73.18': + resolution: {integrity: sha512-RN8piDh/eF+QT6YYmrj3Zd9uiaDsRY/kMT0FYR42j8/M/boE4hs4Xn0u91XzT8CAkU9q/ilyo3wJsXIJo2teww==} engines: {node: '>=18'} - '@react-native/dev-middleware@0.73.7': - resolution: {integrity: sha512-BZXpn+qKp/dNdr4+TkZxXDttfx8YobDh8MFHsMk9usouLm22pKgFIPkGBV0X8Do4LBkFNPGtrnsKkWk/yuUXKg==} + '@react-native/debugger-frontend@0.73.3': + resolution: {integrity: sha512-RgEKnWuoo54dh7gQhV7kvzKhXZEhpF9LlMdZolyhGxHsBqZ2gXdibfDlfcARFFifPIiaZ3lXuOVVa4ei+uPgTw==} engines: {node: '>=18'} '@react-native/dev-middleware@0.73.8': @@ -8924,9 +8920,6 @@ packages: resolution: {integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==} engines: {node: '>=4'} - ip@1.1.8: - resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} - ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -11226,8 +11219,8 @@ packages: react: 18.2.0 react-native: ^0.73.0 - react-native@0.73.4: - resolution: {integrity: sha512-VtS+Yr6OOTIuJGDECIYWzNU8QpJjASQYvMtfa/Hvm/2/h5GdB6W9H9TOmh13x07Lj4AOhNMx3XSsz6TdrO4jIg==} + react-native@0.73.9: + resolution: {integrity: sha512-U9Lc6GMdANG2/9uREZe/UTQEzdVWw6NLRYxBYaFWNqiu/qZAmZ0aXSNcVF6hWw/95Ex0IGQx6EVMT4iPmuSNQw==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -13274,7 +13267,7 @@ packages: snapshots: - '@10play/tentap-editor@0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@10play/tentap-editor@0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tiptap/extension-blockquote': 2.3.0(@tiptap/core@2.6.6(@tiptap/pm@2.6.6)) '@tiptap/extension-bold': 2.3.0(@tiptap/core@2.6.6(@tiptap/pm@2.6.6)) @@ -13306,12 +13299,12 @@ snapshots: lodash: 4.17.21 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-webview: 13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native-webview: 13.6.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - '@tiptap/core' - '@10play/tentap-editor@0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@10play/tentap-editor@0.5.11(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(react-native-webview@13.6.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tiptap/extension-blockquote': 2.3.0(@tiptap/core@2.6.6(@tiptap/pm@2.6.6)) '@tiptap/extension-bold': 2.3.0(@tiptap/core@2.6.6(@tiptap/pm@2.6.6)) @@ -13343,8 +13336,8 @@ snapshots: lodash: 4.17.21 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-webview: 13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native-webview: 13.6.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - '@tiptap/core' @@ -15398,7 +15391,7 @@ snapshots: '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.25.2) - '@babel/types': 7.23.9 + '@babel/types': 7.25.2 '@babel/plugin-transform-react-pure-annotations@7.23.3(@babel/core@7.23.7)': dependencies: @@ -15748,7 +15741,7 @@ snapshots: dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.23.5 + '@babel/helper-validator-option': 7.24.8 '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.25.2) '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.7)': @@ -15895,9 +15888,9 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@dev-plugins/async-storage@0.0.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))': + '@dev-plugins/async-storage@0.0.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))': dependencies: - '@react-native-async-storage/async-storage': 1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + '@react-native-async-storage/async-storage': 1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) expo: 50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13) '@dev-plugins/react-navigation@0.0.6(@react-navigation/core@6.4.10(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react@18.2.0)': @@ -16697,11 +16690,11 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@floating-ui/react-native@0.10.6(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@floating-ui/react-native@0.10.6(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@floating-ui/core': 1.6.0 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) '@floating-ui/react@0.26.18(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: @@ -16715,28 +16708,28 @@ snapshots: '@gar/promisify@1.1.3': {} - '@google-cloud/recaptcha-enterprise-react-native@18.4.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@google-cloud/recaptcha-enterprise-react-native@18.4.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@gorhom/bottom-sheet@4.6.0(@types/react-native@0.73.0(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(@types/react@18.2.55)(react-native-gesture-handler@2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@gorhom/bottom-sheet@4.6.0(@types/react-native@0.73.0(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(@types/react@18.2.55)(react-native-gesture-handler@2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: - '@gorhom/portal': 1.0.14(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@gorhom/portal': 1.0.14(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) invariant: 2.2.4 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-gesture-handler: 2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - react-native-reanimated: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native-gesture-handler: 2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-reanimated: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) optionalDependencies: '@types/react': 18.2.55 '@types/react-native': 0.73.0(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@gorhom/portal@1.0.14(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@gorhom/portal@1.0.14(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: nanoid: 3.3.7 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) '@graphql-typed-document-node/core@3.2.0(graphql@15.8.0)': dependencies: @@ -17018,12 +17011,12 @@ snapshots: '@juggle/resize-observer@3.4.0': {} - '@likashefqet/react-native-image-zoom@3.0.0(patch_hash=zkdmh374s554i7qlaqtqn2nhfm)(react-native-gesture-handler@2.16.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@likashefqet/react-native-image-zoom@3.0.0(patch_hash=zkdmh374s554i7qlaqtqn2nhfm)(react-native-gesture-handler@2.16.2(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-gesture-handler: 2.16.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - react-native-reanimated: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native-gesture-handler: 2.16.2(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-reanimated: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@microsoft/applicationinsights-web-snippet@1.1.2': {} @@ -17104,16 +17097,16 @@ snapshots: mkdirp: 1.0.4 rimraf: 3.0.2 - '@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) optional: true - '@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) '@open-draft/until@1.0.3': {} @@ -17792,44 +17785,44 @@ snapshots: '@react-hook/throttle': 2.2.0(react@18.2.0) react: 18.2.0 - '@react-native-async-storage/async-storage@1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': + '@react-native-async-storage/async-storage@1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': dependencies: merge-options: 3.0.4 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@react-native-clipboard/clipboard@1.14.0(react-native-macos@0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-native-clipboard/clipboard@1.14.0(react-native-macos@0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-macos: 0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - react-native-windows: 0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native-macos: 0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-windows: 0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@react-native-clipboard/clipboard@1.14.0(react-native-macos@0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-native-clipboard/clipboard@1.14.0(react-native-macos@0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-windows@0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-macos: 0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - react-native-windows: 0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native-macos: 0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-windows: 0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@react-native-community/cli-clean@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-clean@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 transitivePeerDependencies: - encoding - '@react-native-community/cli-clean@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-clean@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 transitivePeerDependencies: - encoding - '@react-native-community/cli-config@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-config@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 cosmiconfig: 5.2.1 deepmerge: 4.3.1 @@ -17838,9 +17831,9 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-config@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-config@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) chalk: 4.1.2 cosmiconfig: 5.2.1 deepmerge: 4.3.1 @@ -17849,31 +17842,30 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-debugger-ui@12.3.2': + '@react-native-community/cli-debugger-ui@12.3.6': dependencies: serve-static: 1.15.0 transitivePeerDependencies: - supports-color - '@react-native-community/cli-debugger-ui@12.3.6': + '@react-native-community/cli-debugger-ui@12.3.7': dependencies: serve-static: 1.15.0 transitivePeerDependencies: - supports-color - '@react-native-community/cli-doctor@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-doctor@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-config': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-platform-android': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-platform-ios': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) + '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 command-exists: 1.2.9 deepmerge: 4.3.1 envinfo: 7.11.0 execa: 5.1.1 hermes-profile-transformer: 0.0.6 - ip: 1.1.8 node-stream-zip: 1.15.0 ora: 5.4.1 semver: 7.6.0 @@ -17883,12 +17875,12 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-doctor@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-doctor@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-config': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) chalk: 4.1.2 command-exists: 1.2.9 deepmerge: 4.3.1 @@ -17904,28 +17896,27 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-hermes@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-hermes@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-platform-android': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 hermes-profile-transformer: 0.0.6 - ip: 1.1.8 transitivePeerDependencies: - encoding - '@react-native-community/cli-hermes@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-hermes@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) chalk: 4.1.2 hermes-profile-transformer: 0.0.6 transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-android@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-platform-android@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.5 @@ -17934,9 +17925,9 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-android@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-platform-android@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.5 @@ -17945,9 +17936,9 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-ios@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-platform-ios@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.5 @@ -17956,9 +17947,9 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-platform-ios@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-platform-ios@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 fast-xml-parser: 4.3.5 @@ -17967,14 +17958,14 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-plugin-metro@12.3.2': {} - '@react-native-community/cli-plugin-metro@12.3.6': {} - '@react-native-community/cli-server-api@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-plugin-metro@12.3.7': {} + + '@react-native-community/cli-server-api@12.3.6(encoding@0.1.13)': dependencies: - '@react-native-community/cli-debugger-ui': 12.3.2 - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) + '@react-native-community/cli-debugger-ui': 12.3.6 + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) compression: 1.7.4 connect: 3.7.0 errorhandler: 1.5.1 @@ -17988,10 +17979,10 @@ snapshots: - supports-color - utf-8-validate - '@react-native-community/cli-server-api@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-server-api@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-debugger-ui': 12.3.6 - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-debugger-ui': 12.3.7 + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) compression: 1.7.4 connect: 3.7.0 errorhandler: 1.5.1 @@ -18005,7 +17996,7 @@ snapshots: - supports-color - utf-8-validate - '@react-native-community/cli-tools@12.3.2(encoding@0.1.13)': + '@react-native-community/cli-tools@12.3.6(encoding@0.1.13)': dependencies: appdirsjs: 1.2.7 chalk: 4.1.2 @@ -18020,7 +18011,7 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-tools@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-tools@12.3.7(encoding@0.1.13)': dependencies: appdirsjs: 1.2.7 chalk: 4.1.2 @@ -18035,25 +18026,25 @@ snapshots: transitivePeerDependencies: - encoding - '@react-native-community/cli-types@12.3.2': + '@react-native-community/cli-types@12.3.6': dependencies: joi: 17.12.1 - '@react-native-community/cli-types@12.3.6': + '@react-native-community/cli-types@12.3.7': dependencies: joi: 17.12.1 - '@react-native-community/cli@12.3.2(encoding@0.1.13)': - dependencies: - '@react-native-community/cli-clean': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-config': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-debugger-ui': 12.3.2 - '@react-native-community/cli-doctor': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-hermes': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-plugin-metro': 12.3.2 - '@react-native-community/cli-server-api': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-types': 12.3.2 + '@react-native-community/cli@12.3.6(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-clean': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-debugger-ui': 12.3.6 + '@react-native-community/cli-doctor': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-hermes': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-plugin-metro': 12.3.6 + '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-types': 12.3.6 chalk: 4.1.2 commander: 9.5.0 deepmerge: 4.3.1 @@ -18069,17 +18060,17 @@ snapshots: - supports-color - utf-8-validate - '@react-native-community/cli@12.3.6(encoding@0.1.13)': - dependencies: - '@react-native-community/cli-clean': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-config': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-debugger-ui': 12.3.6 - '@react-native-community/cli-doctor': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-hermes': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-plugin-metro': 12.3.6 - '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-types': 12.3.6 + '@react-native-community/cli@12.3.7(encoding@0.1.13)': + dependencies: + '@react-native-community/cli-clean': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-config': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-debugger-ui': 12.3.7 + '@react-native-community/cli-doctor': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-hermes': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-plugin-metro': 12.3.7 + '@react-native-community/cli-server-api': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-types': 12.3.7 chalk: 4.1.2 commander: 9.5.0 deepmerge: 4.3.1 @@ -18095,57 +18086,57 @@ snapshots: - supports-color - utf-8-validate - '@react-native-community/hooks@2.8.1(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-native-community/hooks@2.8.1(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@react-native-community/netinfo@11.1.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': + '@react-native-community/netinfo@11.1.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': dependencies: - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: opencollective-postinstall: 2.0.3 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) superstruct: 0.6.2 optionalDependencies: expo: 50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13) - '@react-native-firebase/crashlytics@19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))': + '@react-native-firebase/crashlytics@19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))': dependencies: - '@react-native-firebase/app': 19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-native-firebase/app': 19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) stacktrace-js: 2.0.2 optionalDependencies: expo: 50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13) - '@react-native-firebase/perf@19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))': + '@react-native-firebase/perf@19.2.2(@react-native-firebase/app@19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))': dependencies: - '@react-native-firebase/app': 19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-native-firebase/app': 19.2.2(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) optionalDependencies: expo: 50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13) - '@react-native-mac/virtualized-lists@0.73.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': + '@react-native-mac/virtualized-lists@0.73.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - '@react-native-mac/virtualized-lists@0.73.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': + '@react-native-mac/virtualized-lists@0.73.3(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@react-native-picker/picker@2.6.1(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-native-picker/picker@2.6.1(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@react-native-windows/cli@0.73.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': + '@react-native-windows/cli@0.73.2(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': dependencies: - '@react-native-windows/codegen': 0.73.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) + '@react-native-windows/codegen': 0.73.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) '@react-native-windows/fs': 0.73.0 '@react-native-windows/package-utils': 0.73.0 '@react-native-windows/telemetry': 0.73.1 @@ -18159,7 +18150,7 @@ snapshots: mustache: 4.2.0 ora: 3.4.0 prompts: 2.4.2 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) semver: 7.6.0 shelljs: 0.8.5 username: 5.1.0 @@ -18171,9 +18162,9 @@ snapshots: - applicationinsights-native-metrics - supports-color - '@react-native-windows/cli@0.73.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': + '@react-native-windows/cli@0.73.2(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': dependencies: - '@react-native-windows/codegen': 0.73.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + '@react-native-windows/codegen': 0.73.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) '@react-native-windows/fs': 0.73.0 '@react-native-windows/package-utils': 0.73.0 '@react-native-windows/telemetry': 0.73.1 @@ -18187,7 +18178,7 @@ snapshots: mustache: 4.2.0 ora: 3.4.0 prompts: 2.4.2 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) semver: 7.6.0 shelljs: 0.8.5 username: 5.1.0 @@ -18199,23 +18190,23 @@ snapshots: - applicationinsights-native-metrics - supports-color - '@react-native-windows/codegen@0.73.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': + '@react-native-windows/codegen@0.73.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': dependencies: '@react-native-windows/fs': 0.73.0 chalk: 4.1.2 globby: 11.1.0 mustache: 4.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) source-map-support: 0.5.21 yargs: 16.2.0 - '@react-native-windows/codegen@0.73.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': + '@react-native-windows/codegen@0.73.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': dependencies: '@react-native-windows/fs': 0.73.0 chalk: 4.1.2 globby: 11.1.0 mustache: 4.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) source-map-support: 0.5.21 yargs: 16.2.0 @@ -18387,11 +18378,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.73.16(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.73.17(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)': dependencies: - '@react-native-community/cli-server-api': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) - '@react-native/dev-middleware': 0.73.7(encoding@0.1.13) + '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native/dev-middleware': 0.73.8(encoding@0.1.13) '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7)) chalk: 4.1.2 execa: 5.1.1 @@ -18408,11 +18399,11 @@ snapshots: - supports-color - utf-8-validate - '@react-native/community-cli-plugin@0.73.16(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.73.17(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)': dependencies: - '@react-native-community/cli-server-api': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.2(encoding@0.1.13) - '@react-native/dev-middleware': 0.73.7(encoding@0.1.13) + '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native/dev-middleware': 0.73.8(encoding@0.1.13) '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)) chalk: 4.1.2 execa: 5.1.1 @@ -18429,10 +18420,10 @@ snapshots: - supports-color - utf-8-validate - '@react-native/community-cli-plugin@0.73.17(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.73.18(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)': dependencies: - '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-server-api': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) '@react-native/dev-middleware': 0.73.8(encoding@0.1.13) '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7)) chalk: 4.1.2 @@ -18450,10 +18441,10 @@ snapshots: - supports-color - utf-8-validate - '@react-native/community-cli-plugin@0.73.17(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.73.18(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)': dependencies: - '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-server-api': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) '@react-native/dev-middleware': 0.73.8(encoding@0.1.13) '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)) chalk: 4.1.2 @@ -18473,22 +18464,6 @@ snapshots: '@react-native/debugger-frontend@0.73.3': {} - '@react-native/dev-middleware@0.73.7(encoding@0.1.13)': - dependencies: - '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.73.3 - chrome-launcher: 0.15.2 - chromium-edge-launcher: 1.0.0 - connect: 3.7.0 - debug: 2.6.9 - node-fetch: 2.6.12(encoding@0.1.13) - open: 7.4.2 - serve-static: 1.15.0 - temp-dir: 2.0.0 - transitivePeerDependencies: - - encoding - - supports-color - '@react-native/dev-middleware@0.73.8(encoding@0.1.13)': dependencies: '@isaacs/ttlcache': 1.4.1 @@ -18552,27 +18527,27 @@ snapshots: '@react-native/normalize-colors@0.74.84': {} - '@react-native/virtualized-lists@0.73.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': + '@react-native/virtualized-lists@0.73.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - '@react-native/virtualized-lists@0.73.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': + '@react-native/virtualized-lists@0.73.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - '@react-navigation/bottom-tabs@6.5.12(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-navigation/bottom-tabs@6.5.12(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: - '@react-navigation/elements': 1.3.22(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@react-navigation/native': 6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-navigation/elements': 1.3.22(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-navigation/native': 6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) color: 4.2.3 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-safe-area-context: 4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - react-native-screens: 3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native-safe-area-context: 4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-screens: 3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) warn-once: 0.1.1 '@react-navigation/core@6.4.10(react@18.2.0)': @@ -18592,31 +18567,31 @@ snapshots: react: 18.2.0 stacktrace-parser: 0.1.10 - '@react-navigation/elements@1.3.22(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-navigation/elements@1.3.22(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: - '@react-navigation/native': 6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-navigation/native': 6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-safe-area-context: 4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native-safe-area-context: 4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@react-navigation/native-stack@6.9.18(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-navigation/native-stack@6.9.18(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-screens@3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: - '@react-navigation/elements': 1.3.22(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@react-navigation/native': 6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-navigation/elements': 1.3.22(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-navigation/native': 6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-safe-area-context: 4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - react-native-screens: 3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native-safe-area-context: 4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-screens: 3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) warn-once: 0.1.1 - '@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@react-navigation/core': 6.4.10(react@18.2.0) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 nanoid: 3.3.7 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) '@react-navigation/routers@6.1.9': dependencies: @@ -18921,7 +18896,7 @@ snapshots: '@tamagui/core': 1.101.3 '@tamagui/helpers': 1.101.3 - '@tamagui/alert-dialog@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/alert-dialog@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/animate-presence': 1.101.3 '@tamagui/aria-hidden': 1.101.3 @@ -18929,12 +18904,12 @@ snapshots: '@tamagui/constants': 1.101.3 '@tamagui/core': 1.101.3 '@tamagui/create-context': 1.101.3 - '@tamagui/dialog': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/dialog': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/dismissable': 1.101.3 '@tamagui/focus-scope': 1.101.3 '@tamagui/helpers': 1.101.3 '@tamagui/polyfill-dev': 1.101.3 - '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/portal': 1.101.3(react@18.2.0) '@tamagui/remove-scroll': 1.101.3(@types/react@18.2.55)(react@18.2.0) '@tamagui/stacks': 1.101.3 @@ -18965,11 +18940,11 @@ snapshots: '@tamagui/use-presence': 1.101.3 '@tamagui/web': 1.101.3 - '@tamagui/animations-moti@1.101.3(moti@0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0))': + '@tamagui/animations-moti@1.101.3(moti@0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0))': dependencies: '@tamagui/use-presence': 1.101.3 '@tamagui/web': 1.101.3 - moti: 0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) + moti: 0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0) '@tamagui/animations-react-native@1.101.3': dependencies: @@ -19046,23 +19021,23 @@ snapshots: '@tamagui/stacks': 1.101.3 '@tamagui/web': 1.101.3 - '@tamagui/checkbox-headless@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/checkbox-headless@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 '@tamagui/create-context': 1.101.3 '@tamagui/focusable': 1.101.3 '@tamagui/helpers': 1.101.3 - '@tamagui/label': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/label': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/use-controllable-state': 1.101.3 '@tamagui/use-previous': 1.101.3 transitivePeerDependencies: - react - react-native - '@tamagui/checkbox@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/checkbox@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: - '@tamagui/checkbox-headless': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/checkbox-headless': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 '@tamagui/core': 1.101.3 @@ -19072,7 +19047,7 @@ snapshots: '@tamagui/get-token': 1.101.3(react@18.2.0) '@tamagui/helpers': 1.101.3 '@tamagui/helpers-tamagui': 1.101.3(react@18.2.0) - '@tamagui/label': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/label': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/stacks': 1.101.3 '@tamagui/use-controllable-state': 1.101.3 '@tamagui/use-previous': 1.101.3 @@ -19128,7 +19103,7 @@ snapshots: '@tamagui/cubic-bezier-animator@1.101.3': {} - '@tamagui/dialog@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/dialog@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/adapt': 1.101.3 '@tamagui/animate-presence': 1.101.3 @@ -19141,7 +19116,7 @@ snapshots: '@tamagui/focus-scope': 1.101.3 '@tamagui/helpers': 1.101.3 '@tamagui/polyfill-dev': 1.101.3 - '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/portal': 1.101.3(react@18.2.0) '@tamagui/remove-scroll': 1.101.3(@types/react@18.2.55)(react@18.2.0) '@tamagui/sheet': 1.101.3(@types/react@18.2.55)(react@18.2.0) @@ -19169,10 +19144,10 @@ snapshots: '@tamagui/fake-react-native@1.101.3': {} - '@tamagui/floating@1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/floating@1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@floating-ui/react-dom': 2.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@floating-ui/react-native': 0.10.6(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@floating-ui/react-native': 0.10.6(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react: 18.2.0 transitivePeerDependencies: - react-dom @@ -19266,7 +19241,7 @@ snapshots: '@tamagui/core': 1.101.3 react: 18.2.0 - '@tamagui/label@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/label@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 @@ -19277,7 +19252,7 @@ snapshots: '@tamagui/text': 1.101.3(react@18.2.0) '@tamagui/web': 1.101.3 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) '@tamagui/linear-gradient@1.101.3': dependencies: @@ -19303,7 +19278,7 @@ snapshots: '@tamagui/polyfill-dev@1.101.3': {} - '@tamagui/popover@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/popover@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@floating-ui/react': 0.26.18(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tamagui/adapt': 1.101.3 @@ -19313,11 +19288,11 @@ snapshots: '@tamagui/constants': 1.101.3 '@tamagui/core': 1.101.3 '@tamagui/dismissable': 1.101.3 - '@tamagui/floating': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/floating': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/focus-scope': 1.101.3 '@tamagui/helpers': 1.101.3 '@tamagui/polyfill-dev': 1.101.3 - '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/portal': 1.101.3(react@18.2.0) '@tamagui/remove-scroll': 1.101.3(@types/react@18.2.55)(react@18.2.0) '@tamagui/scroll-view': 1.101.3 @@ -19331,12 +19306,12 @@ snapshots: - react-dom - react-native - '@tamagui/popper@1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/popper@1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 '@tamagui/core': 1.101.3 - '@tamagui/floating': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/floating': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/get-token': 1.101.3(react@18.2.0) '@tamagui/stacks': 1.101.3 '@tamagui/use-controllable-state': 1.101.3 @@ -19366,7 +19341,7 @@ snapshots: '@tamagui/proxy-worm@1.101.3': {} - '@tamagui/radio-group@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/radio-group@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 @@ -19375,8 +19350,8 @@ snapshots: '@tamagui/focusable': 1.101.3 '@tamagui/get-token': 1.101.3(react@18.2.0) '@tamagui/helpers': 1.101.3 - '@tamagui/label': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@tamagui/radio-headless': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/label': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/radio-headless': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/roving-focus': 1.101.3 '@tamagui/stacks': 1.101.3 '@tamagui/use-controllable-state': 1.101.3 @@ -19385,24 +19360,24 @@ snapshots: - react - react-native - '@tamagui/radio-headless@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/radio-headless@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 '@tamagui/create-context': 1.101.3 '@tamagui/focusable': 1.101.3 '@tamagui/helpers': 1.101.3 - '@tamagui/label': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/label': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/use-controllable-state': 1.101.3 '@tamagui/use-previous': 1.101.3 transitivePeerDependencies: - react - react-native - '@tamagui/react-native-media-driver@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': + '@tamagui/react-native-media-driver@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))': dependencies: '@tamagui/web': 1.101.3 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) '@tamagui/react-native-svg@1.101.3': {} @@ -19434,11 +19409,11 @@ snapshots: '@tamagui/stacks': 1.101.3 '@tamagui/web': 1.101.3 - '@tamagui/select@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/select@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@floating-ui/react': 0.26.18(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@floating-ui/react-dom': 2.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@floating-ui/react-native': 0.10.6(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@floating-ui/react-native': 0.10.6(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/adapt': 1.101.3 '@tamagui/animate-presence': 1.101.3 '@tamagui/compose-refs': 1.101.3 @@ -19557,18 +19532,18 @@ snapshots: - react - supports-color - '@tamagui/switch-headless@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/switch-headless@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 '@tamagui/helpers': 1.101.3 - '@tamagui/label': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/label': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/use-previous': 1.101.3 react: 18.2.0 transitivePeerDependencies: - react-native - '@tamagui/switch@1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/switch@1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 @@ -19576,9 +19551,9 @@ snapshots: '@tamagui/focusable': 1.101.3 '@tamagui/get-token': 1.101.3(react@18.2.0) '@tamagui/helpers': 1.101.3 - '@tamagui/label': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/label': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/stacks': 1.101.3 - '@tamagui/switch-headless': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/switch-headless': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/use-controllable-state': 1.101.3 '@tamagui/use-previous': 1.101.3 react: 18.2.0 @@ -19643,18 +19618,18 @@ snapshots: - immer - react - '@tamagui/tooltip@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tamagui/tooltip@1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@floating-ui/react': 0.26.18(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tamagui/compose-refs': 1.101.3 '@tamagui/core': 1.101.3 '@tamagui/create-context': 1.101.3 - '@tamagui/floating': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/floating': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/get-token': 1.101.3(react@18.2.0) '@tamagui/helpers': 1.101.3 '@tamagui/polyfill-dev': 1.101.3 - '@tamagui/popover': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/popover': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/stacks': 1.101.3 '@tamagui/text': 1.101.3(react@18.2.0) '@tamagui/use-controllable-state': 1.101.3 @@ -19751,28 +19726,28 @@ snapshots: dependencies: '@tanstack/query-core': 4.27.0 - '@tanstack/react-query-devtools@4.29.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@tanstack/react-query-devtools@4.29.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@tanstack/match-sorter-utils': 8.8.4 - '@tanstack/react-query': 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tanstack/react-query': 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) superjson: 1.12.2 use-sync-external-store: 1.2.0(react@18.2.0) - '@tanstack/react-query-persist-client@4.28.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))': + '@tanstack/react-query-persist-client@4.28.0(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))': dependencies: '@tanstack/query-persist-client-core': 4.27.0 - '@tanstack/react-query': 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tanstack/react-query': 4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': + '@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: '@tanstack/query-core': 4.36.1 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) optionalDependencies: react-dom: 18.2.0(react@18.2.0) - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) '@tanstack/react-query@5.32.1(react@18.2.0)': dependencies: @@ -19809,12 +19784,12 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react-native@12.5.3(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react-test-renderer@18.2.0(react@18.2.0))(react@18.2.0)': + '@testing-library/react-native@12.5.3(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react-test-renderer@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: jest-matcher-utils: 29.7.0 pretty-format: 29.7.0 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) react-test-renderer: 18.2.0(react@18.2.0) redent: 3.0.0 optionalDependencies: @@ -20306,7 +20281,7 @@ snapshots: '@types/react-native@0.73.0(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)': dependencies: - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) transitivePeerDependencies: - '@babel/core' - '@babel/preset-env' @@ -22291,9 +22266,9 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0): + drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0): optionalDependencies: - '@op-engineering/op-sqlite': 5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@op-engineering/op-sqlite': 5.0.5(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@opentelemetry/api': 1.8.0 '@types/better-sqlite3': 7.6.9 '@types/react': 18.2.55 @@ -22301,9 +22276,9 @@ snapshots: react: 18.2.0 optional: true - drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0): + drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0): optionalDependencies: - '@op-engineering/op-sqlite': 5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@op-engineering/op-sqlite': 5.0.5(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@opentelemetry/api': 1.8.0 '@types/better-sqlite3': 7.6.9 '@types/react': 18.2.55 @@ -24062,8 +24037,6 @@ snapshots: ip-regex@2.1.0: {} - ip@1.1.8: {} - ipaddr.js@1.9.1: {} is-alphabetical@2.0.1: {} @@ -25575,10 +25548,10 @@ snapshots: moment@2.29.4: {} - moti@0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0): + moti@0.28.1(react-dom@18.2.0(react@18.2.0))(react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0): dependencies: framer-motion: 6.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react-native-reanimated: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-reanimated: 3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - react - react-dom @@ -26151,15 +26124,15 @@ snapshots: dependencies: fflate: 0.4.8 - ? posthog-react-native@2.11.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(@react-navigation/native@6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo-application@5.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-device@5.9.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-file-system@16.0.9(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-localization@14.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(react-native-device-info@10.12.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))) + ? posthog-react-native@2.11.3(@react-native-async-storage/async-storage@1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(@react-navigation/native@6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(expo-application@5.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-device@5.9.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-file-system@16.0.9(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(expo-localization@14.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)))(react-native-device-info@10.12.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))) : optionalDependencies: - '@react-native-async-storage/async-storage': 1.21.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) - '@react-navigation/native': 6.1.10(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-native-async-storage/async-storage': 1.21.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + '@react-navigation/native': 6.1.10(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) expo-application: 5.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) expo-device: 5.9.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) expo-file-system: 16.0.9(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) expo-localization: 14.8.3(expo@50.0.6(@babel/core@7.25.2)(@react-native/babel-preset@0.73.21(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2)))(encoding@0.1.13)) - react-native-device-info: 10.12.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + react-native-device-info: 10.12.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) prebuild-install@7.1.1: dependencies: @@ -26459,7 +26432,7 @@ snapshots: minimist: 1.2.6 strip-json-comments: 2.0.1 - react-beautiful-dnd@13.1.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-beautiful-dnd@13.1.1(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.23.8 css-box-model: 1.2.1 @@ -26467,7 +26440,7 @@ snapshots: raf-schd: 4.0.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-redux: 7.2.8(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-redux: 7.2.8(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) redux: 4.2.0 use-memo-one: 1.1.3(react@18.2.0) transitivePeerDependencies: @@ -26632,34 +26605,34 @@ snapshots: react-is@18.2.0: {} - react-native-branch@5.9.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): + react-native-branch@5.9.2(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): dependencies: - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-context-menu-view@1.15.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-context-menu-view@1.15.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-context-menu-view@1.15.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-context-menu-view@1.15.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-country-codes-picker@2.3.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-country-codes-picker@2.3.5(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-device-info@10.12.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): + react-native-device-info@10.12.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): dependencies: - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) react-native-fetch-api@3.0.0: dependencies: p-defer: 3.0.0 - react-native-gesture-handler@2.16.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-gesture-handler@2.16.2(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@egjs/hammerjs': 2.0.17 hoist-non-react-statics: 3.3.2 @@ -26667,29 +26640,29 @@ snapshots: lodash: 4.17.21 prop-types: 15.8.1 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-gesture-handler@2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-gesture-handler@2.18.1(patch_hash=3cmtvqiauuehrkeqz6qjuqgv4a)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@egjs/hammerjs': 2.0.17 hoist-non-react-statics: 3.3.2 invariant: 2.2.4 prop-types: 15.8.1 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-get-random-values@1.11.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): + react-native-get-random-values@1.11.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): dependencies: fast-base64-decode: 1.0.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-macos@0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-macos@0.73.24(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native-community/cli': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) - '@react-native-mac/virtualized-lists': 0.73.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) + '@react-native-mac/virtualized-lists': 0.73.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.7(@babel/core@7.23.7)) '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13) @@ -26733,13 +26706,13 @@ snapshots: - supports-color - utf-8-validate - react-native-macos@0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-macos@0.73.24(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native-community/cli': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) - '@react-native-mac/virtualized-lists': 0.73.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + '@react-native-mac/virtualized-lists': 0.73.3(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.7(@babel/core@7.25.2)) '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13) @@ -26783,24 +26756,24 @@ snapshots: - supports-color - utf-8-validate - react-native-phone-input@1.3.7(@react-native-picker/picker@2.6.1(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): + react-native-phone-input@1.3.7(@react-native-picker/picker@2.6.1(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): dependencies: - '@react-native-picker/picker': 2.6.1(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-native-picker/picker': 2.6.1(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) google-libphonenumber: 3.2.34 lodash: 4.17.21 prop-types: 15.8.1 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-polyfill-globals@3.1.0(base-64@1.0.0)(react-native-fetch-api@3.0.0)(react-native-get-random-values@1.11.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(react-native-url-polyfill@2.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(text-encoding@0.7.0)(web-streams-polyfill@3.3.3): + react-native-polyfill-globals@3.1.0(base-64@1.0.0)(react-native-fetch-api@3.0.0)(react-native-get-random-values@1.11.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(react-native-url-polyfill@2.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)))(text-encoding@0.7.0)(web-streams-polyfill@3.3.3): dependencies: base-64: 1.0.0 react-native-fetch-api: 3.0.0 - react-native-get-random-values: 1.11.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) - react-native-url-polyfill: 2.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + react-native-get-random-values: 1.11.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + react-native-url-polyfill: 2.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) text-encoding: 0.7.0 web-streams-polyfill: 3.3.3 - react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.23.7)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@babel/core': 7.23.7 '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.7) @@ -26812,11 +26785,11 @@ snapshots: convert-source-map: 2.0.0 invariant: 2.2.4 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) transitivePeerDependencies: - supports-color - react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-reanimated@3.8.1(patch_hash=n2blcrn244i77n7euhqt5mi2km)(@babel/core@7.25.2)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@babel/core': 7.25.2 '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.25.2) @@ -26828,25 +26801,25 @@ snapshots: convert-source-map: 2.0.0 invariant: 2.2.4 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) transitivePeerDependencies: - supports-color - react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-safe-area-context@4.9.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-safe-area-context@4.9.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-screens@3.29.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-screens@3.29.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 react-freeze: 1.0.3(react@18.2.0) - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) warn-once: 0.1.1 react-native-sse@1.2.1: {} @@ -26856,35 +26829,35 @@ snapshots: opencollective: 1.0.3 opencollective-postinstall: 2.0.3 - react-native-svg-transformer@1.3.0(react-native-svg@15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(typescript@5.4.5): + react-native-svg-transformer@1.3.0(react-native-svg@15.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(typescript@5.4.5): dependencies: '@svgr/core': 8.1.0(typescript@5.4.5) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5)) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5))(typescript@5.4.5) path-dirname: 1.0.2 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-svg: 15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native-svg: 15.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - supports-color - typescript - react-native-svg@15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-svg@15.0.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: css-select: 5.1.0 css-tree: 1.1.3 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-svg@15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-svg@15.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: css-select: 5.1.0 css-tree: 1.1.3 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-url-polyfill@2.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): + react-native-url-polyfill@2.0.0(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)): dependencies: - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) whatwg-url-without-unicode: 8.0.0-3 react-native-web-internals@1.101.3: @@ -26921,35 +26894,35 @@ snapshots: transitivePeerDependencies: - encoding - react-native-webview@13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-webview@13.6.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: escape-string-regexp: 2.0.0 invariant: 2.2.4 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) - react-native-webview@13.6.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-webview@13.6.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: escape-string-regexp: 2.0.0 invariant: 2.2.4 react: 18.2.0 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) - react-native-windows@0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-windows@0.73.11(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.23.8 '@jest/create-cache-key-function': 29.7.0 '@react-native-community/cli': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) - '@react-native-windows/cli': 0.73.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) + '@react-native-windows/cli': 0.73.2(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.7(@babel/core@7.23.7)) '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 - '@react-native/virtualized-lists': 0.73.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) + '@react-native/virtualized-lists': 0.73.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -26970,7 +26943,7 @@ snapshots: promise: 8.3.0 react: 18.2.0 react-devtools-core: 4.28.5 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) react-refresh: 0.14.0 react-shallow-renderer: 16.15.0(react@18.2.0) regenerator-runtime: 0.13.11 @@ -26989,21 +26962,21 @@ snapshots: - supports-color - utf-8-validate - react-native-windows@0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-native-windows@0.73.11(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.23.8 '@jest/create-cache-key-function': 29.7.0 '@react-native-community/cli': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-android': 12.3.6(encoding@0.1.13) '@react-native-community/cli-platform-ios': 12.3.6(encoding@0.1.13) - '@react-native-windows/cli': 0.73.2(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + '@react-native-windows/cli': 0.73.2(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.7(@babel/core@7.25.2)) '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 - '@react-native/virtualized-lists': 0.73.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + '@react-native/virtualized-lists': 0.73.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -27024,7 +26997,7 @@ snapshots: promise: 8.3.0 react: 18.2.0 react-devtools-core: 4.28.5 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) react-refresh: 0.14.0 react-shallow-renderer: 16.15.0(react@18.2.0) regenerator-runtime: 0.13.11 @@ -27043,19 +27016,19 @@ snapshots: - supports-color - utf-8-validate - react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0): + react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-platform-android': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-platform-ios': 12.3.2(encoding@0.1.13) + '@react-native-community/cli': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 12.3.7(encoding@0.1.13) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.7(@babel/core@7.23.7)) - '@react-native/community-cli-plugin': 0.73.16(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.73.18(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 - '@react-native/virtualized-lists': 0.73.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) + '@react-native/virtualized-lists': 0.73.4(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -27092,19 +27065,19 @@ snapshots: - supports-color - utf-8-validate - react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0): + react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native-community/cli': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-platform-android': 12.3.2(encoding@0.1.13) - '@react-native-community/cli-platform-ios': 12.3.2(encoding@0.1.13) + '@react-native-community/cli': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-platform-android': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-platform-ios': 12.3.7(encoding@0.1.13) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.7(@babel/core@7.25.2)) - '@react-native/community-cli-plugin': 0.73.16(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.73.18(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 - '@react-native/virtualized-lists': 0.73.4(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) + '@react-native/virtualized-lists': 0.73.4(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0)) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -27147,15 +27120,15 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-qr-code@2.0.12(react-native-svg@15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0): + react-qr-code@2.0.12(react-native-svg@15.0.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(react@18.2.0): dependencies: prop-types: 15.8.1 qr.js: 0.0.0 react: 18.2.0 optionalDependencies: - react-native-svg: 15.0.0(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + react-native-svg: 15.0.0(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - react-redux@7.2.8(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + react-redux@7.2.8(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.23.8 '@types/react-redux': 7.1.24 @@ -27166,7 +27139,7 @@ snapshots: react-is: 17.0.2 optionalDependencies: react-dom: 18.2.0(react@18.2.0) - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0) react-refresh@0.14.0: {} @@ -27851,13 +27824,13 @@ snapshots: sprintf-js@1.0.3: {} - sqlocal@0.11.1(drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0)): + sqlocal@0.11.1(drizzle-orm@0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0)): dependencies: '@sqlite.org/sqlite-wasm': 3.46.0-build2 coincident: 1.2.3 nanoid: 5.0.7 optionalDependencies: - drizzle-orm: 0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0) + drizzle-orm: 0.30.9(patch_hash=cegrec33e6f7d6ltk7vff5r7w4)(@op-engineering/op-sqlite@5.0.5(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0))(@opentelemetry/api@1.8.0)(@types/better-sqlite3@7.6.9)(@types/react@18.2.55)(better-sqlite3@9.4.5)(react@18.2.0) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -28171,16 +28144,16 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - tailwind-rn@4.2.0(patch_hash=huubbq5aurym44djogb6gnyabq)(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.1): + tailwind-rn@4.2.0(patch_hash=huubbq5aurym44djogb6gnyabq)(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.1): dependencies: - '@react-native-community/hooks': 2.8.1(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@react-native-community/hooks': 2.8.1(react-native@0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) chokidar: 3.5.3 color-string: 1.9.1 css: 3.0.0 css-mediaquery: 0.1.2 css-to-react-native: 3.2.0 meow: 7.1.1 - react-native: 0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) + react-native: 0.73.9(@babel/core@7.25.2)(@babel/preset-env@7.23.7(@babel/core@7.25.2))(encoding@0.1.13)(react@18.2.0) tailwindcss: 3.4.1 transitivePeerDependencies: - react @@ -28228,21 +28201,21 @@ snapshots: transitivePeerDependencies: - ts-node - tamagui@1.101.3(@types/react@18.2.55)(immer@9.0.21)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): + tamagui@1.101.3(@types/react@18.2.55)(immer@9.0.21)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0): dependencies: '@tamagui/accordion': 1.101.3 '@tamagui/adapt': 1.101.3 - '@tamagui/alert-dialog': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/alert-dialog': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/animate-presence': 1.101.3 '@tamagui/avatar': 1.101.3(react@18.2.0) '@tamagui/button': 1.101.3(react@18.2.0) '@tamagui/card': 1.101.3 - '@tamagui/checkbox': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/checkbox': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/compose-refs': 1.101.3 '@tamagui/constants': 1.101.3 '@tamagui/core': 1.101.3 '@tamagui/create-context': 1.101.3 - '@tamagui/dialog': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/dialog': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/elements': 1.101.3(react@18.2.0) '@tamagui/fake-react-native': 1.101.3 '@tamagui/focusable': 1.101.3 @@ -28254,29 +28227,29 @@ snapshots: '@tamagui/group': 1.101.3(@types/react@18.2.55)(immer@9.0.21)(react@18.2.0) '@tamagui/helpers-tamagui': 1.101.3(react@18.2.0) '@tamagui/image': 1.101.3(react@18.2.0) - '@tamagui/label': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/label': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/linear-gradient': 1.101.3 '@tamagui/list-item': 1.101.3(react@18.2.0) '@tamagui/polyfill-dev': 1.101.3 - '@tamagui/popover': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/popover': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/popper': 1.101.3(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/portal': 1.101.3(react@18.2.0) '@tamagui/progress': 1.101.3(react@18.2.0) - '@tamagui/radio-group': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) - '@tamagui/react-native-media-driver': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) + '@tamagui/radio-group': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/react-native-media-driver': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0)) '@tamagui/scroll-view': 1.101.3 - '@tamagui/select': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/select': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/separator': 1.101.3 '@tamagui/shapes': 1.101.3 '@tamagui/sheet': 1.101.3(@types/react@18.2.55)(react@18.2.0) '@tamagui/slider': 1.101.3(react@18.2.0) '@tamagui/stacks': 1.101.3 - '@tamagui/switch': 1.101.3(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/switch': 1.101.3(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/tabs': 1.101.3(@types/react@18.2.55)(immer@9.0.21)(react@18.2.0) '@tamagui/text': 1.101.3(react@18.2.0) '@tamagui/theme': 1.101.3 '@tamagui/toggle-group': 1.101.3(@types/react@18.2.55)(immer@9.0.21)(react@18.2.0) - '@tamagui/tooltip': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.4(patch_hash=ouiwprrx2mkoquswbpf4us43yu)(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + '@tamagui/tooltip': 1.101.3(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-native@0.73.9(@babel/core@7.23.7)(@babel/preset-env@7.23.7(@babel/core@7.23.7))(encoding@0.1.13)(react@18.2.0))(react@18.2.0) '@tamagui/use-controllable-state': 1.101.3 '@tamagui/use-debounce': 1.101.3 '@tamagui/use-force-update': 1.101.3 From 1b57cb687d184bced9d9ca2118b181848cc88d9c Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Thu, 12 Sep 2024 17:47:58 -0400 Subject: [PATCH 013/157] add image url list item icon, add placeholder lure meta display --- .../ui/src/components/AppInviteDisplay.tsx | 40 +++++++++++++++++++ packages/ui/src/components/Avatar.tsx | 6 +-- .../ui/src/components/ListItem/ListItem.tsx | 4 ++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 packages/ui/src/components/AppInviteDisplay.tsx diff --git a/packages/ui/src/components/AppInviteDisplay.tsx b/packages/ui/src/components/AppInviteDisplay.tsx new file mode 100644 index 0000000000..c38bbf3b83 --- /dev/null +++ b/packages/ui/src/components/AppInviteDisplay.tsx @@ -0,0 +1,40 @@ +import { DeepLinkMetadata } from '@tloncorp/shared/dist'; +import React from 'react'; + +import { ListItem } from './ListItem'; + +function AppInviteDisplayRaw({ metadata }: { metadata: DeepLinkMetadata }) { + const { + inviterUserId, + invitedGroupId, + inviterNickname, + invitedGroupTitle, + invitedGroupIconImageUrl, + } = metadata; + + if (!inviterUserId || !invitedGroupId) { + return null; + } + + return ( + + {invitedGroupIconImageUrl ? ( + + ) : null} + + + Join {invitedGroupTitle ?? invitedGroupId} + + + Invited by {inviterNickname ?? inviterUserId} + + + + ); +} + +export const AppInviteDisplay = React.memo(AppInviteDisplayRaw); diff --git a/packages/ui/src/components/Avatar.tsx b/packages/ui/src/components/Avatar.tsx index cf30469313..77c50f1b5c 100644 --- a/packages/ui/src/components/Avatar.tsx +++ b/packages/ui/src/components/Avatar.tsx @@ -201,16 +201,14 @@ export const ImageAvatar = function ImageAvatarComponent({ imageUrl?: string; fallback?: React.ReactNode; } & AvatarProps) { - const calmSettings = useCalm(); + // const calmSettings = useCalm(); const [loadFailed, setLoadFailed] = useState(false); const handleLoadError = useCallback(() => { setLoadFailed(true); }, []); - return imageUrl && - (props.ignoreCalm || !calmSettings.disableAvatars) && - !loadFailed ? ( + return imageUrl && (props.ignoreCalm || true) && !loadFailed ? ( Date: Thu, 12 Sep 2024 18:01:42 -0400 Subject: [PATCH 014/157] thread group/user metadata through Branch, add invite display to WelcomeScreen --- .../src/hooks/useDeepLinkListener.ts | 2 +- .../src/screens/Onboarding/WelcomeScreen.tsx | 4 ++ packages/app/contexts/branch.tsx | 25 ++++++- packages/shared/src/logic/branch.ts | 67 +++++++++++++++---- packages/shared/src/store/lure.ts | 42 ++++++++---- packages/ui/src/index.tsx | 1 + 6 files changed, 111 insertions(+), 30 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index b476b6e022..b61606fbaf 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -19,7 +19,7 @@ export const useDeepLinkListener = () => { if (ship && lure) { (async () => { try { - await inviteShipWithLure({ ship, lure }); + await inviteShipWithLure({ ship, lure: lure.id }); Alert.alert( '', 'Your invitation to the group is on its way. It will appear in the Groups list.', diff --git a/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx index 30fe36252c..da95b029ee 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx @@ -2,11 +2,13 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { useIsDarkMode } from '@tloncorp/app/hooks/useIsDarkMode'; import { ActionSheet, + AppInviteDisplay, PrimaryButton, SizableText, View, YStack, } from '@tloncorp/ui'; +import { useLureMetadata } from 'packages/app/contexts/branch'; import { useState } from 'react'; import { ImageBackground, Pressable } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -16,6 +18,7 @@ import type { OnboardingStackParamList } from '../../types'; type Props = NativeStackScreenProps; export const WelcomeScreen = ({ navigation }: Props) => { + const lureMeta = useLureMetadata(); const isDarkMode = useIsDarkMode(); const { bottom } = useSafeAreaInsets(); const [open, setOpen] = useState(false); @@ -35,6 +38,7 @@ export const WelcomeScreen = ({ navigation }: Props) => { }} source={bgSource} > + {lureMeta ? : null} { return context; }; +export const useLureMetadata = () => { + const context = useContext(Context); + + if (!context) { + throw new Error( + 'Must call `useLureMetadata` within a `BranchProvider` component.' + ); + } + + return context.lure ?? null; +}; + export const BranchProvider = ({ children }: { children: ReactNode }) => { const [{ deepLinkPath, lure, priorityToken }, setState] = useState(INITIAL_STATE); @@ -81,7 +99,10 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { // Link had a lure field embedded console.debug('[branch] Detected lure link:', params.lure); const nextLure: Lure = { - lure: params.lure as string, + lure: { + ...extractLureMetadata(params), + id: params.lure as string, + }, priorityToken: params.token as string | undefined, }; setState({ diff --git a/packages/shared/src/logic/branch.ts b/packages/shared/src/logic/branch.ts index 8c8a6d2a6f..734ca3b194 100644 --- a/packages/shared/src/logic/branch.ts +++ b/packages/shared/src/logic/branch.ts @@ -28,11 +28,38 @@ export const getDeepLink = async ( export type DeepLinkType = 'lure' | 'wer'; -interface DeepLinkData { +export interface DeepLinkMetadata { + inviterUserId?: string; + inviterNickname?: string; + inviterAvatarImage?: string; + invitedGroupId?: string; + invitedGroupTitle?: string; + invitedGroupDescription?: string; + invitedGroupIconImageUrl?: string; + invitedGroupiconImageColor?: string; +} +interface DeepLinkData extends DeepLinkMetadata { $desktop_url: string; $canonical_url: string; - lure?: string; wer?: string; + lure?: string; +} + +export function extractLureMetadata(branchParams: any) { + if (!branchParams || typeof branchParams !== 'object') { + return {}; + } + + return { + inviterUserId: branchParams.inviterUserId, + inviterNickname: branchParams.inviterNickname, + inviterAvatarImage: branchParams.inviterAvatarImage, + invitedGroupId: branchParams.invitedGroupId, + invitedGroupTitle: branchParams.invitedGroupTitle, + invitedGroupDescription: branchParams.invitedGroupDescription, + invitedGroupIconImageUrl: branchParams.invitedGroupIconImageUrl, + invitedGroupiconImageColor: branchParams.invitedGroupiconImageColor, + }; } export async function getDmLink( @@ -42,23 +69,31 @@ export async function getDmLink( ): Promise { const dmPath = `dm/${ship}`; const fallbackUrl = `https://tlon.network/lure/~loshut-lonreg/tlon`; // for now, send to generic signup page on desktop - const link = await createDeepLink( + const link = await createDeepLink({ fallbackUrl, - 'wer', - dmPath, + type: 'wer', + path: dmPath, branchDomain, - branchKey - ); + branchKey, + }); return link || ''; } -export const createDeepLink = async ( - fallbackUrl: string | undefined, - type: DeepLinkType, - path: string, - branchDomain: string, - branchKey: string -) => { +export const createDeepLink = async ({ + fallbackUrl, + type, + path, + branchDomain, + branchKey, + metadata, +}: { + fallbackUrl: string | undefined; + type: DeepLinkType; + path: string; + branchDomain: string; + branchKey: string; + metadata?: DeepLinkMetadata; +}) => { if (!fallbackUrl || !path) { return undefined; } @@ -81,7 +116,11 @@ export const createDeepLink = async ( const data: DeepLinkData = { $desktop_url: fallbackUrl, $canonical_url: fallbackUrl, + ...(metadata ?? {}), }; + data['$desktop_url'] = fallbackUrl; + data['$canonical_url'] = fallbackUrl; + if (type === 'lure') { data.lure = path; } else { diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index d353fd1650..f595b7667b 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -3,9 +3,10 @@ import produce from 'immer'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import create from 'zustand'; -import { poke, scry, subscribeOnce } from '../api/urbit'; +import { getCurrentUserId, poke, scry, subscribeOnce } from '../api/urbit'; +import * as db from '../db'; import { createDevLogger } from '../debug'; -import { createDeepLink } from '../logic/branch'; +import { DeepLinkMetadata, createDeepLink } from '../logic/branch'; import { getPreviewTracker } from '../logic/subscriptionTracking'; import { asyncWithDefault, getFlagParts } from '../logic/utils'; import { stringToTa } from '../urbit'; @@ -58,7 +59,7 @@ interface LureState { start: () => Promise; } -const lureLogger = createDevLogger('lure', false); +const lureLogger = createDevLogger('lure', true); function groupsDescribe(meta: GroupMeta) { return { @@ -183,14 +184,29 @@ export const useLureState = create((set, get) => ({ let deepLinkUrl: string | undefined; lureLogger.log('enabled', enabled); - if (enabled && url) { - deepLinkUrl = await createDeepLink( - url, - 'lure', - flag, + if (enabled) { + const currentUserId = getCurrentUserId(); + const group = await db.getGroup({ id: flag }); + const user = await db.getContact({ id: currentUserId }); + const metadata: DeepLinkMetadata = { + inviterUserId: currentUserId, + inviterNickname: user?.nickname ?? undefined, + inviterAvatarImage: user?.avatarImage ?? undefined, + invitedGroupId: flag, + invitedGroupTitle: group?.title ?? undefined, + invitedGroupDescription: group?.description ?? undefined, + invitedGroupIconImageUrl: group?.iconImage ?? undefined, + invitedGroupiconImageColor: group?.iconImageColor ?? undefined, + }; + + deepLinkUrl = await createDeepLink({ + fallbackUrl: 'https://en.wikipedia.org/wiki/Construction', + type: 'lure', + path: flag, branchDomain, - branchKey - ); + branchKey, + metadata, + }); lureLogger.log('deepLinkUrl created', deepLinkUrl); } @@ -269,9 +285,9 @@ export function useLure({ }; } -export function useLureLinkChecked(url: string, enabled: boolean) { +export function useLureLinkChecked(url: string | undefined, enabled: boolean) { const prevData = useRef(false); - const pathEncodedUrl = stringToTa(url); + const pathEncodedUrl = stringToTa(url ?? ''); const { data, ...query } = useQuery({ queryKey: ['lure-check', url], queryFn: async () => @@ -279,7 +295,7 @@ export function useLureLinkChecked(url: string, enabled: boolean) { { app: 'grouper', path: `/check-link/${pathEncodedUrl}` }, 4500 ), - enabled, + enabled: enabled && Boolean(url), refetchInterval: 5000, }); diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx index d95e7e9c8d..fcf1fc1f8f 100644 --- a/packages/ui/src/index.tsx +++ b/packages/ui/src/index.tsx @@ -70,6 +70,7 @@ export * from './components/UrbitSigil'; export * from './components/UserProfileScreenView'; export * from './components/View'; export * from './components/WelcomeSheet'; +export * from './components/AppInviteDisplay'; export * from './contexts'; export * from './tamagui.config'; export * from './types'; From 032588991b056d37a1546a126ca9ca8cbe6ff466 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Thu, 12 Sep 2024 18:39:03 -0400 Subject: [PATCH 015/157] add to other screens, use actual group avatar --- .../screens/Onboarding/SignUpEmailScreen.tsx | 5 ++++ .../Onboarding/SignUpPasswordScreen.tsx | 4 +++ .../src/screens/Onboarding/WelcomeScreen.tsx | 8 +++++- .../ui/src/components/AppInviteDisplay.tsx | 26 +++++++++++++------ packages/ui/src/components/Avatar.tsx | 8 +++++- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx index 8696ecb79f..9febac63bf 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx @@ -7,6 +7,7 @@ import { import { getHostingAvailability } from '@tloncorp/app/lib/hostingApi'; import { trackError, trackOnboardingAction } from '@tloncorp/app/utils/posthog'; import { + AppInviteDisplay, Button, GenericHeader, KeyboardAvoidingView, @@ -17,6 +18,7 @@ import { YStack, } from '@tloncorp/ui'; import { Field } from '@tloncorp/ui'; +import { useLureMetadata } from 'packages/app/contexts/branch'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { Controller } from 'react-hook-form'; @@ -40,6 +42,8 @@ export const SignUpEmailScreen = ({ }: Props) => { const [isSubmitting, setIsSubmitting] = useState(false); + const lureMeta = useLureMetadata(); + const { control, handleSubmit, @@ -106,6 +110,7 @@ export const SignUpEmailScreen = ({ /> + {lureMeta ? : null} Enter your email address. You’ll use it to log in to Tlon and we’ll email you the occasional service update. diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx index 604029dadc..8508aee364 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx @@ -12,6 +12,7 @@ import { import { isEulaAgreed, setEulaAgreed } from '@tloncorp/app/utils/eula'; import { trackError, trackOnboardingAction } from '@tloncorp/app/utils/posthog'; import { + AppInviteDisplay, Button, CheckboxInput, Field, @@ -25,6 +26,7 @@ import { View, YStack, } from '@tloncorp/ui'; +import { useLureMetadata } from 'packages/app/contexts/branch'; import { useEffect, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; @@ -45,6 +47,7 @@ export const SignUpPasswordScreen = ({ }, }: Props) => { const [isSubmitting, setIsSubmitting] = useState(false); + const lureMeta = useLureMetadata(); const { control, setFocus, @@ -187,6 +190,7 @@ export const SignUpPasswordScreen = ({ /> + {lureMeta ? : null} Please set a strong password with at least 8 characters. diff --git a/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx index da95b029ee..dd76b62196 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx @@ -38,7 +38,13 @@ export const WelcomeScreen = ({ navigation }: Props) => { }} source={bgSource} > - {lureMeta ? : null} + {lureMeta ? ( + + ) : null} ) { const { inviterUserId, invitedGroupId, inviterNickname, invitedGroupTitle, invitedGroupIconImageUrl, + invitedGroupiconImageColor, } = metadata; if (!inviterUserId || !invitedGroupId) { return null; } + const groupShim = { + id: invitedGroupId, + title: invitedGroupTitle, + iconImage: invitedGroupIconImageUrl, + iconImageColor: invitedGroupiconImageColor, + }; + return ( - + {invitedGroupIconImageUrl ? ( - + ) : null} diff --git a/packages/ui/src/components/Avatar.tsx b/packages/ui/src/components/Avatar.tsx index 77c50f1b5c..b50bd72e72 100644 --- a/packages/ui/src/components/Avatar.tsx +++ b/packages/ui/src/components/Avatar.tsx @@ -95,10 +95,16 @@ export const ContactAvatar = React.memo(function ContactAvatComponent({ ); }); +export interface GroupImageShim { + id: string; + title?: string; + iconImage?: string; + iconImageColor?: string; +} export const GroupAvatar = React.memo(function GroupAvatarComponent({ model, ...props -}: { model: db.Group } & AvatarProps) { +}: { model: GroupImageShim } & AvatarProps) { const fallback = ( Date: Thu, 12 Sep 2024 18:53:00 -0400 Subject: [PATCH 016/157] fix calm settings usage and layer in app data provider --- .../ui/src/components/AppInviteDisplay.tsx | 36 ++++++++++--------- packages/ui/src/components/Avatar.tsx | 6 ++-- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/packages/ui/src/components/AppInviteDisplay.tsx b/packages/ui/src/components/AppInviteDisplay.tsx index a5ae9c766d..e962f8f1eb 100644 --- a/packages/ui/src/components/AppInviteDisplay.tsx +++ b/packages/ui/src/components/AppInviteDisplay.tsx @@ -1,6 +1,7 @@ import { DeepLinkMetadata } from '@tloncorp/shared/dist'; import React, { ComponentProps } from 'react'; +import { AppDataContextProvider } from '../contexts'; import { ListItem } from './ListItem'; function AppInviteDisplayRaw({ @@ -28,22 +29,25 @@ function AppInviteDisplayRaw({ }; return ( - - {invitedGroupIconImageUrl ? ( - - ) : null} - - - Join {invitedGroupTitle ?? invitedGroupId} - - - Invited by {inviterNickname ?? inviterUserId} - - - + // provider needed to support calm settings usage down the tree + + + {invitedGroupIconImageUrl ? ( + + ) : null} + + + Join {invitedGroupTitle ?? invitedGroupId} + + + Invited by {inviterNickname ?? inviterUserId} + + + + ); } diff --git a/packages/ui/src/components/Avatar.tsx b/packages/ui/src/components/Avatar.tsx index b50bd72e72..7fcb17a338 100644 --- a/packages/ui/src/components/Avatar.tsx +++ b/packages/ui/src/components/Avatar.tsx @@ -207,14 +207,16 @@ export const ImageAvatar = function ImageAvatarComponent({ imageUrl?: string; fallback?: React.ReactNode; } & AvatarProps) { - // const calmSettings = useCalm(); + const calmSettings = useCalm(); const [loadFailed, setLoadFailed] = useState(false); const handleLoadError = useCallback(() => { setLoadFailed(true); }, []); - return imageUrl && (props.ignoreCalm || true) && !loadFailed ? ( + return imageUrl && + (props.ignoreCalm || !calmSettings.disableAvatars) && + !loadFailed ? ( Date: Thu, 12 Sep 2024 18:55:40 -0400 Subject: [PATCH 017/157] fix types --- packages/ui/src/components/Avatar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/Avatar.tsx b/packages/ui/src/components/Avatar.tsx index 7fcb17a338..79770b1bbe 100644 --- a/packages/ui/src/components/Avatar.tsx +++ b/packages/ui/src/components/Avatar.tsx @@ -104,7 +104,7 @@ export interface GroupImageShim { export const GroupAvatar = React.memo(function GroupAvatarComponent({ model, ...props -}: { model: GroupImageShim } & AvatarProps) { +}: { model: db.Group | GroupImageShim } & AvatarProps) { const fallback = ( Date: Thu, 12 Sep 2024 18:58:19 -0400 Subject: [PATCH 018/157] fix another lure ref --- apps/tlon-mobile/src/App.main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tlon-mobile/src/App.main.tsx b/apps/tlon-mobile/src/App.main.tsx index d814ffb80f..0663bcc732 100644 --- a/apps/tlon-mobile/src/App.main.tsx +++ b/apps/tlon-mobile/src/App.main.tsx @@ -107,7 +107,7 @@ const App = ({ Date: Thu, 12 Sep 2024 19:00:50 -0400 Subject: [PATCH 019/157] import paths --- apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx | 2 +- .../tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx | 2 +- apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx | 2 +- packages/app/contexts/branch.tsx | 2 +- packages/app/lib/nativeDb.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx index 9febac63bf..9b2763ed5b 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx @@ -4,6 +4,7 @@ import { DEFAULT_PRIORITY_TOKEN, EMAIL_REGEX, } from '@tloncorp/app/constants'; +import { useLureMetadata } from '@tloncorp/app/contexts/branch'; import { getHostingAvailability } from '@tloncorp/app/lib/hostingApi'; import { trackError, trackOnboardingAction } from '@tloncorp/app/utils/posthog'; import { @@ -18,7 +19,6 @@ import { YStack, } from '@tloncorp/ui'; import { Field } from '@tloncorp/ui'; -import { useLureMetadata } from 'packages/app/contexts/branch'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { Controller } from 'react-hook-form'; diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx index 8508aee364..83795fdbc7 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx @@ -5,6 +5,7 @@ import { } from '@google-cloud/recaptcha-enterprise-react-native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { RECAPTCHA_SITE_KEY } from '@tloncorp/app/constants'; +import { useLureMetadata } from '@tloncorp/app/contexts/branch'; import { logInHostingUser, signUpHostingUser, @@ -26,7 +27,6 @@ import { View, YStack, } from '@tloncorp/ui'; -import { useLureMetadata } from 'packages/app/contexts/branch'; import { useEffect, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; diff --git a/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx index dd76b62196..bd1d885778 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/WelcomeScreen.tsx @@ -1,4 +1,5 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { useLureMetadata } from '@tloncorp/app/contexts/branch'; import { useIsDarkMode } from '@tloncorp/app/hooks/useIsDarkMode'; import { ActionSheet, @@ -8,7 +9,6 @@ import { View, YStack, } from '@tloncorp/ui'; -import { useLureMetadata } from 'packages/app/contexts/branch'; import { useState } from 'react'; import { ImageBackground, Pressable } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; diff --git a/packages/app/contexts/branch.tsx b/packages/app/contexts/branch.tsx index 6b02652919..48f6347a81 100644 --- a/packages/app/contexts/branch.tsx +++ b/packages/app/contexts/branch.tsx @@ -1,5 +1,5 @@ import { DeepLinkMetadata } from '@tloncorp/shared/dist'; -import { extractLureMetadata } from 'packages/shared/src/logic'; +import { extractLureMetadata } from '@tloncorp/shared/src/logic'; import { type ReactNode, createContext, diff --git a/packages/app/lib/nativeDb.ts b/packages/app/lib/nativeDb.ts index 26333a2155..659f3e4512 100644 --- a/packages/app/lib/nativeDb.ts +++ b/packages/app/lib/nativeDb.ts @@ -1,7 +1,7 @@ import { open } from '@op-engineering/op-sqlite'; import { createDevLogger, escapeLog } from '@tloncorp/shared'; import { handleChange, schema, setClient } from '@tloncorp/shared/dist/db'; -import { AnySqliteDatabase } from 'packages/shared/dist/db/client'; +import { AnySqliteDatabase } from '@tloncorp/shared/dist/db/client'; import { useEffect, useMemo, useState } from 'react'; import { OPSQLite$SQLiteConnection } from './opsqliteConnection'; From 761ee7540defd01490b01ac5f0cbb46d33cd3f0e Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Fri, 13 Sep 2024 07:21:27 -0500 Subject: [PATCH 020/157] input: fix input height and scrolling issues when input is 'full' --- packages/editor/src/MessageInputEditor.tsx | 2 +- .../ui/src/components/BigInput.native.tsx | 16 ++++-------- .../components/MessageInput/index.native.tsx | 26 ++++++++++++++++--- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/editor/src/MessageInputEditor.tsx b/packages/editor/src/MessageInputEditor.tsx index 506e14704c..45dd305ecc 100644 --- a/packages/editor/src/MessageInputEditor.tsx +++ b/packages/editor/src/MessageInputEditor.tsx @@ -76,7 +76,7 @@ export const MessageInputEditor = () => { overflow: 'auto', height: 'auto', fontSize: 16, - overflowY: 'hidden', + overflowY: 'auto', color: useIsDark() ? 'white' : 'black', fontFamily: "System, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', sans-serif", diff --git a/packages/ui/src/components/BigInput.native.tsx b/packages/ui/src/components/BigInput.native.tsx index 29fd39a1db..8fcf8b12ca 100644 --- a/packages/ui/src/components/BigInput.native.tsx +++ b/packages/ui/src/components/BigInput.native.tsx @@ -127,16 +127,10 @@ export function BigInput({ )} - - + {channelType === 'notebook' && editorRef.current && editorRef.current.editor && ( diff --git a/packages/ui/src/components/MessageInput/index.native.tsx b/packages/ui/src/components/MessageInput/index.native.tsx index 9be3329619..3901e48edc 100644 --- a/packages/ui/src/components/MessageInput/index.native.tsx +++ b/packages/ui/src/components/MessageInput/index.native.tsx @@ -159,10 +159,18 @@ export const MessageInput = forwardRef( const titleInputHeight = 48; const inputBasePadding = getToken('$s', 'space'); const imageInputButtonHeight = 50; - const basicOffset = - top + headerHeight + titleInputHeight + imageInputButtonHeight; - const bigInputHeightBasic = - height - basicOffset - bottom - inputBasePadding * 2; + const maxInputHeight = useMemo( + () => height - headerHeight - bottom - top, + [height, bottom, top, headerHeight] + ); + const basicOffset = useMemo( + () => top + headerHeight + titleInputHeight + imageInputButtonHeight, + [top, headerHeight, titleInputHeight, imageInputButtonHeight] + ); + const bigInputHeightBasic = useMemo( + () => height - basicOffset - bottom - inputBasePadding * 2, + [height, basicOffset, bottom, inputBasePadding] + ); const [bigInputHeight, setBigInputHeight] = useState(bigInputHeightBasic); const [mentionText, setMentionText] = useState(); const [showMentionPopup, setShowMentionPopup] = useState(false); @@ -728,6 +736,12 @@ export const MessageInput = forwardRef( } if (type === 'contentHeight') { + if (payload === containerHeight) { + return; + } + if (containerHeight > maxInputHeight) { + return; + } setContainerHeight(payload); setHeight?.(payload); return; @@ -768,6 +782,8 @@ export const MessageInput = forwardRef( webviewRef, editorCrashed, setEditorCrashed, + containerHeight, + maxInputHeight, ] ); @@ -827,12 +843,14 @@ export const MessageInput = forwardRef( borderColor="$border" borderWidth={1} borderRadius="$xl" + maxHeight={maxInputHeight} > {showInlineAttachments && } Date: Fri, 13 Sep 2024 12:41:00 +0000 Subject: [PATCH 021/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index ed12ec966c..6f46277a6d 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v4.kgre5.dcoki.is810.6mj41.s3k9q.glob' 0v4.kgre5.dcoki.is810.6mj41.s3k9q] + glob-http+['https://bootstrap.urbit.org/glob-0v4.egepg.b3pvn.bj8v4.tj86r.1iml7.glob' 0v4.egepg.b3pvn.bj8v4.tj86r.1iml7] base+'groups' version+[6 3 0] website+'https://tlon.io' From 6141f96521b460570b04d75dce92b473f0e47e9b Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Fri, 13 Sep 2024 09:21:28 -0400 Subject: [PATCH 022/157] rework activity rendering (#3912) * rework activity rendering * add back new item background * show summary text for flagged posts --- apps/tlon-mobile/cosmos.imports.ts | 58 +-- .../src/fixtures/Activity.fixture.tsx | 92 ++++ .../src/fixtures/activityHelpers.tsx | 395 ++++++++++++++++++ .../src/fixtures/contentHelpers.tsx | 22 + packages/shared/src/store/dbHooks.ts | 8 +- .../components/Activity/ActivityHeader.tsx | 2 +- .../components/Activity/ActivityListItem.tsx | 184 ++++++++ .../Activity/ActivityScreenView.tsx | 146 ++++--- .../Activity/ActivitySourceContent.tsx | 198 ++++++--- .../Activity/ActivitySummaryHeader.tsx | 35 -- .../Activity/ActivitySummaryMessage.tsx | 265 ++++++------ .../Activity/ChannelActivitySummary.tsx | 85 ---- .../Activity/GroupActivitySummary.tsx | 103 ----- packages/ui/src/components/Avatar.tsx | 6 + .../ContentReference/ContentReference.tsx | 2 +- .../components/PostContent/contentUtils.tsx | 49 ++- packages/ui/src/components/ScreenHeader.tsx | 7 +- packages/ui/src/components/Tabs.tsx | 15 +- 18 files changed, 1134 insertions(+), 538 deletions(-) create mode 100644 apps/tlon-mobile/src/fixtures/Activity.fixture.tsx create mode 100644 apps/tlon-mobile/src/fixtures/activityHelpers.tsx create mode 100644 packages/ui/src/components/Activity/ActivityListItem.tsx delete mode 100644 packages/ui/src/components/Activity/ActivitySummaryHeader.tsx delete mode 100644 packages/ui/src/components/Activity/ChannelActivitySummary.tsx delete mode 100644 packages/ui/src/components/Activity/GroupActivitySummary.tsx diff --git a/apps/tlon-mobile/cosmos.imports.ts b/apps/tlon-mobile/cosmos.imports.ts index a5a05f2536..f4da6171f0 100644 --- a/apps/tlon-mobile/cosmos.imports.ts +++ b/apps/tlon-mobile/cosmos.imports.ts @@ -3,17 +3,18 @@ import { RendererConfig, UserModuleWrappers } from 'react-cosmos-core'; import * as fixture0 from './src/App.fixture'; -import * as fixture47 from './src/fixtures/ActionSheet/AddGalleryPostSheet.fixture'; -import * as fixture46 from './src/fixtures/ActionSheet/AttachmentSheet.fixture'; -import * as fixture45 from './src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture'; -import * as fixture44 from './src/fixtures/ActionSheet/CreateChannelSheet.fixture'; -import * as fixture43 from './src/fixtures/ActionSheet/DeleteSheet.fixture'; -import * as fixture42 from './src/fixtures/ActionSheet/EditSectionNameSheet.fixture'; -import * as fixture41 from './src/fixtures/ActionSheet/GenericActionSheet.fixture'; -import * as fixture40 from './src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture'; -import * as fixture39 from './src/fixtures/ActionSheet/GroupPreviewSheet.fixture'; -import * as fixture38 from './src/fixtures/ActionSheet/ProfileSheet.fixture'; -import * as fixture37 from './src/fixtures/ActionSheet/SendPostRetrySheet.fixture'; +import * as fixture48 from './src/fixtures/ActionSheet/AddGalleryPostSheet.fixture'; +import * as fixture47 from './src/fixtures/ActionSheet/AttachmentSheet.fixture'; +import * as fixture46 from './src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture'; +import * as fixture45 from './src/fixtures/ActionSheet/CreateChannelSheet.fixture'; +import * as fixture44 from './src/fixtures/ActionSheet/DeleteSheet.fixture'; +import * as fixture43 from './src/fixtures/ActionSheet/EditSectionNameSheet.fixture'; +import * as fixture42 from './src/fixtures/ActionSheet/GenericActionSheet.fixture'; +import * as fixture41 from './src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture'; +import * as fixture40 from './src/fixtures/ActionSheet/GroupPreviewSheet.fixture'; +import * as fixture39 from './src/fixtures/ActionSheet/ProfileSheet.fixture'; +import * as fixture38 from './src/fixtures/ActionSheet/SendPostRetrySheet.fixture'; +import * as fixture34 from './src/fixtures/Activity.fixture'; import * as fixture33 from './src/fixtures/AttachmentPreviewList.fixture'; import * as fixture32 from './src/fixtures/AudioEmbed.fixture'; import * as fixture31 from './src/fixtures/Avatar.fixture'; @@ -25,9 +26,9 @@ import * as fixture26 from './src/fixtures/ChannelHeader.fixture'; import * as fixture25 from './src/fixtures/ChannelSwitcherSheet.fixture'; import * as fixture24 from './src/fixtures/ChatMessage.fixture'; import * as fixture23 from './src/fixtures/ContactList.fixture'; -import * as fixture36 from './src/fixtures/DetailView/ChatDetailView.fixture'; -import * as fixture35 from './src/fixtures/DetailView/GalleryDetailView.fixture'; -import * as fixture34 from './src/fixtures/DetailView/NotebookDetailView.fixture'; +import * as fixture37 from './src/fixtures/DetailView/ChatDetailView.fixture'; +import * as fixture36 from './src/fixtures/DetailView/GalleryDetailView.fixture'; +import * as fixture35 from './src/fixtures/DetailView/NotebookDetailView.fixture'; import * as fixture22 from './src/fixtures/Form.fixture'; import * as fixture21 from './src/fixtures/GalleryPost.fixture'; import * as fixture20 from './src/fixtures/GroupList.fixture'; @@ -92,39 +93,40 @@ const fixtures = { 'src/fixtures/Avatar.fixture.tsx': { module: fixture31 }, 'src/fixtures/AudioEmbed.fixture.tsx': { module: fixture32 }, 'src/fixtures/AttachmentPreviewList.fixture.tsx': { module: fixture33 }, + 'src/fixtures/Activity.fixture.tsx': { module: fixture34 }, 'src/fixtures/DetailView/NotebookDetailView.fixture.tsx': { - module: fixture34, + module: fixture35, }, 'src/fixtures/DetailView/GalleryDetailView.fixture.tsx': { - module: fixture35, + module: fixture36, }, - 'src/fixtures/DetailView/ChatDetailView.fixture.tsx': { module: fixture36 }, + 'src/fixtures/DetailView/ChatDetailView.fixture.tsx': { module: fixture37 }, 'src/fixtures/ActionSheet/SendPostRetrySheet.fixture.tsx': { - module: fixture37, + module: fixture38, }, - 'src/fixtures/ActionSheet/ProfileSheet.fixture.tsx': { module: fixture38 }, + 'src/fixtures/ActionSheet/ProfileSheet.fixture.tsx': { module: fixture39 }, 'src/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx': { - module: fixture39, + module: fixture40, }, 'src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture.tsx': { - module: fixture40, + module: fixture41, }, 'src/fixtures/ActionSheet/GenericActionSheet.fixture.tsx': { - module: fixture41, + module: fixture42, }, 'src/fixtures/ActionSheet/EditSectionNameSheet.fixture.tsx': { - module: fixture42, + module: fixture43, }, - 'src/fixtures/ActionSheet/DeleteSheet.fixture.tsx': { module: fixture43 }, + 'src/fixtures/ActionSheet/DeleteSheet.fixture.tsx': { module: fixture44 }, 'src/fixtures/ActionSheet/CreateChannelSheet.fixture.tsx': { - module: fixture44, + module: fixture45, }, 'src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture.tsx': { - module: fixture45, + module: fixture46, }, - 'src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx': { module: fixture46 }, + 'src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx': { module: fixture47 }, 'src/fixtures/ActionSheet/AddGalleryPostSheet.fixture.tsx': { - module: fixture47, + module: fixture48, }, }; diff --git a/apps/tlon-mobile/src/fixtures/Activity.fixture.tsx b/apps/tlon-mobile/src/fixtures/Activity.fixture.tsx new file mode 100644 index 0000000000..fb66135b3f --- /dev/null +++ b/apps/tlon-mobile/src/fixtures/Activity.fixture.tsx @@ -0,0 +1,92 @@ +import * as logic from '@tloncorp/shared/dist/logic'; +import { + ActivityScreenContent, + AppDataContextProvider, +} from '@tloncorp/ui/src'; +import { PropsWithChildren } from 'react'; +import { Alert } from 'react-native'; + +import { FixtureWrapper } from './FixtureWrapper'; +import { activityItems } from './activityHelpers'; +import { exampleContacts, postsByType } from './contentHelpers'; +import { tlonLocalBulletinBoard, tlonLocalGettingStarted } from './fakeData'; + +const baseContentProps = { + activeTab: 'all', + onPressTab: () => {}, + onPressEvent: () => { + Alert.alert('Event pressed'); + }, + onEndReached: () => {}, + isFetching: false, + isRefreshing: false, + onRefreshTriggered: () => {}, +} as const; + +function ActivityFixtureWrapper({ children }: PropsWithChildren) { + return ( + + + {children} + + + ); +} + +const ActivityFixture = ({ + items, +}: { + items: logic.SourceActivityEvents[]; +}) => { + return ( + + + + ); +}; + +const contentVariants = [ + ...Object.values(postsByType).map((v) => + activityItems.groupPost(4, { + post: v, + }) + ), + activityItems.groupPostWithRandomContent(4, { + channel: tlonLocalBulletinBoard, + }), + activityItems.groupPostWithRandomContent(4, { + channel: tlonLocalGettingStarted, + }), +]; + +const eventVariants = [ + ...Object.values(activityItems).map((item) => item(1)), + activityItems.groupPost(1, { + isMention: true, + }), + activityItems.groupDmPost(1, { isMention: true }), + activityItems.groupThreadReply(1, { isMention: true }), + activityItems.groupDmThreadReply(1, { isMention: true }), +]; + +const counts = [1, 2, 3, 4, 8, 16]; + +const countVariants = counts.map((count) => activityItems.groupPost(count)); + +export default { + All: ( + + ), + EventTypes: , + ContentTypes: , + Counts: , +}; diff --git a/apps/tlon-mobile/src/fixtures/activityHelpers.tsx b/apps/tlon-mobile/src/fixtures/activityHelpers.tsx new file mode 100644 index 0000000000..7d3cca2e35 --- /dev/null +++ b/apps/tlon-mobile/src/fixtures/activityHelpers.tsx @@ -0,0 +1,395 @@ +import { PostContent } from '@tloncorp/shared/dist/api'; +import * as db from '@tloncorp/shared/dist/db'; +import * as logic from '@tloncorp/shared/dist/logic'; +import { ExtendedEventType } from '@tloncorp/shared/dist/urbit'; + +import { exampleContacts, postsByType } from './contentHelpers'; +import { group as fakeGroup, tlonLocalIntros } from './fakeData'; + +function exampleContact(i: number) { + return Object.values(exampleContacts)[ + i % Object.values(exampleContacts).length + ]; +} + +const groupDmChannel: db.Channel = { + id: '0v4.00000.qcon2.pk30o.idqbo.qjuv7', + type: 'groupDm', +} as const; + +const dmChannel: db.Channel = { + id: '~fabled-faster', + type: 'dm', +}; + +export const groupJoinRequestActivity = ( + count: number, + extraProps?: GroupJoinRequestEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => { + return groupJoinRequestEvent({ + contact: exampleContact(i), + ...extraProps, + }); + }) + ); + +export const groupThreadReplyActivity = ( + count: number, + extraProps?: GroupThreadReplyEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => + groupThreadReplyEvent({ + ...extraProps, + contact: exampleContact(i), + }) + ) + ); + +export const groupPostActivity = ( + count: number, + extraProps?: GroupPostEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => + groupPostEvent({ + contact: exampleContact(i), + ...extraProps, + }) + ) + ); + +export const groupPostWithRandomContentActivity = ( + count: number, + extraProps?: GroupPostEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => + groupPostEvent({ + contact: exampleContact(i), + post: Object.values(postsByType)[i % Object.values(postsByType).length], + ...extraProps, + }) + ) + ); + +export const dmPostActivity = (count: number, extraProps?: DmPostEventParams) => + summary( + ...Array.from({ length: count }, (v, i) => + dmPostEvent({ + contact: exampleContact(i), + ...extraProps, + }) + ) + ); + +export const groupDmPostActivity = ( + count: number, + extraProps?: GroupDmPostEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => + groupDmPostEvent({ + contact: exampleContact(i), + ...extraProps, + }) + ) + ); + +export const groupDmThreadReplyActivity = ( + count: number, + extraProps?: ThreadReplyEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => + groupDmThreadReplyEvent({ + contact: exampleContact(i), + ...extraProps, + }) + ) + ); + +export const flagPostActivity = ( + count: number, + extraProps?: PostFlagEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => + postFlagEvent({ + contact: exampleContact(i), + ...extraProps, + }) + ) + ); + +export const flagReplyActivity = ( + count: number, + extraProps?: ReplyFlagEventParams +) => + summary( + ...Array.from({ length: count }, (v, i) => + replyFlagEvent({ contact: exampleContact(i), ...extraProps }) + ) + ); + +export const activityItems = { + groupJoinRequest: groupJoinRequestActivity, + groupPost: groupPostActivity, + groupPostWithRandomContent: groupPostWithRandomContentActivity, + groupThreadReply: groupThreadReplyActivity, + groupDmPost: groupDmPostActivity, + groupDmThreadReply: groupDmThreadReplyActivity, + dmPost: dmPostActivity, + flagPost: flagPostActivity, + flagReply: flagReplyActivity, +}; + +function summary(...events: db.ActivityEvent[]): logic.SourceActivityEvents { + return logic.toSourceActivityEvents(events)[0]; +} + +interface PostFlagEventParams { + group?: db.Group; + post?: db.Post; + channel?: db.Channel; + contact?: db.Contact; +} + +function postFlagEvent({ + group = fakeGroup, + channel = tlonLocalIntros, + post = postsByType.text, + contact = exampleContacts.fabledFaster, +}: PostFlagEventParams): db.ActivityEvent { + return postEvent({ + group, + channel, + post, + sourceId: `group/${group.id}`, + contact, + type: 'flag-post', + }); +} + +interface ReplyFlagEventParams { + group?: db.Group; + post?: db.Post; + parent?: db.Post; + channel?: db.Channel; + contact?: db.Contact; +} + +function replyFlagEvent({ + group = fakeGroup, + channel = tlonLocalIntros, + post = postsByType.text, + parent = postsByType.text, + contact = exampleContacts.fabledFaster, +}: ReplyFlagEventParams): db.ActivityEvent { + return postEvent({ + group, + channel, + post, + parent, + sourceId: `group/${group.id}`, + contact, + type: 'flag-reply', + }); +} + +interface ThreadReplyEventParams { + channel?: db.Channel; + post?: db.Post; + parent?: db.Post; + contact?: db.Contact; + content?: PostContent; + isMention?: boolean; +} + +function groupDmThreadReplyEvent({ + channel = groupDmChannel, + post = postsByType.text, + parent = postsByType.code, + contact = exampleContacts.fabledFaster, + isMention = false, +}: ThreadReplyEventParams): db.ActivityEvent { + return postEvent({ + channel, + post, + parent, + contact, + sourceId: `dm-thread/${channel.id}/${parent.id}`, + type: 'reply', + bucketId: isMention ? 'replies' : 'mentions', + isMention, + }); +} + +type GroupThreadReplyEventParams = ThreadReplyEventParams & { + group?: db.Group; +}; + +function groupThreadReplyEvent({ + group = fakeGroup, + channel = tlonLocalIntros, + post = postsByType.text, + parent = postsByType.code, + contact = exampleContacts.fabledFaster, + isMention = false, +}: GroupThreadReplyEventParams): db.ActivityEvent { + return postEvent({ + group, + channel, + post, + parent, + contact, + sourceId: `thread/${channel.id}`, + type: 'reply', + bucketId: isMention ? 'replies' : 'mentions', + isMention, + }); +} + +interface GroupJoinRequestEventParams { + group?: db.Group; + contact?: db.Contact; +} + +function groupJoinRequestEvent({ + group = fakeGroup, + contact = exampleContacts.fabledFaster, +}: GroupJoinRequestEventParams): db.ActivityEvent { + return { + id: randomId(), + bucketId: 'all', + sourceId: `group/${group.id}`, + type: 'group-ask', + timestamp: Date.now(), + groupId: group.id, + group, + groupEventUserId: contact.id, + }; +} + +interface DmPostEventParams { + channel?: db.Channel; + contact?: db.Contact; + post?: db.Post; +} + +function dmPostEvent({ + channel = dmChannel, + contact = exampleContacts.fabledFaster, + post = postsByType.text, +}: DmPostEventParams) { + return postEvent({ + channel, + contact, + post, + sourceId: `dm/${channel.id}`, + type: 'post', + }); +} + +type GroupDmPostEventParams = DmPostEventParams & { + isMention?: boolean; +}; + +function groupDmPostEvent({ + channel = groupDmChannel, + contact = exampleContacts.fabledFaster, + post = postsByType.text, + isMention, +}: GroupDmPostEventParams) { + return postEvent({ + channel, + contact, + post, + sourceId: `group-dm/${channel.id}`, + type: 'post', + bucketId: isMention ? 'mentions' : 'all', + isMention, + }); +} + +type GroupPostEventParams = GroupDmPostEventParams & { + group?: db.Group; +}; + +function groupPostEvent({ + group = fakeGroup, + channel = tlonLocalIntros, + post = postsByType.text, + contact = exampleContacts.fabledFaster, + isMention, +}: GroupPostEventParams) { + return postEvent({ + group, + channel, + contact, + post, + sourceId: `group/${group.id}`, + type: 'post', + bucketId: isMention ? 'mentions' : 'all', + isMention, + }); +} + +interface PostEventParams { + group?: db.Group | null; + channel: db.Channel; + contact: db.Contact; + post: db.Post; + sourceId: string; + type: ExtendedEventType; + parent?: db.Post | null; + isMention?: boolean; + bucketId?: db.ActivityBucket; +} + +function postEvent({ + group, + channel, + contact, + post, + sourceId, + type, + parent, + isMention, + bucketId = 'all', +}: PostEventParams): db.ActivityEvent { + return { + id: randomId(), + bucketId, + sourceId, + type, + timestamp: Date.now(), + postId: post.id, + post: { + ...post, + authorId: contact.id, + author: contact, + groupId: group?.id, + group, + channelId: channel.id, + channel, + }, + authorId: contact.id, + author: contact, + parentId: parent?.id, + parent: parent, + channelId: channel.id, + channel, + groupId: group?.id, + group, + content: JSON.parse(post.content as string) as PostContent, + shouldNotify: true, + isMention: !!isMention, + }; +} + +function randomId() { + return Math.random().toString(36).substring(2); +} diff --git a/apps/tlon-mobile/src/fixtures/contentHelpers.tsx b/apps/tlon-mobile/src/fixtures/contentHelpers.tsx index eb0a41f381..4a658ba2e7 100644 --- a/apps/tlon-mobile/src/fixtures/contentHelpers.tsx +++ b/apps/tlon-mobile/src/fixtures/contentHelpers.tsx @@ -545,6 +545,28 @@ export const postWithSingleEmoji = makePost(exampleContacts.emotive, [ verse.inline('🙏', inline.break()), ]); +export const postsByType = { + image: postWithImage, + text: postWithText, + mention: postWithMention, + blockquote: postWithBlockquote, + code: postWithCode, + list: postWithList, + link: postWithLink, + chatReference: postWithChatReference, + imageAndText: postWithImageAndText, + groupReference: postWithGroupReference, + groupReferenceNoAvatar: postWithGroupReferenceNoAvatar, + longNote: postWithLongNote, + galleryReference: postWithGalleryReference, + notebookReference: postWithNotebookReference, + video: postWithVideo, + deleted: postWithDeleted, + hidden: postWithHidden, + emoji: postWithEmoji, + singleEmoji: postWithSingleEmoji, +}; + const postsMap: Record = Object.fromEntries( [ referencedGalleryPost, diff --git a/packages/shared/src/store/dbHooks.ts b/packages/shared/src/store/dbHooks.ts index 96967fd44b..d613b88aa9 100644 --- a/packages/shared/src/store/dbHooks.ts +++ b/packages/shared/src/store/dbHooks.ts @@ -289,8 +289,9 @@ export const useLiveGroupUnread = (unread: db.GroupUnread | null) => { }; export const useLiveUnread = ( - unread: db.ChannelUnread | db.ThreadUnreadState | null + unread: db.ChannelUnread | db.ThreadUnreadState | db.GroupUnread | null ) => { + const isGroup = useMemo(() => unread && 'groupId' in unread, [unread]); const isThread = useMemo(() => unread && 'threadId' in unread, [unread]); const threadUnread = useLiveThreadUnread( isThread ? (unread as db.ThreadUnreadState) : null @@ -298,7 +299,10 @@ export const useLiveUnread = ( const channelUnread = useLiveChannelUnread( isThread ? null : (unread as db.ChannelUnread | null) ); - return isThread ? threadUnread : channelUnread; + const groupUnread = useLiveGroupUnread( + isGroup ? (unread as db.GroupUnread) : null + ); + return isThread ? threadUnread : isGroup ? groupUnread : channelUnread; }; export const useGroups = (options: db.GetGroupsOptions) => { diff --git a/packages/ui/src/components/Activity/ActivityHeader.tsx b/packages/ui/src/components/Activity/ActivityHeader.tsx index e1c5163836..7099ea98a8 100644 --- a/packages/ui/src/components/Activity/ActivityHeader.tsx +++ b/packages/ui/src/components/Activity/ActivityHeader.tsx @@ -18,7 +18,7 @@ function ActivityHeaderRaw({ - Activity + Activity diff --git a/packages/ui/src/components/Activity/ActivityListItem.tsx b/packages/ui/src/components/Activity/ActivityListItem.tsx new file mode 100644 index 0000000000..c2cfd79a34 --- /dev/null +++ b/packages/ui/src/components/Activity/ActivityListItem.tsx @@ -0,0 +1,184 @@ +import * as db from '@tloncorp/shared/dist/db'; +import * as logic from '@tloncorp/shared/dist/logic'; +import * as store from '@tloncorp/shared/dist/store'; +import React, { PropsWithChildren, useCallback, useMemo } from 'react'; +import { View, XStack, YStack, styled } from 'tamagui'; + +import { useCalm } from '../../contexts'; +import { getChannelTitle } from '../../utils'; +import { ChannelAvatar, ContactAvatar, GroupAvatar } from '../Avatar'; +import { Icon } from '../Icon'; +import { Text } from '../TextV2'; +import { UnreadDot } from '../UnreadDot'; +import { ActivitySourceContent } from './ActivitySourceContent'; +import { SummaryMessage } from './ActivitySummaryMessage'; + +export const ActivityListItem = React.memo(function ActivityListItem({ + sourceActivity, + seenMarker, + onPress, +}: { + sourceActivity: logic.SourceActivityEvents; + seenMarker: number; + onPress: (event: db.ActivityEvent) => void; +}) { + const event = sourceActivity.newest; + const handlePress = useCallback(() => onPress(event), [event, onPress]); + + if ( + event.type === 'post' || + event.type === 'reply' || + event.type === 'flag-post' || + event.type === 'flag-reply' || + event.type === 'group-ask' + ) { + return ( + + + + ); + } + + return Event type {event.type} not supported; +}); + +export function ActivityListItemContent({ + summary, + pressHandler, + seenMarker, +}: { + summary: logic.SourceActivityEvents; + pressHandler?: () => void; + seenMarker: number; +}) { + const calm = useCalm(); + const newestPost = summary.newest; + const group = newestPost.group ?? undefined; + const channel: db.Channel | undefined = newestPost.channel ?? undefined; + const modelUnread = + summary.type === 'post' + ? newestPost.channel?.unread ?? null + : summary.type === 'group-ask' + ? newestPost.group?.unread ?? null + : newestPost.parent?.threadUnread ?? null; + const { data: unread } = store.useLiveUnread(modelUnread); + const unreadCount = useMemo(() => { + return (isGroupUnread(unread) ? unread.notifyCount : unread?.count) ?? 0; + }, [unread]); + + const title = !channel + ? group + ? group.title ?? '' + : '' + : channel.type === 'dm' + ? 'Direct message' + : channel.type === 'groupDm' + ? 'Group chat' + : (group?.title ? group.title + ': ' : '') + + getChannelTitle(channel, calm.disableNicknames); + + return ( + seenMarker ? '$positiveBackground' : 'unset' + } + pressStyle={{ backgroundColor: '$secondaryBackground' }} + onPress={pressHandler} + > + + + + + + + + + {summary.type !== 'group-ask' ? ( + + ) : null} + + + + ); +} + +function ActivitySummaryTitleIcon({ + channel, + group, +}: { + channel?: db.Channel | null; + group?: db.Group | null; +}) { + return group ? ( + channel ? ( + + ) : ( + + ) + ) : ( + + ); +} + +function ActivitySummaryHeader({ + title, + unreadCount, + sentTime, + children, +}: PropsWithChildren<{ + title: string; + unreadCount: number; + sentTime?: number; +}>) { + return ( + + {unreadCount || children ? ( + + {unreadCount ? : null} + {children} + + ) : null} + + {title} + + {sentTime && ( + + {logic.makePrettyTime(new Date(sentTime))} + + )} + + ); +} + +export const ActivitySummaryFrame = styled(XStack, { + padding: '$l', + gap: '$l', + borderRadius: '$xl', +}); + +export const ActivitySummaryContent = styled(YStack, { + paddingTop: '$xs', + gap: '$2xs', + flex: 1, +}); + +function isGroupUnread( + unread?: db.GroupUnread | db.ChannelUnread | db.ThreadUnreadState | null +): unread is db.GroupUnread { + return !!unread && 'groupId' in unread; +} diff --git a/packages/ui/src/components/Activity/ActivityScreenView.tsx b/packages/ui/src/components/Activity/ActivityScreenView.tsx index 2e8cce35fc..9789c204f0 100644 --- a/packages/ui/src/components/Activity/ActivityScreenView.tsx +++ b/packages/ui/src/components/Activity/ActivityScreenView.tsx @@ -1,15 +1,13 @@ import * as db from '@tloncorp/shared/dist/db'; import * as logic from '@tloncorp/shared/dist/logic'; import * as store from '@tloncorp/shared/dist/store'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import React from 'react'; -import { FlatList, RefreshControl } from 'react-native'; -import { SizableText, View } from 'tamagui'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { FlatList, RefreshControl, StyleProp, ViewStyle } from 'react-native'; +import { View, useStyle } from 'tamagui'; import { LoadingSpinner } from '../LoadingSpinner'; import { ActivityHeader } from './ActivityHeader'; -import { ChannelActivitySummary } from './ChannelActivitySummary'; -import { GroupActivitySummary } from './GroupActivitySummary'; +import { ActivityListItem } from './ActivityListItem'; export function ActivityScreenView({ isFocused, @@ -37,6 +35,7 @@ export function ActivityScreenView({ bucketFetchers.all.activity[0]?.newest.timestamp ?? activitySeenMarker ); }, [activitySeenMarker, bucketFetchers.all.activity]); + const moveSeenMarker = useCallback(() => { setTimeout(() => { store.advanceActivitySeenMarker(newestTimestamp); @@ -95,19 +94,6 @@ export function ActivityScreenView({ [goToChannel, goToThread, goToGroup] ); - const renderItem = useCallback( - ({ item }: { item: logic.SourceActivityEvents }) => { - return ( - - ); - }, - [activitySeenMarker, handlePressEvent] - ); - const events = useMemo( () => currentFetcher.activity, [currentFetcher.activity] @@ -128,10 +114,6 @@ export function ActivityScreenView({ } }, [currentFetcher]); - const keyExtractor = useCallback((item: logic.SourceActivityEvents) => { - return `${item.sourceId}/${item.newest.bucketId}/${item.all.length}`; - }, []); - const [refreshing, setRefreshing] = React.useState(false); const onRefresh = React.useCallback(async () => { setRefreshing(true); @@ -140,68 +122,82 @@ export function ActivityScreenView({ }, [refresh]); return ( - - - {events.length > 0 && ( - : null - } - refreshControl={ - - } - /> - )} - + ); } -function ActivityEventRaw({ - sourceActivity, +export function ActivityScreenContent({ + activeTab, + events, + isFetching, + isRefreshing, + onPressTab, + onPressEvent, + onEndReached, + onRefreshTriggered, seenMarker, - onPress, }: { + activeTab: db.ActivityBucket; + onPressTab: (tab: db.ActivityBucket) => void; + onPressEvent: (event: db.ActivityEvent) => void; + onEndReached: () => void; + events: logic.SourceActivityEvents[]; + isFetching: boolean; + isRefreshing: boolean; + onRefreshTriggered: () => void; seenMarker: number; - sourceActivity: logic.SourceActivityEvents; - onPress: (event: db.ActivityEvent) => void; }) { - const event = sourceActivity.newest; - const handlePress = useCallback(() => onPress(event), [event, onPress]); + const keyExtractor = useCallback((item: logic.SourceActivityEvents) => { + return `${item.newest.id}/${item.sourceId}/${item.newest.bucketId}/${item.all.length}`; + }, []); - if (db.isGroupEvent(event)) { - return ( - - { + return ( + - - ); - } + ); + }, + [onPressEvent, seenMarker] + ); - if ( - event.type === 'post' || - event.type === 'reply' || - event.type === 'flag-post' || - event.type === 'flag-reply' - ) { - return ( - - - - ); - } + const containerStyle = useStyle({ + padding: '$l', + gap: '$l', + }) as StyleProp; - return Event type {event.type} not supported; + return ( + + + {events.length > 0 && ( + : null} + refreshControl={ + + } + /> + )} + + ); } -const SourceActivityDisplay = React.memo(ActivityEventRaw); diff --git a/packages/ui/src/components/Activity/ActivitySourceContent.tsx b/packages/ui/src/components/Activity/ActivitySourceContent.tsx index 9a79f290a1..d7d5529f2d 100644 --- a/packages/ui/src/components/Activity/ActivitySourceContent.tsx +++ b/packages/ui/src/components/Activity/ActivitySourceContent.tsx @@ -1,77 +1,159 @@ import * as db from '@tloncorp/shared/dist/db'; import * as logic from '@tloncorp/shared/dist/logic'; import { useMemo } from 'react'; -import { ScrollView, View } from 'tamagui'; +import { ScrollView, styled } from 'tamagui'; +import { useContactName } from '../ContactNameV2'; +import { PostReference } from '../ContentReference'; import { GalleryPost } from '../GalleryPost'; -import { NotebookPost } from '../NotebookPost'; -import { PostContentRenderer } from '../PostContent'; +import { Icon } from '../Icon'; +import { createContentRenderer } from '../PostContent'; +import { + BlockData, + InlineData, + prependInline, + usePostContent, +} from '../PostContent/contentUtils'; +import { Text } from '../TextV2'; + +type ActivitySourceContentProps = { + summary: logic.SourceActivityEvents; + pressHandler?: () => void; +}; export function ActivitySourceContent({ summary, pressHandler, -}: { - summary: logic.SourceActivityEvents; - pressHandler?: () => void; -}) { +}: ActivitySourceContentProps) { + const isReply = !!summary.newest.parentId; + const isChatPost = + summary.newest.channel?.type !== 'gallery' && + summary.newest.channel?.type !== 'notebook'; + return isReply || isChatPost ? ( + + ) : ( + + ); +} + +function ChatContentRenderer({ summary }: ActivitySourceContentProps) { const post = useMemo(() => getPost(summary.newest), [summary.newest]); - const allPosts = useMemo(() => { - const fullPosts = - summary.all?.map((event) => getPost(event)).filter(Boolean) ?? []; // defensive + const postAuthorName = useContactName(post.authorId); + const content = usePostContent(post); + // We want to display the author name inline if possible, so we inject it into + // the content. + const enrichedContent: BlockData[] = useMemo(() => { + const authorNameInline: InlineData = { + type: 'text', + text: postAuthorName + ': ', + }; + return prependInline(content, authorNameInline); + }, [content, postAuthorName]); + return ( + <> + + {summary.all.length > 1 ? ( + + +{summary.all.length - 1} more + + ) : null} + + ); +} + +function NotebookOrGalleryContentRenderer({ + summary, + pressHandler, +}: ActivitySourceContentProps) { + const posts = useUniqueSummaryPosts(summary); + const isNote = summary.newest.channel?.type === 'notebook'; + + return ( + + {posts.map((post) => + isNote ? ( + + ) : ( + + ) + )} + + ); +} + +function useUniqueSummaryPosts(summary: logic.SourceActivityEvents) { + return useMemo(() => { const seen = new Set(); - return fullPosts.filter((item) => { - if (seen.has(item.id)) { - return false; + return summary.all?.flatMap((event) => { + if (event.postId && !seen.has(event.postId)) { + seen.add(event.postId); + return [getPost(event)]; } - seen.add(item.id); - return true; + return []; }); }, [summary.all]); +} - // thread or comment - if (summary.newest.parentId) { - return ; - } - - if ( - summary.newest.channel?.type === 'gallery' || - summary.newest.channel?.type === 'notebook' - ) { - return ( - - {summary.newest.channel?.type === 'notebook' ? ( - <> - {allPosts.map((post) => ( - - - - ))} - - ) : ( - <> - {allPosts.map((post) => ( - - - - ))} - - )} - - ); - } +const ActivityContentRenderer = createContentRenderer({ + blockSettings: { + blockWrapper: { + padding: 0, + }, + lineText: { + size: '$label/m', + trimmed: false, + }, + image: { + flex: 1, + alignSelf: 'flex-start', + marginVertical: '$xs', + borderRadius: '$xs', + imageProps: { + width: '$4xl', + height: '$4xl', + aspectRatio: 'unset', + }, + }, + bigEmoji: { + marginVertical: '$s', + }, + }, + blockRenderers: { + reference: () => { + return ; + }, + code: () => { + return ; + }, + video: () => { + return ; + }, + }, +}); - return ; -} +const PlaceholderIcon = styled(Icon, { + backgroundColor: '$secondaryBackground', + borderRadius: '$xs', + customSize: [24, 12], + marginVertical: '$xs', +}); function getPost(event: db.ActivityEvent): db.Post { let post: db.Post; diff --git a/packages/ui/src/components/Activity/ActivitySummaryHeader.tsx b/packages/ui/src/components/Activity/ActivitySummaryHeader.tsx deleted file mode 100644 index 4b52db3cd1..0000000000 --- a/packages/ui/src/components/Activity/ActivitySummaryHeader.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as logic from '@tloncorp/shared/dist/logic'; -import { PropsWithChildren } from 'react'; -import { SizableText, XStack } from 'tamagui'; - -import { UnreadDot } from '../UnreadDot'; - -export function ActivitySummaryHeader({ - title, - unreadCount, - sentTime, - children, -}: PropsWithChildren<{ - title: string; - unreadCount: number; - sentTime?: number; -}>) { - return ( - - {unreadCount || children ? ( - - {unreadCount ? : null} - {children} - - ) : null} - - {title} - - {sentTime && ( - - {logic.makePrettyTime(new Date(sentTime))} - - )} - - ); -} diff --git a/packages/ui/src/components/Activity/ActivitySummaryMessage.tsx b/packages/ui/src/components/Activity/ActivitySummaryMessage.tsx index 91c9737a44..ffd3f34120 100644 --- a/packages/ui/src/components/Activity/ActivitySummaryMessage.tsx +++ b/packages/ui/src/components/Activity/ActivitySummaryMessage.tsx @@ -1,222 +1,141 @@ -import * as api from '@tloncorp/shared/dist/api'; import * as db from '@tloncorp/shared/dist/db'; import * as logic from '@tloncorp/shared/dist/logic'; -import { PropsWithChildren, useMemo } from 'react'; -import React from 'react'; -import { SizableText } from 'tamagui'; +import React, { Fragment, useMemo } from 'react'; +import { styled } from 'tamagui'; -import ContactName from '../ContactName'; +import { useCurrentUserId } from '../../contexts'; +import { ContactName } from '../ContactNameV2'; +import { Text } from '../TextV2'; function SummaryMessageRaw({ summary, }: { summary: logic.SourceActivityEvents; }) { - const relevancy = getRelevancy(summary); + const currentUserId = useCurrentUserId(); + const relevancy = getRelevancy(summary, currentUserId); const newest = summary.newest; const count = summary.all.length; const plural = summary.all.length > 1; - const otherSet = new Set(); - summary.all.forEach((event) => { - if (event.authorId && event.authorId !== newest.authorId) { - otherSet.add(event.authorId); - } - }); - const otherAuthors = Array.from(otherSet); - - const NewestAuthor = useMemo(() => { - return ( - - ); - }, [newest.authorId]); + const authors = useActivitySummaryAuthors(summary); - const Authors = useMemo(() => { - return ( - <> - - {otherAuthors[0] && ( - <> - {`${otherAuthors[1] ? ', ' : ' and '}`} - - - )} - {otherAuthors[1] && ( - <> - {', and '} - - - )} - - ); - }, [newest.authorId, otherAuthors]); + if ( + authors.length === 1 && + relevancy !== 'groupJoinRequest' && + relevancy !== 'flaggedPost' && + relevancy !== 'flaggedReply' + ) { + return null; + } // if it's a mention, life is easy and we just say what it is if (relevancy === 'mention') { return ( - - {NewestAuthor} - {` mentioned you in a ${postName(newest)}`} - - ); - } - - if (relevancy === 'dm') { - const message = - count === 1 ? ' sent you a message' : ` sent you ${count} messages`; - return ( - - {NewestAuthor} - {message} - + + + {` mentioned you`}: + ); } if (relevancy === 'groupchat') { + const message = count === 1 ? ' sent a message' : ` sent ${count} messages`; return ( - - {Authors} - {' messaged the group'} - + + + {message}: + ); } if (relevancy === 'postInYourChannel') { return ( - + {`New ${postName(newest, plural)} from `} - {Authors} - + + ); } if (relevancy === 'replyToGalleryOrNote') { - const message = `commented on your ${postName(newest)}`; + const message = `commented on your post`; return ( - - {Authors} + + {` ${message}`} - + ); } if (relevancy === 'replyToChatPost') { const message = `replied to your message`; return ( - - {Authors} + + {` ${message}`} - + ); } if (relevancy === 'involvedThread') { const message = `replied in a thread you're involved in`; return ( - - {Authors} + + {` ${message}`} - + ); } if (relevancy === 'postToChannel') { - const message = ` ${postVerb(newest.channel?.type ?? 'chat')} ${postName(newest, plural)} to the channel`; + const message = ` ${postVerb(newest.channel?.type ?? 'chat')} ${postName(newest, plural)}:`; return ( - - {Authors} + + {message} - + ); } if (relevancy === 'flaggedPost') { const message = ` flagged a ${postName(newest)} in your group`; return ( - - {Authors} + + {message} - + ); } if (relevancy === 'flaggedReply') { const message = ` flagged a reply in your group`; return ( - - {Authors} + + {message} - + ); } if (relevancy === 'groupJoinRequest') { - return test; - } - - if (summary.all.length === 1) { - return ( - - - {` ${postVerb(newest.channel?.type ?? 'chat')} a ${postName(newest)}`} - - ); - } - - const uniqueAuthors = new Set(); - summary.all.forEach((event) => uniqueAuthors.add(event.authorId ?? '')); - if (uniqueAuthors.size === 1) { return ( - - - {` ${postVerb(newest.channel?.type ?? 'chat')} ${count} ${postName(newest, count > 1)}`} - + + + {` ${plural ? 'have' : 'has'} requested to join the group`} + ); - } else { - - {`${postVerb(newest.channel?.type ?? 'chat')} ${count} ${postName(newest, count > 1)}`} - ; } } export const SummaryMessage = React.memo(SummaryMessageRaw); -function SummaryMessageWrapper({ children }: PropsWithChildren) { - return ( - - {children} - - ); -} +export const SummaryText = styled(Text, { + size: '$label/m', + trimmed: false, +}); function postName(event: db.ActivityEvent, plural?: boolean) { - const channelType = event.channel?.type ?? 'chat'; - - const name = - channelType === 'gallery' - ? 'block' - : channelType === 'notebook' - ? 'note' - : 'message'; + const name = 'post'; return `${name}${plural ? 's' : ''}`; } @@ -238,12 +157,13 @@ type ActivityRelevancy = | 'postInYourChannel' | 'postToChannel' | 'flaggedPost' - | 'flaggedReply'; + | 'flaggedReply' + | 'groupJoinRequest'; export function getRelevancy( - summary: logic.SourceActivityEvents + summary: logic.SourceActivityEvents, + currentUserId: string ): ActivityRelevancy { - const currentUserId = api.getCurrentUserId(); const newest = summary.newest; if (newest.isMention) { @@ -294,9 +214,72 @@ export function getRelevancy( return 'flaggedReply'; } + if (newest.type === 'group-ask') { + return 'groupJoinRequest'; + } + console.log( 'Unknown relevancy type for activity summary. Defaulting to involvedThread.', summary ); return 'involvedThread'; } + +export function ActivitySummaryAuthorList({ + contactIds, +}: { + contactIds: string[]; +}) { + if (!contactIds.length) { + return null; + } else if (contactIds.length === 1) { + return ( + + ); + } else if (contactIds.length === 2) { + return ( + <> + + {' and '} + + + ); + } else { + const visibleAuthors = contactIds.slice(0, 3); + const overflowCount = contactIds.length - visibleAuthors.length; + return ( + <> + {visibleAuthors.map((contactId, i) => ( + + + {i === visibleAuthors.length - 1 && !overflowCount ? '' : ', '} + {i === visibleAuthors.length - 2 && !overflowCount ? 'and ' : ''} + + ))} + {overflowCount ? `and others` : ''} + + ); + } +} + +export function useActivitySummaryAuthors(summary: logic.SourceActivityEvents) { + return useMemo(() => { + const firstAuthorId = db.isGroupEvent(summary.newest) + ? summary.newest.groupEventUserId + : summary.newest.authorId; + const rawIds = db.isGroupEvent(summary.newest) + ? summary.all.map((p) => p.groupEventUserId) + : summary.all.map((p) => p.authorId); + const validIds = rawIds.filter( + (p): p is string => !!p && p !== firstAuthorId + ); + const secondaryAuthorIds = [...new Set(validIds)]; + return firstAuthorId + ? [firstAuthorId, ...secondaryAuthorIds] + : secondaryAuthorIds; + }, [summary]); +} diff --git a/packages/ui/src/components/Activity/ChannelActivitySummary.tsx b/packages/ui/src/components/Activity/ChannelActivitySummary.tsx deleted file mode 100644 index e1066905c9..0000000000 --- a/packages/ui/src/components/Activity/ChannelActivitySummary.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import * as db from '@tloncorp/shared/dist/db'; -import * as logic from '@tloncorp/shared/dist/logic'; -import * as store from '@tloncorp/shared/dist/store'; -import { useMemo } from 'react'; -import { View, XStack, YStack } from 'tamagui'; - -import { useCalm } from '../../contexts'; -import { getChannelTitle } from '../../utils'; -import { ChannelAvatar, ContactAvatar } from '../Avatar'; -import { ActivitySourceContent } from './ActivitySourceContent'; -import { ActivitySummaryHeader } from './ActivitySummaryHeader'; -import { SummaryMessage } from './ActivitySummaryMessage'; - -export function ChannelActivitySummary({ - summary, - seenMarker, - pressHandler, -}: { - summary: logic.SourceActivityEvents; - seenMarker: number; - pressHandler?: () => void; -}) { - const calm = useCalm(); - const newestPost = summary.newest; - const group = newestPost.group ?? undefined; - const channel: db.Channel | undefined = newestPost.channel ?? undefined; - const modelUnread = - summary.type === 'post' - ? newestPost.channel?.unread ?? null - : newestPost.parent?.threadUnread ?? null; - const { data: unread } = store.useLiveUnread(modelUnread); - const unreadCount = useMemo(() => unread?.count ?? 0, [unread]); - - const newestIsBlockOrNote = - (summary.type === 'post' && newestPost.channel?.type === 'gallery') || - newestPost.channel?.type === 'notebook'; - const title = !channel - ? '' - : channel.type === 'dm' - ? 'DM' - : channel.type === 'groupDm' - ? 'Group chat' - : getChannelTitle(channel, calm.disableNicknames); - - return ( - seenMarker || unreadCount > 0 - ? '$positiveBackground' - : 'unset' - } - borderRadius="$l" - onPress={newestIsBlockOrNote ? undefined : pressHandler} - > - - - - {channel && ( - - {group && ( - - )} - - )} - - - - - - - - ); -} diff --git a/packages/ui/src/components/Activity/GroupActivitySummary.tsx b/packages/ui/src/components/Activity/GroupActivitySummary.tsx deleted file mode 100644 index 467a10cbcb..0000000000 --- a/packages/ui/src/components/Activity/GroupActivitySummary.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import * as logic from '@tloncorp/shared/dist/logic'; -import * as store from '@tloncorp/shared/dist/store'; -import { useMemo } from 'react'; -import { SizableText, View, XStack, YStack } from 'tamagui'; - -import { ContactAvatar, GroupAvatar } from '../Avatar'; -import ContactName from '../ContactName'; -import { ActivitySummaryHeader } from './ActivitySummaryHeader'; - -export function GroupActivitySummary({ - summary, - seenMarker, - pressHandler, -}: { - summary: logic.SourceActivityEvents; - seenMarker: number; - pressHandler?: () => void; -}) { - const newest = summary.newest; - const group = newest.group ?? undefined; - const modelUnread = newest.group?.unread ?? null; - const { data: unread } = store.useLiveGroupUnread(modelUnread); - const unreadCount = useMemo(() => unread?.notifyCount ?? 0, [unread]); - const otherSet = new Set(); - summary.all.forEach((event) => { - if ( - event.groupEventUserId && - event.groupEventUserId !== newest.groupEventUserId - ) { - otherSet.add(event.groupEventUserId); - } - }); - const otherAuthors = Array.from(otherSet); - const plural = summary.all.length > 1 && otherSet.size > 0; - - const NewestAuthor = useMemo(() => { - return ( - - ); - }, [newest.authorId]); - - const Authors = useMemo(() => { - return ( - <> - {NewestAuthor} - {otherAuthors[0] && ( - <> - {`${otherAuthors[1] ? ', ' : ' and '}`} - - - )} - {otherAuthors[1] && ( - <> - {', and '} - - - )} - - ); - }, [newest.authorId, otherAuthors]); - - return ( - seenMarker || unreadCount > 0 - ? '$positiveBackground' - : 'unset' - } - borderRadius="$l" - onPress={pressHandler} - > - - - - {group && ( - - - - )} - - - {Authors} - {` ${plural ? 'have' : 'has'} requested to join the group`} - - - - - - ); -} diff --git a/packages/ui/src/components/Avatar.tsx b/packages/ui/src/components/Avatar.tsx index cf30469313..d1db02e72a 100644 --- a/packages/ui/src/components/Avatar.tsx +++ b/packages/ui/src/components/Avatar.tsx @@ -45,6 +45,11 @@ const AvatarFrame = styled(View, { width: '$3xl', borderRadius: '$xs', }, + '$3.5xl': { + height: '$3.5xl', + width: '$3.5xl', + borderRadius: '$s', + }, $4xl: { height: '$4xl', width: '$4xl', @@ -238,6 +243,7 @@ export const TextAvatar = React.memo(function TextAvatarComponent({ $xl: 12, $2xl: 14, $3xl: 16, + '$3.5xl': 16, $4xl: 16, $5xl: 24, $9xl: 32, diff --git a/packages/ui/src/components/ContentReference/ContentReference.tsx b/packages/ui/src/components/ContentReference/ContentReference.tsx index b9e13d43b3..7c1825295a 100644 --- a/packages/ui/src/components/ContentReference/ContentReference.tsx +++ b/packages/ui/src/components/ContentReference/ContentReference.tsx @@ -108,7 +108,7 @@ export const PostReference = ({ }: ReferenceProps & { channelId: string; post?: db.Post | null }) => { const channelType = getChannelType(channelId); return ( - + {post?.type === 'block' ? ( diff --git a/packages/ui/src/components/PostContent/contentUtils.tsx b/packages/ui/src/components/PostContent/contentUtils.tsx index 81b5a10e17..2179702b11 100644 --- a/packages/ui/src/components/PostContent/contentUtils.tsx +++ b/packages/ui/src/components/PostContent/contentUtils.tsx @@ -2,7 +2,7 @@ import { utils } from '@tloncorp/shared'; import * as api from '@tloncorp/shared/dist/api'; import { Post } from '@tloncorp/shared/dist/db'; import * as ub from '@tloncorp/shared/dist/urbit'; -import { PropsWithChildren, useContext, useMemo } from 'react'; +import { useContext, useMemo } from 'react'; import { createStyledContext } from 'tamagui'; export interface ContentContextProps { @@ -372,3 +372,50 @@ function convertInlineContent(inlines: ub.Inline[]): InlineData[] { }); return nodes; } + +export function prependInline( + content: BlockData[], + inline: InlineData +): BlockData[] { + if (content[0]?.type === 'paragraph') { + return [ + { + ...content[0], + content: [inline, ...content[0].content], + }, + ...content.slice(1), + ]; + } else { + return [ + { + type: 'paragraph', + content: [inline], + }, + ...content, + ]; + } +} + +export function appendInline( + content: BlockData[], + inline: InlineData +): BlockData[] { + const lastBlock = content.at(-1); + if (lastBlock?.type === 'paragraph') { + return [ + ...content.slice(0, -1), + { + ...lastBlock, + content: [...lastBlock.content, inline], + }, + ]; + } else { + return [ + ...content, + { + type: 'paragraph', + content: [inline], + }, + ]; + } +} diff --git a/packages/ui/src/components/ScreenHeader.tsx b/packages/ui/src/components/ScreenHeader.tsx index 066168cb94..077d25f33e 100644 --- a/packages/ui/src/components/ScreenHeader.tsx +++ b/packages/ui/src/components/ScreenHeader.tsx @@ -3,10 +3,11 @@ import { PropsWithChildren, ReactNode } from 'react'; import Animated, { FadeInDown, FadeOutUp } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { isWeb, styled, withStaticProperties } from 'tamagui'; -import { SizableText, View, XStack } from 'tamagui'; +import { View, XStack } from 'tamagui'; import { ChevronLeft } from '../assets/icons'; import { IconButton } from './IconButton'; +import { Text } from './TextV2'; export const ScreenHeaderComponent = ({ children, @@ -69,8 +70,8 @@ const HeaderBackButton = ({ onPress }: { onPress?: () => void }) => { ); }; -const HeaderTitle = styled(SizableText, { - size: '$m', +const HeaderTitle = styled(Text, { + size: '$label/2xl', textAlign: 'left', fontWeight: '500', flex: 1, diff --git a/packages/ui/src/components/Tabs.tsx b/packages/ui/src/components/Tabs.tsx index ab10e412e2..c482a53cab 100644 --- a/packages/ui/src/components/Tabs.tsx +++ b/packages/ui/src/components/Tabs.tsx @@ -1,16 +1,17 @@ import { ReactNode } from 'react'; -import { SizableText, XStack, styled, withStaticProperties } from 'tamagui'; +import { XStack, styled, withStaticProperties } from 'tamagui'; import { useBoundHandler } from './ListItem/listItemUtils'; +import { Text } from './TextV2'; const TabsWrapper = styled(XStack, { width: '100%', }); -const TabTitleComponent = styled(SizableText, { - width: 100, +const TabTitleComponent = styled(Text, { textAlign: 'center', paddingVertical: '$m', + size: '$label/m', variants: { active: { true: { @@ -24,13 +25,17 @@ const TabTitleComponent = styled(SizableText, { }); const TabFrame = styled(XStack, { + flexBasis: 1, flexGrow: 1, justifyContent: 'center', - borderColor: '$primaryText', + alignItems: 'center', + borderBottomWidth: 1, + borderColor: '$shadow', + height: '$4xl', variants: { active: { true: { - borderBottomWidth: 1, + borderColor: '$primaryText', }, }, }, From c11f061b3dccf4526dd23231f7c373e9e8baee4a Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 13 Sep 2024 10:55:14 -0400 Subject: [PATCH 023/157] expose: responsive styling --- desk/app/expose.hoon | 124 +++++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 33 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 724840f982..5a9daee011 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -327,12 +327,11 @@ "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", sans-serif; - font-size: 20px; + font-size: 16px; font-weight: 400; - line-height: 1.8; - max-width: 40em; - margin: 1em auto; - padding: 0 0.5em; + line-height: 1.6; + margin: 1em 1em; + padding: 0; word-wrap: break-word; color: #1a1818; color: var(--text-main); @@ -355,6 +354,41 @@ } } + body.chat { + margin: 0; + } + + body.chat article { + margin: 0 1em; + } + + + @media screen and (min-width: 40rem) { + body { + font-size: 20px; + line-height: 1.8; + } + body:not(.chat) { + max-width: 40rem; + margin: 1em auto; + padding: 0 1em; + } + } + + @media screen and (min-width: 960px) { + body:not(.chat) { + margin: 2em auto 2em 2em; + padding: 0; + } + body.chat { + max-width: 40rem; + margin: 0 auto; + height: 100vh; + display: flex; + align-items: center; + } + } + button { transition: background-color 0.1s linear, border-color 0.1s linear, color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; @@ -1733,9 +1767,30 @@ } footer { - border-top: 0.125em solid #e5e5e5; - border-top: 0.125em solid var(--border); - padding-top: 1em; + position: fixed; + bottom: 1em; + right: 1em; + background: var(--background); + font-size: 0.75em; + padding: 0.5em 0.75em; + border-radius: 0.5em; + border: 0.125em solid var(--border); + box-shadow: 0 0.125em 0.25em var(--selection), 0 0 1em var(--selection); + } + + @media screen and (min-width: 960px) { + footer { + bottom: 2em; + right: 2em; + } + } + + footer a { + display: flex; + gap: 0.5em; + align-items: center; + text-decoration: none; + color: var(--text-main); } @media (prefers-color-scheme: dark) { @@ -1803,20 +1858,13 @@ } } - body.chat { - min-height: 100vh; - margin: 0 auto; - display: flex; - flex-direction: column; - justify-content: center; - } - .cover { border-radius: 1em; margin-bottom: 1em; } .prelude { + font-size: 0.75em; display: flex; flex-direction: column; gap: 0.4em; @@ -1825,16 +1873,32 @@ margin-bottom: 1em; } - .chat .prelude { + body.chat .prelude { + padding: 1em; flex-direction: row-reverse; justify-content: space-between; + align-items: center; + } + + @media screen and (min-width: 960px) { + body.chat .prelude { + margin: 0; + position: fixed; + bottom: 2em; + left: 2em; + border: 0; + background: var(--background); + font-size: 0.75em; + padding: 0.5em 0.75em; + border-radius: 0.5em; + gap: 1em; + } } .prelude .author { display: flex; align-items: center; - gap: 0.4em; - font-size: 0.8em; + gap: 0.5em; font-weight: 500; color: var(--text-muted); } @@ -1852,18 +1916,12 @@ .prelude time { color: var(--text-muted); - font-size: 0.8em; padding: 0; } - footer { - display: flex; - flex-direction: column; - align-items: center; - } - - footer p { - font-size: 0.6em; + article img { + max-width: 100%; + border-radius: 0.5em; } ''' :: @@ -1908,11 +1966,11 @@ :: ++ footer ;footer - ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); - ;p - ; Powered by - ;a/"https://tlon.io":"Tlon" - ; , a communication tool you can trust. + ;a(href "https://tlon.io") + ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); + ;span + ; Powered by Tlon + == == == :: From 7b65b61abce37787dca1036cbaf1e3ee5bd5839a Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 13 Sep 2024 10:59:33 -0400 Subject: [PATCH 024/157] remove missing session header dimming, add hook for tracking session + start sync, update home screen to show that status --- packages/app/features/top/ChatListScreen.tsx | 21 +++++++-- packages/shared/src/store/session.ts | 48 +++++++++++++++++++- packages/shared/src/store/sync.ts | 9 ++-- packages/ui/src/components/ScreenHeader.tsx | 14 +----- 4 files changed, 70 insertions(+), 22 deletions(-) diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx index cec119e78a..7597975975 100644 --- a/packages/app/features/top/ChatListScreen.tsx +++ b/packages/app/features/top/ChatListScreen.tsx @@ -98,6 +98,21 @@ export default function ChatListScreen({ const currentUser = useCurrentUserId(); + const connStatus = store.useConnectionStatus(); + const notReadyMessage: string | null = useMemo(() => { + // if not fully connected yet, show status + if (connStatus !== 'Connected') { + return `${connStatus}...`; + } + + // if still loading the screen data, show loading + if (!chats || (!chats.unpinned.length && !chats.pinned.length)) { + return 'Loading...'; + } + + return null; + }, [connStatus, chats]); + const resolvedChats = useMemo(() => { return { pinned: chats?.pinned ?? [], @@ -240,11 +255,7 @@ export default function ChatListScreen({ > {chats && chats.unpinned.length ? ( diff --git a/packages/shared/src/store/session.ts b/packages/shared/src/store/session.ts index de45aa3306..2afb72340d 100644 --- a/packages/shared/src/store/session.ts +++ b/packages/shared/src/store/session.ts @@ -2,9 +2,10 @@ import { useSyncExternalStore } from 'react'; export type Session = { startTime: number }; -type SessionListener = (session: Session | null) => void; - +// Session — time when subscriptions were first initialized and we can assume +// all new events have been heard let session: Session | null = null; +type SessionListener = (session: Session | null) => void; const sessionListeners: SessionListener[] = []; export function getSession() { @@ -26,3 +27,46 @@ function subscribeToSession(listener: SessionListener) { export function useCurrentSession() { return useSyncExternalStore(subscribeToSession, getSession); } + +// Syncing — whether our start sync logic is currently running +let isSyncing: boolean = false; +type SyncListener = (syncing: boolean) => void; +const syncListeners: SyncListener[] = []; + +export function getSyncing() { + return isSyncing; +} + +export function updateIsSyncing(newValue: boolean) { + console.log(`updating is syncing`, newValue); + isSyncing = newValue; + syncListeners.forEach((listener) => listener(newValue)); +} + +function subscribeToIsSyncing(listener: SyncListener) { + syncListeners.push(listener); + return () => { + syncListeners.splice(syncListeners.indexOf(listener), 1); + }; +} + +export function useSyncing() { + return useSyncExternalStore(subscribeToIsSyncing, getSyncing); +} + +export function useConnectionStatus() { + const currentSession = useCurrentSession(); + const syncing = useSyncing(); + + console.log(`con status render`, currentSession, syncing); + + if (!currentSession) { + return 'Connecting'; + } + + if (syncing) { + return 'Syncing'; + } + + return 'Connected'; +} diff --git a/packages/shared/src/store/sync.ts b/packages/shared/src/store/sync.ts index 0ffa7b0b4e..c243a07a2f 100644 --- a/packages/shared/src/store/sync.ts +++ b/packages/shared/src/store/sync.ts @@ -13,13 +13,13 @@ import { } from '../store/useActivityFetchers'; import { ErrorReporter } from './errorReporting'; import { useLureState } from './lure'; -import { updateSession } from './session'; +import { updateIsSyncing, updateSession } from './session'; import { SyncCtx, SyncPriority, syncQueue } from './syncQueue'; import { addToChannelPosts, clearChannelPostsQueries } from './useChannelPosts'; export { SyncPriority, syncQueue } from './syncQueue'; -const logger = createDevLogger('sync', false); +const logger = createDevLogger('sync', true); // Used to track latest post we've seen for each channel. // Updated when: @@ -1013,6 +1013,7 @@ export const handleDiscontinuity = async () => { }; export const syncStart = async (alreadySubscribed?: boolean) => { + updateIsSyncing(true); const reporter = new ErrorReporter('sync start', logger); reporter.log(`sync start running${alreadySubscribed ? ' (recovery)' : ''}`); @@ -1088,13 +1089,15 @@ export const syncStart = async (alreadySubscribed?: boolean) => { }), ]; - Promise.all(lowPriorityPromises) + await Promise.all(lowPriorityPromises) .then(() => { reporter.log(`finished low priority sync`); }) .catch((e) => { reporter.report(e); }); + + updateIsSyncing(false); }; export const setupHighPrioritySubscriptions = async (ctx?: SyncCtx) => { diff --git a/packages/ui/src/components/ScreenHeader.tsx b/packages/ui/src/components/ScreenHeader.tsx index 077d25f33e..50cdb864d3 100644 --- a/packages/ui/src/components/ScreenHeader.tsx +++ b/packages/ui/src/components/ScreenHeader.tsx @@ -1,4 +1,3 @@ -import { useCurrentSession } from '@tloncorp/shared'; import { PropsWithChildren, ReactNode } from 'react'; import Animated, { FadeInDown, FadeOutUp } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -20,7 +19,6 @@ export const ScreenHeaderComponent = ({ rightControls?: ReactNode | null; }>) => { const { top } = useSafeAreaInsets(); - const currentSession = useCurrentSession(); return ( @@ -32,11 +30,7 @@ export const ScreenHeaderComponent = ({ > {typeof title === 'string' ? ( isWeb ? ( - - {title} - + {title} ) : ( - - {title} - + {title} ) ) : ( From 09c614b31c454e82a44b762d40ed47059381f6e2 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 13 Sep 2024 11:03:41 -0400 Subject: [PATCH 025/157] grammar --- packages/shared/src/store/session.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/shared/src/store/session.ts b/packages/shared/src/store/session.ts index 2afb72340d..c43d272647 100644 --- a/packages/shared/src/store/session.ts +++ b/packages/shared/src/store/session.ts @@ -2,8 +2,8 @@ import { useSyncExternalStore } from 'react'; export type Session = { startTime: number }; -// Session — time when subscriptions were first initialized and we can assume -// all new events have been heard +// Session — time when subscriptions were first initialized after which we can assume +// all new events will be heard let session: Session | null = null; type SessionListener = (session: Session | null) => void; const sessionListeners: SessionListener[] = []; @@ -28,7 +28,7 @@ export function useCurrentSession() { return useSyncExternalStore(subscribeToSession, getSession); } -// Syncing — whether our start sync logic is currently running +// Syncing — whether our initial fetching logic is currently running let isSyncing: boolean = false; type SyncListener = (syncing: boolean) => void; const syncListeners: SyncListener[] = []; From 7824575de119f5411699417f01bf91c954069ec8 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 13 Sep 2024 11:04:19 -0400 Subject: [PATCH 026/157] comments --- packages/shared/src/store/session.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/shared/src/store/session.ts b/packages/shared/src/store/session.ts index c43d272647..368330a35d 100644 --- a/packages/shared/src/store/session.ts +++ b/packages/shared/src/store/session.ts @@ -38,7 +38,6 @@ export function getSyncing() { } export function updateIsSyncing(newValue: boolean) { - console.log(`updating is syncing`, newValue); isSyncing = newValue; syncListeners.forEach((listener) => listener(newValue)); } @@ -58,8 +57,6 @@ export function useConnectionStatus() { const currentSession = useCurrentSession(); const syncing = useSyncing(); - console.log(`con status render`, currentSession, syncing); - if (!currentSession) { return 'Connecting'; } From f07f7a723694f45ee292ff3e69267342aca11b28 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 13 Sep 2024 11:05:01 -0400 Subject: [PATCH 027/157] turn off logger --- packages/shared/src/store/sync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/store/sync.ts b/packages/shared/src/store/sync.ts index c243a07a2f..9f403e0533 100644 --- a/packages/shared/src/store/sync.ts +++ b/packages/shared/src/store/sync.ts @@ -19,7 +19,7 @@ import { addToChannelPosts, clearChannelPostsQueries } from './useChannelPosts'; export { SyncPriority, syncQueue } from './syncQueue'; -const logger = createDevLogger('sync', true); +const logger = createDevLogger('sync', false); // Used to track latest post we've seen for each channel. // Updated when: From 02f12c82c6f6c40844ac8824ec4f140bd6c465a0 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 13 Sep 2024 11:06:05 -0400 Subject: [PATCH 028/157] expose: hide empty paragraph elements --- desk/app/expose.hoon | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 5a9daee011..102c7cc897 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -1919,6 +1919,10 @@ padding: 0; } + article p:empty { + display: none; + } + article img { max-width: 100%; border-radius: 0.5em; From 2f751106686ed839f10ea32101e24288b19f9abe Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 13 Sep 2024 15:50:00 +0000 Subject: [PATCH 029/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 6f46277a6d..5e72d62e33 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v4.egepg.b3pvn.bj8v4.tj86r.1iml7.glob' 0v4.egepg.b3pvn.bj8v4.tj86r.1iml7] + glob-http+['https://bootstrap.urbit.org/glob-0v4p5lr.n2m0g.2bld9.6mhs7.puqn3.glob' 0v4p5lr.n2m0g.2bld9.6mhs7.puqn3] base+'groups' version+[6 3 0] website+'https://tlon.io' From 7cd80cdf37dae33607155ccd8d4534732b18a86a Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 13 Sep 2024 15:50:34 +0000 Subject: [PATCH 030/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 5e72d62e33..803eb75e8b 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v4p5lr.n2m0g.2bld9.6mhs7.puqn3.glob' 0v4p5lr.n2m0g.2bld9.6mhs7.puqn3] + glob-http+['https://bootstrap.urbit.org/glob-0v2p6mc.ao9o7.5i06p.a9bqu.4r2k5.glob' 0v2p6mc.ao9o7.5i06p.a9bqu.4r2k5] base+'groups' version+[6 3 0] website+'https://tlon.io' From 4a33fd544a96508ba3516762205a8ecdf6c78dc0 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 13 Sep 2024 11:54:49 -0400 Subject: [PATCH 031/157] minor fix: remove guard on rendering invite group icon --- packages/ui/src/components/AppInviteDisplay.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/components/AppInviteDisplay.tsx b/packages/ui/src/components/AppInviteDisplay.tsx index e962f8f1eb..193a59d14a 100644 --- a/packages/ui/src/components/AppInviteDisplay.tsx +++ b/packages/ui/src/components/AppInviteDisplay.tsx @@ -32,12 +32,10 @@ function AppInviteDisplayRaw({ // provider needed to support calm settings usage down the tree - {invitedGroupIconImageUrl ? ( - - ) : null} + Join {invitedGroupTitle ?? invitedGroupId} From 9131244cb5849634356adff1beb962607b3b9224 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 13 Sep 2024 13:49:58 -0400 Subject: [PATCH 032/157] expose: center content --- desk/app/expose.hoon | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 102c7cc897..378732c29e 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -362,6 +362,9 @@ margin: 0 1em; } + body.chat header > h1 { + padding: 1rem 1rem 0; + } @media screen and (min-width: 40rem) { body { @@ -383,9 +386,12 @@ body.chat { max-width: 40rem; margin: 0 auto; - height: 100vh; + min-height: 100vh; display: flex; + flex-direction: column; align-items: center; + justify-content: center; + overflow: auto; } } From e0a1149f2a8ac0cbdd1688f786e12b1118dd4cbf Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 13 Sep 2024 15:01:20 -0400 Subject: [PATCH 033/157] fix fallbackurl and hardcode priority token --- .../tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx | 2 +- packages/shared/src/store/lure.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx index ae86e1672a..472cc6c8d9 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx @@ -36,7 +36,7 @@ export const InventoryCheckScreen = ({ try { const { enabled } = await getHostingAvailability({ lure, - priorityToken, + priorityToken: 'mobile', }); if (enabled) { navigation.navigate('SignUpEmail', { lure, priorityToken }); diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index f595b7667b..b2b6a3856d 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -200,7 +200,7 @@ export const useLureState = create((set, get) => ({ }; deepLinkUrl = await createDeepLink({ - fallbackUrl: 'https://en.wikipedia.org/wiki/Construction', + fallbackUrl: url, type: 'lure', path: flag, branchDomain, From 18817e52e6483ee1c5eec3ec8e7348fbfdeffd99 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 13 Sep 2024 15:19:55 -0400 Subject: [PATCH 034/157] v1 interactions --- packages/shared/src/store/lure.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index b2b6a3856d..1da5c644de 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -165,7 +165,7 @@ export const useLureState = create((set, get) => ({ async () => scry({ app: 'reel', - path: `/metadata/${flag}`, + path: `/v1/metadata/${flag}`, }), prevLure?.metadata ), @@ -292,7 +292,7 @@ export function useLureLinkChecked(url: string | undefined, enabled: boolean) { queryKey: ['lure-check', url], queryFn: async () => subscribeOnce( - { app: 'grouper', path: `/check-link/${pathEncodedUrl}` }, + { app: 'grouper', path: `/v1/check-link/${pathEncodedUrl}` }, 4500 ), enabled: enabled && Boolean(url), From adde000f4330af7e0a2e19bdfcb295f5eb9fe3da Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 16 Sep 2024 15:18:22 +0000 Subject: [PATCH 035/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 803eb75e8b..ea4e69ad06 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v2p6mc.ao9o7.5i06p.a9bqu.4r2k5.glob' 0v2p6mc.ao9o7.5i06p.a9bqu.4r2k5] + glob-http+['https://bootstrap.urbit.org/glob-0vje6d9.dk50f.2e9m3.htalj.s9dl9.glob' 0vje6d9.dk50f.2e9m3.htalj.s9dl9] base+'groups' version+[6 3 0] website+'https://tlon.io' From 15c0a08e8b5100bb0b6a6f53c2cf3f111c07c5ad Mon Sep 17 00:00:00 2001 From: James Acklin Date: Mon, 16 Sep 2024 13:44:48 -0400 Subject: [PATCH 036/157] MessageInput: visible but gray as default state --- .../ui/src/components/MessageInput/MessageInputBase.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/MessageInput/MessageInputBase.tsx b/packages/ui/src/components/MessageInput/MessageInputBase.tsx index f81320509e..38bdd0b838 100644 --- a/packages/ui/src/components/MessageInput/MessageInputBase.tsx +++ b/packages/ui/src/components/MessageInput/MessageInputBase.tsx @@ -103,8 +103,8 @@ export const MessageInputContainer = ({ onSelectMention={onSelectMention} /> ) : ( - + + {children} + + + + + + ); + } + return ( ) : null} - {isEditing ? ( - - - - ) : null} {canUpload && showAttachmentButton ? ( - + ) : null} @@ -141,14 +182,8 @@ export const MessageInputContainer = ({ {disableSend ? null : ( - ) : ( - - ) - } + onPress={onPressSend} + icon={} /> )} @@ -156,7 +191,7 @@ export const MessageInputContainer = ({ From 5e5a30fdba1413ea66676f3bd7841de7651a3716 Mon Sep 17 00:00:00 2001 From: fang Date: Mon, 16 Sep 2024 22:02:02 +0200 Subject: [PATCH 038/157] channels: support existence check on /v2 scries Kinda weird to not have %u on paths we have %x for. --- desk/app/channels.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/app/channels.hoon b/desk/app/channels.hoon index 2f1dbf70f1..9ac89c740c 100644 --- a/desk/app/channels.hoon +++ b/desk/app/channels.hoon @@ -752,7 +752,7 @@ =/ =ship (slav %p ship.pole) (ca-peek:(ca-abed:ca-core kind.pole ship name.pole) rest.pole v.pole) :: - [%u ?(%v0 %v1) =kind:c ship=@ name=@ ~] + [%u ?(%v0 %v1 %v2) =kind:c ship=@ name=@ ~] =/ =ship (slav %p ship.pole) ``loob+!>((~(has by v-channels) kind.pole ship name.pole)) :: From 218f0a216b9a491619612d67f4a5b211404bdb1f Mon Sep 17 00:00:00 2001 From: fang Date: Mon, 16 Sep 2024 22:04:58 +0200 Subject: [PATCH 039/157] expose: properly save responses into eyre cache We move a bunch of code around to make this easier/nicer. --- desk/app/expose.hoon | 313 ++++++++++++++++++++++++------------------- 1 file changed, 175 insertions(+), 138 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 378732c29e..4086d2f568 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -22,149 +22,39 @@ :: +$ card card:agent:gall :: -++ hutils :: http request utils - ::NOTE most of the below are also available in /lib/server, but we - :: reimplement them here for independence's sake +++ e |% - +$ order [id=@ta inbound-request:eyre] - +$ query [trail args=(list [key=@t value=@t])] - +$ trail [ext=(unit @ta) site=(list @t)] - +$ reply - $% [%page bod=manx] :: html page - [%xtra hed=header-list:http bod=manx] :: html page w/ heads - == - :: - ++ purse :: url cord to query - |= url=@t - ^- query - (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) - :: - ++ press :: manx to octs - (cork en-xml:html as-octt:mimes:html) - :: - ++ paint :: render response into payload - |= =reply - ^- simple-payload:http - ?- -.reply - %page [[200 ['content-type' 'text/html']~] `(press bod.reply)] - %xtra =? hed.reply ?=(~ (get-header:http 'content-type' hed.reply)) - ['content-type'^'text/html' hed.reply] - [[200 hed.reply] `(press bod.reply)] - == - :: - ++ spout :: build full response cards - |= [eyre-id=@ta simple-payload:http] - ^- (list card) - =/ =path /http-response/[eyre-id] - :~ [%give %fact ~[path] [%http-response-header !>(response-header)]] - [%give %fact ~[path] [%http-response-data !>(data)]] - [%give %kick ~[path] ~] - == - -- --- -:: -%- agent:dbug -%+ verb | -^- agent:gall -:: -=| state-0 -=* state - -|_ =bowl:gall -+* this . -++ on-init - ^- (quip card _this) - :_ this - [%pass /eyre/connect %arvo %e %connect [~ /expose] dap.bowl]~ -:: -++ on-save !>(state) -++ on-load - |= ole=vase - ^- (quip card _this) - [~ this(state !<(state-0 ole))] -:: -++ on-poke - |= [=mark =vase] - ^- (quip card _this) - ?+ mark !! - %noun - ?+ q.vase !! - [?(%show %hide) *] - =+ !<(act=action vase) - =. open - ?- -.act - %show (~(put in open) (parse:c path.act)) ::TODO populate cache - %hide (~(del in open) (parse:c path.act)) ::TODO update the cache w/ 404 - == - [~ this] - == - :: - %handle-http-request - =+ !<([rid=@ta inbound-request:eyre] vase) - :_ this - =; payload=simple-payload:http - ::TODO re-enable caching - :: :_ - (spout:hutils rid payload) - :: if we handled a request here, make sure it's cached for next time - :: - :: [%pass /eyre/cache %arvo %e %set-response url.request `[| %payload payload]] - =/ ref=(unit cite:c) - (rush url.request (sear purse:c ;~(pfix (jest '/expose') stap))) - ?~ ref - [[400 ~] `(as-octs:mimes:html 'bad request')] - :: - =; bod=(unit manx) - ?~ bod [[404 ~] `(as-octs:mimes:html 'not found')] - (paint:hutils %page u.bod) - :: - ?. (~(has in open) u.ref) - ~ - ?. ?=(%chan -.u.ref) - ~ - ::TODO the whole "deconstruct the ref path" situation is horrendous - ?. ?=([?(%msg %note %curio) @ ~] wer.u.ref) - ~ - =/ msg=(unit post:d) - :- ~ ::TODO we want to do existence checks first though... - .^ post:d - %gx - (scot %p our.bowl) - %channels - (scot %da now.bowl) - :: - =, u.ref - /v2/[p.nest]/(scot %p p.q.nest)/[q.q.nest]/posts/post/(scot %ud (rash i.t.wer dum:ag))/channel-post-2 - == - ?~ msg - ~ - :: + ++ render-post + |= [our=@p now=@da] + |= [=nest:g:c msg=post:d] + ^- (unit manx) =/ aco=(unit contact:co) - =/ base=path /(scot %p our.bowl)/contacts/(scot %da now.bowl) + =/ base=path /(scot %p our)/contacts/(scot %da now) ?. .^(? %gu (weld base /$)) ~ =+ .^(rol=rolodex:co %gx (weld base /all/contact-rolodex)) - ?~ for=(~(get by rol) author.u.msg) + ?~ for=(~(get by rol) author.msg) ~ ?. ?=([[@ ^] *] u.for) ~ `con.for.u.for :: ::TODO if we render replies then we can "unroll" whole chat threads too (: - |^ ?+ p.nest.u.ref ~ + |^ ?+ p.nest ~ %chat - ?> ?=(%chat -.kind-data.u.msg) + ?> ?=(%chat -.kind-data.msg) =/ title=tape - (trip (rap 3 (turn (first-inline content.u.msg) flatten-inline:u))) + (trip (rap 3 (turn (first-inline content.msg) flatten-inline:u))) %- some %: build "chat" (heads title ~) [chat-prelude]~ - (story:en-manx:u content.u.msg) + (story:en-manx:u content.msg) == :: %diary - ?> ?=(%diary -.kind-data.u.msg) - =* kd kind-data.u.msg + ?> ?=(%diary -.kind-data.msg) + =* kd kind-data.msg =/ title=tape (trip title.kd) %- some %: build "diary" @@ -174,14 +64,14 @@ :- ;img.cover@"{(trip image.kd)}"(alt "Cover image"); (diary-prelude title) :: - (story:en-manx:u content.u.msg) + (story:en-manx:u content.msg) == :: %heap - ?> ?=(%heap -.kind-data.u.msg) + ?> ?=(%heap -.kind-data.msg) =/ title=tape - ?: &(?=(^ title.kind-data.u.msg) !=('' u.title.kind-data.u.msg)) - (trip u.title.kind-data.u.msg) + ?: &(?=(^ title.kind-data.msg) !=('' u.title.kind-data.msg)) + (trip u.title.kind-data.msg) ::NOTE could flatten the first-inline, but we don't. showing that :: as both h1 and content is strange "" @@ -189,7 +79,7 @@ %: build "chat" (heads ?:(=("" title) "Gallery item" title) ~) (heap-prelude title) - (story:en-manx:u content.u.msg) + (story:en-manx:u content.msg) == == :: @@ -361,11 +251,11 @@ body.chat article { margin: 0 1em; } - + body.chat header > h1 { padding: 1rem 1rem 0; } - + @media screen and (min-width: 40rem) { body { font-size: 20px; @@ -1948,16 +1838,16 @@ :: ;meta(name "robots", content "noindex, nofollow, noimageindex"); :: - ::TODO make sure this is the right/new app id + ::REVIEW make sure this is the right/new app id ;meta(property "apple-itunes-app", content "app-id=6451392109"); ::NOTE at the time of writing, android supports no such thing :: - ::TODO could get smarter about description, preview image, etc + ::TODO could get even smarter about description, preview image, etc ;meta(property "og:title", content title); ;meta(property "twitter:title", content title); ;meta(property "og:site_name", content "Tlon"); ;meta(property "og:type", content "article"); - ;meta(property "og:article:author:username", content (scow %p author.u.msg)); + ;meta(property "og:article:author:username", content (scow %p author.msg)); :: ;* ?~ img :_ ~ @@ -1988,7 +1878,7 @@ ^- manx ;div.prelude ;div.published - ;+ (render-datetime sent.u.msg) + ;+ (render-datetime sent.msg) == ;+ author-node == @@ -1999,7 +1889,7 @@ :~ ;h1:"{title}" ;div.prelude ;div.published - ;+ (render-datetime sent.u.msg) + ;+ (render-datetime sent.msg) == ;+ author-node == @@ -2013,14 +1903,14 @@ [-]~ ;div.prelude ;div.published - ;+ (render-datetime sent.u.msg) + ;+ (render-datetime sent.msg) == ;+ author-node == :: ++ author-node ^- manx - =* author author.u.msg + =* author author.msg ;div.author ;div.avatar ;+ @@ -2098,6 +1988,153 @@ == == -- + :: + ++ post-from-cite + |= [our=@p now=@da ref=cite:c] + ^- (unit [=nest:g:c =post:d]) + ?. ?=(%chan -.ref) + ~ + ::TODO the whole "deconstruct the ref path" situation is horrendous + ?. ?=([?(%msg %note %curio) @ ~] wer.ref) + ~ + =, ref + =/ base=path + %+ weld + /(scot %p our)/channels/(scot %da now) + /v2/[p.nest]/(scot %p p.q.nest)/[q.q.nest] + ?. .^(? %gu base) ~ + :+ ~ nest + .^ post:d %gx + %+ weld base + /posts/post/(scot %ud (rash i.t.wer dum:ag))/channel-post-2 + == + -- +:: +++ hutils :: http request utils + ::NOTE most of the below are also available in /lib/server, but we + :: reimplement them here for independence's sake + |% + +$ order [id=@ta inbound-request:eyre] + +$ query [trail args=(list [key=@t value=@t])] + +$ trail [ext=(unit @ta) site=(list @t)] + +$ reply + $% [%page bod=manx] :: html page + [%xtra hed=header-list:http bod=manx] :: html page w/ heads + == + :: + ++ purse :: url cord to query + |= url=@t + ^- query + (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) + :: + ++ press :: manx to octs + (cork en-xml:html as-octt:mimes:html) + :: + ++ paint :: render response into payload + |= =reply + ^- simple-payload:http + ?- -.reply + %page [[200 ['content-type' 'text/html']~] `(press bod.reply)] + %xtra =? hed.reply ?=(~ (get-header:http 'content-type' hed.reply)) + ['content-type'^'text/html' hed.reply] + [[200 hed.reply] `(press bod.reply)] + == + :: + ++ spout :: build full response cards + |= [eyre-id=@ta simple-payload:http] + ^- (list card) + =/ =path /http-response/[eyre-id] + :~ [%give %fact ~[path] [%http-response-header !>(response-header)]] + [%give %fact ~[path] [%http-response-data !>(data)]] + [%give %kick ~[path] ~] + == + :: + ++ store :: set cache entry + |= [url=@t entry=(unit cache-entry:eyre)] + ^- card + [%pass /eyre/cache %arvo %e %set-response url entry] + -- +-- +:: +%- agent:dbug +%+ verb | +^- agent:gall +:: +=| state-0 +=* state - +|_ =bowl:gall ++* this . +++ on-init + ^- (quip card _this) + :_ this + [%pass /eyre/connect %arvo %e %connect [~ /expose] dap.bowl]~ +:: +++ on-save !>(state) +++ on-load + |= ole=vase + ^- (quip card _this) + [~ this(state !<(state-0 ole))] +:: +++ on-poke + |= [=mark =vase] + ^- (quip card _this) + ?+ mark !! ::TODO support json pokes + %noun + ?+ q.vase !! + [?(%show %hide) *] + =+ !<(act=action vase) + ?- -.act + %show + =/ ref=cite:c + (parse:c path.act) + =/ msg=(unit [=nest:g:c =post:d]) + (post-from-cite:e our.bowl now.bowl ref) + ?> ?=(^ msg) + =/ pag=(unit manx) + ((render-post:e [our now]:bowl) u.msg) + ?> ?=(^ pag) + =. open (~(put in open) ref) + =/ url=@t (cat 3 '/expose' (spat path.act)) + :_ this :_ ~ + %+ store:hutils url + `[| %payload (paint:hutils %page u.pag)] + :: + %hide + =/ ref=cite:c + (parse:c path.act) + ?. (~(has in open) ref) + [~ this] + =. open (~(del in open) ref) + =/ url=@t (cat 3 '/expose' (spat path.act)) + :_ this :_ ~ + %+ store:hutils url + :^ ~ | %payload + [[404 ~] `(as-octs:mimes:html 'not found')] + == + == + :: + %handle-http-request + =+ !<([rid=@ta inbound-request:eyre] vase) + :_ this + =; payload=simple-payload:http + :_ (spout:hutils rid payload) + :: if we handled a request here, make sure it's cached for next time + :: + [%pass /eyre/cache %arvo %e %set-response url.request `[| %payload payload]] + =/ ref=(unit cite:c) + (rush url.request (sear purse:c ;~(pfix (jest '/expose') stap))) + ?~ ref + [[400 ~] `(as-octs:mimes:html 'bad request')] + :: + =; bod=(unit manx) + ?~ bod [[404 ~] `(as-octs:mimes:html 'not found')] + (paint:hutils %page u.bod) + :: + ?. (~(has in open) u.ref) + ~ + %+ biff + (post-from-cite:e our.bowl now.bowl u.ref) + (render-post:e [our now]:bowl) == :: ++ on-watch @@ -2117,7 +2154,7 @@ :: ++ on-leave |=(* [~ this]) ++ on-agent |=(* [~ this]) -++ on-peek |=(* ~) +++ on-peek |=(* ~) ::TODO support scrying to see if it's published :: ++ on-fail |= [=term =tang] From 995612688f95c5a59d9374b27fb115df9eb86e80 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Mon, 16 Sep 2024 16:12:43 -0400 Subject: [PATCH 040/157] Members: adds padding to the bottom of the list --- packages/ui/src/components/GroupMembersScreenView.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/ui/src/components/GroupMembersScreenView.tsx b/packages/ui/src/components/GroupMembersScreenView.tsx index 805ce80c0e..397ea41707 100644 --- a/packages/ui/src/components/GroupMembersScreenView.tsx +++ b/packages/ui/src/components/GroupMembersScreenView.tsx @@ -2,6 +2,7 @@ import * as db from '@tloncorp/shared/dist/db'; import { GroupPrivacy } from '@tloncorp/shared/dist/db/schema'; import { useCallback, useMemo, useState } from 'react'; import { SectionList } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { View, getTokenValue } from 'tamagui'; import { ContactList } from './ContactList'; @@ -37,6 +38,7 @@ export function GroupMembersScreenView({ onPressAcceptJoinRequest: (contactId: string) => void; onPressRejectJoinRequest: (contactId: string) => void; }) { + const { bottom } = useSafeAreaInsets(); const [selectedContact, setSelectedContact] = useState(null); const contacts = useMemo( () => @@ -179,6 +181,7 @@ export function GroupMembersScreenView({ initialNumToRender={11} contentContainerStyle={{ paddingHorizontal: getTokenValue('$l', 'size'), + paddingBottom: bottom, }} windowSize={2} renderItem={renderItem} From 20ec562af43df27cefc1edd8a70a71eb198e2b2d Mon Sep 17 00:00:00 2001 From: fang Date: Mon, 16 Sep 2024 22:35:09 +0200 Subject: [PATCH 041/157] expose: support checking ref path for exposure Through scry. --- desk/app/expose.hoon | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 4086d2f568..b7b2e43768 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -2152,9 +2152,16 @@ [~ this] ::TODO print if not successful == :: +++ on-peek + |= =path + ^- (unit (unit cage)) + ?+ path [~ ~] + [?(%x %u) %show *] + ``loob+!>((~(has in open) (parse:c t.t.path))) + == +:: ++ on-leave |=(* [~ this]) ++ on-agent |=(* [~ this]) -++ on-peek |=(* ~) ::TODO support scrying to see if it's published :: ++ on-fail |= [=term =tang] From b2a023aec41d442429b7ce166be085c9fb1ab68c Mon Sep 17 00:00:00 2001 From: fang Date: Mon, 16 Sep 2024 22:45:46 +0200 Subject: [PATCH 042/157] expose: support poking with json Minimally viable inline conversions. --- desk/app/expose.hoon | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index b7b2e43768..aee43eb2d6 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -2078,7 +2078,7 @@ ++ on-poke |= [=mark =vase] ^- (quip card _this) - ?+ mark !! ::TODO support json pokes + ?+ mark !! %noun ?+ q.vase !! [?(%show %hide) *] @@ -2112,6 +2112,14 @@ [[404 ~] `(as-octs:mimes:html 'not found')] == == + :: + %json + :: we intentionally slum it with in-agent conversions for now + :: + =+ !<(=json vase) + =- $(mark %noun, vase !>(`action`-)) + %. json + (of show+pa hide+pa ~):dejs:format :: %handle-http-request =+ !<([rid=@ta inbound-request:eyre] vase) From dbe38f5eda26beae1014b33d73d74d4d484eb9b2 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Mon, 16 Sep 2024 17:55:03 -0400 Subject: [PATCH 043/157] use signup params hook --- .../src/hooks/useDeepLinkListener.ts | 38 ++++++++++--------- .../Onboarding/InventoryCheckScreen.tsx | 21 ++++------ .../screens/Onboarding/ReserveShipScreen.tsx | 12 +++--- .../screens/Onboarding/SignUpEmailScreen.tsx | 34 +++++++++-------- .../Onboarding/SignUpPasswordScreen.tsx | 18 ++++++--- apps/tlon-mobile/src/types.ts | 8 ++-- packages/app/contexts/branch.tsx | 16 ++++++++ packages/app/lib/hostingApi.ts | 2 +- 8 files changed, 87 insertions(+), 62 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index b61606fbaf..f4585db53b 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -1,6 +1,6 @@ import { useNavigation } from '@react-navigation/native'; import type { NavigationProp } from '@react-navigation/native'; -import { useBranch } from '@tloncorp/app/contexts/branch'; +import { useBranch, useSignupParams } from '@tloncorp/app/contexts/branch'; import { useShip } from '@tloncorp/app/contexts/ship'; import { inviteShipWithLure } from '@tloncorp/app/lib/hostingApi'; import { trackError } from '@tloncorp/app/utils/posthog'; @@ -12,14 +12,16 @@ import { RootStackParamList } from '../types'; export const useDeepLinkListener = () => { const navigation = useNavigation>(); const { ship } = useShip(); - const { lure, deepLinkPath, clearLure, clearDeepLink } = useBranch(); + const signupParams = useSignupParams(); + const { clearLure } = useBranch(); // If lure is present, invite it and mark as handled useEffect(() => { - if (ship && lure) { + if (ship && signupParams.lureId) { (async () => { try { - await inviteShipWithLure({ ship, lure: lure.id }); + console.log(`bl: inviting ship with lure`, ship, signupParams.lureId); + await inviteShipWithLure({ ship, lure: signupParams.lureId }); Alert.alert( '', 'Your invitation to the group is on its way. It will appear in the Groups list.', @@ -44,20 +46,20 @@ export const useDeepLinkListener = () => { clearLure(); })(); } - }, [ship, lure, clearLure]); + }, [ship, signupParams, clearLure]); // If deep link clicked, broadcast that navigation update to the webview and mark as handled - useEffect(() => { - // TODO: hook up deep links without webview - // if (deepLinkPath && webviewContext.appLoaded) { - // console.debug( - // '[useDeepLinkListener] Setting webview path:', - // deepLinkPath - // ); - // webviewContext.setGotoPath(deepLinkPath); - // const tab = parseActiveTab(deepLinkPath) ?? 'Groups'; - // navigation.navigate(tab, { screen: 'Webview' }); - // clearDeepLink(); - // } - }, [deepLinkPath, navigation, clearDeepLink]); + // useEffect(() => { + // TODO: hook up deep links without webview + // if (deepLinkPath && webviewContext.appLoaded) { + // console.debug( + // '[useDeepLinkListener] Setting webview path:', + // deepLinkPath + // ); + // webviewContext.setGotoPath(deepLinkPath); + // const tab = parseActiveTab(deepLinkPath) ?? 'Groups'; + // navigation.navigate(tab, { screen: 'Webview' }); + // clearDeepLink(); + // } + // }, [deepLinkPath, navigation, clearDeepLink]); }; diff --git a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx index 472cc6c8d9..704a145d7b 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx @@ -12,6 +12,7 @@ import { XStack, YStack, } from '@tloncorp/ui'; +import { useBranch, useSignupParams } from 'packages/app/contexts/branch'; import { useState } from 'react'; import { Image } from 'react-native'; @@ -19,15 +20,8 @@ import type { OnboardingStackParamList } from '../../types'; type Props = NativeStackScreenProps; -export const InventoryCheckScreen = ({ - navigation, - route: { - params: { - lure = DEFAULT_LURE, - priorityToken = DEFAULT_PRIORITY_TOKEN, - } = {}, - }, -}: Props) => { +export const InventoryCheckScreen = ({ navigation }: Props) => { + const signupParams = useSignupParams(); const [isChecking, setIsChecking] = useState(false); const checkAvailability = async () => { @@ -35,13 +29,14 @@ export const InventoryCheckScreen = ({ try { const { enabled } = await getHostingAvailability({ - lure, - priorityToken: 'mobile', + lure: signupParams.lureId, + priorityToken: signupParams.priorityToken, }); if (enabled) { - navigation.navigate('SignUpEmail', { lure, priorityToken }); + console.log(`bl: inv check lure`, signupParams.lureId); + navigation.navigate('SignUpEmail'); } else { - navigation.navigate('JoinWaitList', { lure }); + navigation.navigate('JoinWaitList', {}); } } catch (err) { console.error('Error checking hosting availability:', err); diff --git a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx index 55be542102..82fb82014a 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx @@ -47,10 +47,11 @@ export const ReserveShipScreen = ({ // Fetch statuses for the user's ships and start any required booting/resuming const shipsWithStatus = await getShipsWithStatus(shipIds); if (!shipsWithStatus) { - return setState({ - state: 'error', - error: "Sorry, we couldn't find an active ship for your account.", - }); + // return setState({ + // state: 'error', + // error: "Sorry, we couldn't find an active ship for your account.", + // }); + return setState({ state: 'booting' }); } const { status, shipId } = shipsWithStatus; @@ -119,7 +120,8 @@ export const ReserveShipScreen = ({ } // We are done using the saved lure link, it can be cleared before dropping user in the app - clearLure(); + // TODO: should this be removed? + // clearLure(); // Set the ship info in the main context to navigate to chat view setShip({ diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx index 9b2763ed5b..2829a67737 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx @@ -4,7 +4,11 @@ import { DEFAULT_PRIORITY_TOKEN, EMAIL_REGEX, } from '@tloncorp/app/constants'; -import { useLureMetadata } from '@tloncorp/app/contexts/branch'; +import { + useBranch, + useLureMetadata, + useSignupParams, +} from '@tloncorp/app/contexts/branch'; import { getHostingAvailability } from '@tloncorp/app/lib/hostingApi'; import { trackError, trackOnboardingAction } from '@tloncorp/app/utils/posthog'; import { @@ -31,17 +35,10 @@ type FormData = { email: string; }; -export const SignUpEmailScreen = ({ - navigation, - route: { - params: { - lure = DEFAULT_LURE, - priorityToken = DEFAULT_PRIORITY_TOKEN, - } = {}, - }, -}: Props) => { +export const SignUpEmailScreen = ({ navigation, route: { params } }: Props) => { const [isSubmitting, setIsSubmitting] = useState(false); + const signupParams = useSignupParams(); const lureMeta = useLureMetadata(); const { @@ -56,14 +53,19 @@ export const SignUpEmailScreen = ({ setIsSubmitting(true); try { + console.log( + `bl: getting hosting availability`, + email, + signupParams.lureId + ); const { enabled, validEmail } = await getHostingAvailability({ email, - lure, - priorityToken, + lure: signupParams.lureId, + priorityToken: 'testing2', }); if (!enabled) { - navigation.navigate('JoinWaitList', { email, lure }); + navigation.navigate('JoinWaitList', { email }); } else if (!validEmail) { setError('email', { type: 'custom', @@ -75,9 +77,11 @@ export const SignUpEmailScreen = ({ trackOnboardingAction({ actionName: 'Email submitted', email, - lure, + lure: signupParams.lureId, + }); + navigation.navigate('SignUpPassword', { + email, }); - navigation.navigate('SignUpPassword', { email, lure, priorityToken }); } } catch (err) { console.error('Error getting hosting availability:', err); diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx index 83795fdbc7..1e3a84436c 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx @@ -4,8 +4,12 @@ import { initClient, } from '@google-cloud/recaptcha-enterprise-react-native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { RECAPTCHA_SITE_KEY } from '@tloncorp/app/constants'; -import { useLureMetadata } from '@tloncorp/app/contexts/branch'; +import { DEFAULT_LURE, RECAPTCHA_SITE_KEY } from '@tloncorp/app/constants'; +import { + useBranch, + useLureMetadata, + useSignupParams, +} from '@tloncorp/app/contexts/branch'; import { logInHostingUser, signUpHostingUser, @@ -43,10 +47,11 @@ type FormData = { export const SignUpPasswordScreen = ({ navigation, route: { - params: { email, lure, priorityToken }, + params: { email }, }, }: Props) => { const [isSubmitting, setIsSubmitting] = useState(false); + const signupParams = useSignupParams(); const lureMeta = useLureMetadata(); const { control, @@ -104,12 +109,13 @@ export const SignUpPasswordScreen = ({ } try { + console.log(`bl: signing up hosting user`, signupParams.lureId); await signUpHostingUser({ email, password, recaptchaToken, - lure, - priorityToken, + lure: signupParams.lureId, + priorityToken: 'testing2', }); } catch (err) { console.error('Error signing up user:', err); @@ -127,7 +133,7 @@ export const SignUpPasswordScreen = ({ trackOnboardingAction({ actionName: 'Account Created', email, - lure, + lure: signupParams.lureId, }); try { diff --git a/apps/tlon-mobile/src/types.ts b/apps/tlon-mobile/src/types.ts index 4b1a5790b1..4e2e676f1a 100644 --- a/apps/tlon-mobile/src/types.ts +++ b/apps/tlon-mobile/src/types.ts @@ -92,11 +92,11 @@ export type SettingsStackParamList = { export type OnboardingStackParamList = { Welcome: undefined; - InventoryCheck: { lure?: string; priorityToken?: string } | undefined; - SignUpEmail: { lure?: string; priorityToken?: string } | undefined; + InventoryCheck: undefined; + SignUpEmail: undefined; EULA: undefined; - SignUpPassword: { email: string; lure: string; priorityToken?: string }; - JoinWaitList: { email?: string; lure?: string }; + SignUpPassword: { email: string }; + JoinWaitList: { email?: string }; RequestPhoneVerify: { user: User }; CheckVerify: { user: User }; ReserveShip: { user: User; signUpExtras?: SignUpExtras }; diff --git a/packages/app/contexts/branch.tsx b/packages/app/contexts/branch.tsx index 48f6347a81..0c2e4366df 100644 --- a/packages/app/contexts/branch.tsx +++ b/packages/app/contexts/branch.tsx @@ -10,6 +10,7 @@ import { } from 'react'; import branch from 'react-native-branch'; +import { DEFAULT_LURE, DEFAULT_PRIORITY_TOKEN } from '../constants'; import storage from '../lib/storage'; import { getPathFromWer } from '../utils/string'; @@ -69,6 +70,21 @@ export const useBranch = () => { return context; }; +export const useSignupParams = () => { + const context = useContext(Context); + + if (!context) { + throw new Error( + 'Must call `useSignupParams` within a `BranchProvider` component.' + ); + } + + return { + lureId: context.lure?.id ?? DEFAULT_LURE, + priorityToken: context.priorityToken ?? DEFAULT_PRIORITY_TOKEN, + }; +}; + export const useLureMetadata = () => { const context = useContext(Context); diff --git a/packages/app/lib/hostingApi.ts b/packages/app/lib/hostingApi.ts index b3cd1e4fcd..5e023d3e8b 100644 --- a/packages/app/lib/hostingApi.ts +++ b/packages/app/lib/hostingApi.ts @@ -141,7 +141,7 @@ export const signUpHostingUser = async (params: { email: params.email, password: params.password, lure: params.lure || DEFAULT_LURE, - priorityToken: params.priorityToken || DEFAULT_PRIORITY_TOKEN, + priorityToken: 'testing2', recaptcha: { recaptchaToken: { token: params.recaptchaToken || '' }, recaptchaPlatform: Platform.OS, From b7dab0fbdd0da87535be2aa13ce2b3aedf6af744 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Mon, 16 Sep 2024 18:33:45 -0400 Subject: [PATCH 044/157] fix import path --- .../tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx index 704a145d7b..fe0ec58e11 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx @@ -1,5 +1,6 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { DEFAULT_LURE, DEFAULT_PRIORITY_TOKEN } from '@tloncorp/app/constants'; +import { useBranch, useSignupParams } from '@tloncorp/app/contexts/branch'; import { getHostingAvailability } from '@tloncorp/app/lib/hostingApi'; import { trackError } from '@tloncorp/app/utils/posthog'; import { @@ -12,7 +13,6 @@ import { XStack, YStack, } from '@tloncorp/ui'; -import { useBranch, useSignupParams } from 'packages/app/contexts/branch'; import { useState } from 'react'; import { Image } from 'react-native'; From 8fe6349e002095baa6f28cf291d0c67e057e53c9 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Mon, 16 Sep 2024 18:36:30 -0400 Subject: [PATCH 045/157] one more import --- .../src/screens/Onboarding/JoinWaitListScreen.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/JoinWaitListScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/JoinWaitListScreen.tsx index 3f3570a983..06ad570443 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/JoinWaitListScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/JoinWaitListScreen.tsx @@ -22,12 +22,7 @@ type FormData = { email: string; }; -export const JoinWaitListScreen = ({ - navigation, - route: { - params: { lure }, - }, -}: Props) => { +export const JoinWaitListScreen = ({ navigation }: Props) => { const [remoteError, setRemoteError] = useState(); const { control, @@ -37,7 +32,7 @@ export const JoinWaitListScreen = ({ const onSubmit = async (data: FormData) => { try { - await addUserToWaitlist({ email: data.email, lure }); + await addUserToWaitlist({ email: data.email }); trackOnboardingAction({ actionName: 'Waitlist Joined', }); From 48ee564433d93b811126045a81607ac83a38c7f3 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Mon, 16 Sep 2024 18:37:24 -0400 Subject: [PATCH 046/157] actually one more i swear --- apps/tlon-mobile/src/App.main.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/tlon-mobile/src/App.main.tsx b/apps/tlon-mobile/src/App.main.tsx index 0663bcc732..83849bde04 100644 --- a/apps/tlon-mobile/src/App.main.tsx +++ b/apps/tlon-mobile/src/App.main.tsx @@ -107,7 +107,6 @@ const App = ({ Date: Mon, 16 Sep 2024 18:46:11 -0400 Subject: [PATCH 047/157] fix deeplinklistener loop --- apps/tlon-mobile/src/hooks/useDeepLinkListener.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index f4585db53b..e2befc6ea4 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -10,14 +10,13 @@ import { Alert } from 'react-native'; import { RootStackParamList } from '../types'; export const useDeepLinkListener = () => { - const navigation = useNavigation>(); const { ship } = useShip(); const signupParams = useSignupParams(); - const { clearLure } = useBranch(); + const { clearLure, lure } = useBranch(); // If lure is present, invite it and mark as handled useEffect(() => { - if (ship && signupParams.lureId) { + if (ship && lure) { (async () => { try { console.log(`bl: inviting ship with lure`, ship, signupParams.lureId); @@ -46,7 +45,7 @@ export const useDeepLinkListener = () => { clearLure(); })(); } - }, [ship, signupParams, clearLure]); + }, [ship, signupParams, clearLure, lure]); // If deep link clicked, broadcast that navigation update to the webview and mark as handled // useEffect(() => { From 39fc4ccf387e22c0a555279edca3319c2beb261f Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Mon, 16 Sep 2024 18:13:42 -0500 Subject: [PATCH 048/157] branch: use token instead of flag for lure id --- packages/shared/src/logic/branch.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/shared/src/logic/branch.ts b/packages/shared/src/logic/branch.ts index 734ca3b194..3b2f9c9b2c 100644 --- a/packages/shared/src/logic/branch.ts +++ b/packages/shared/src/logic/branch.ts @@ -97,9 +97,9 @@ export const createDeepLink = async ({ if (!fallbackUrl || !path) { return undefined; } - if (type === 'lure' && !urbit.whomIsFlag(path)) { - return undefined; - } + // if (type === 'lure' && !urbit.whomIsFlag(path)) { + // return undefined; + // } if (type === 'wer') { const parts = path.split('/'); const isDMLure = @@ -122,7 +122,7 @@ export const createDeepLink = async ({ data['$canonical_url'] = fallbackUrl; if (type === 'lure') { - data.lure = path; + data.lure = token; } else { data.wer = path; } From 5df3d845f2e3da90e6228202074e3ce2b880af39 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Tue, 17 Sep 2024 07:43:18 -0500 Subject: [PATCH 049/157] refurb group creation flow --- .../src/components/AddGroupSheet.tsx | 318 --------------- .../controllers/ChatListScreenController.tsx | 79 ++-- .../FindGroupsScreenController.tsx | 15 + apps/tlon-mobile/src/navigation/RootStack.tsx | 2 + apps/tlon-mobile/src/types.ts | 1 + apps/tlon-web-new/src/app.tsx | 2 + .../controllers/ChatListScreenController.tsx | 44 +-- .../FindGroupsScreenController.tsx | 8 + packages/app/features/top/ChannelScreen.tsx | 12 +- packages/app/features/top/ChatListScreen.tsx | 88 +++-- .../app/features/top/FindGroupsScreen.tsx | 11 + .../app/hooks/useChannelNavigation.native.ts | 19 - packages/app/hooks/useChannelNavigation.ts | 19 - packages/app/hooks/useGroupActions.native.tsx | 33 ++ packages/app/hooks/useGroupActions.tsx | 30 ++ .../ui/src/components/AddChats/AddDmSheet.tsx | 138 ------- .../components/AddChats/CreateGroupWidget.tsx | 44 ++- packages/ui/src/components/AddGroupSheet.tsx | 371 ++++++++++++++++++ .../ui/src/components/Buttons/TextButton.tsx | 15 + packages/ui/src/components/Buttons/index.ts | 1 + .../components/Channel/EmptyChannelNotice.tsx | 33 +- packages/ui/src/components/Channel/index.tsx | 1 - packages/ui/src/components/ContactBook.tsx | 43 +- packages/ui/src/components/FindGroupsView.tsx | 98 +++++ .../src/components/FloatingActionButton.tsx | 18 +- .../components/FloatingAddButton.native.tsx | 37 -- .../ui/src/components/FloatingAddButton.tsx | 57 --- .../ui/src/components/InviteUsersWidget.tsx | 1 - packages/ui/src/index.tsx | 4 +- 29 files changed, 762 insertions(+), 780 deletions(-) delete mode 100644 apps/tlon-mobile/src/components/AddGroupSheet.tsx create mode 100644 apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx create mode 100644 apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx create mode 100644 packages/app/features/top/FindGroupsScreen.tsx create mode 100644 packages/app/hooks/useGroupActions.native.tsx create mode 100644 packages/app/hooks/useGroupActions.tsx delete mode 100644 packages/ui/src/components/AddChats/AddDmSheet.tsx create mode 100644 packages/ui/src/components/AddGroupSheet.tsx create mode 100644 packages/ui/src/components/Buttons/TextButton.tsx create mode 100644 packages/ui/src/components/FindGroupsView.tsx delete mode 100644 packages/ui/src/components/FloatingAddButton.native.tsx delete mode 100644 packages/ui/src/components/FloatingAddButton.tsx diff --git a/apps/tlon-mobile/src/components/AddGroupSheet.tsx b/apps/tlon-mobile/src/components/AddGroupSheet.tsx deleted file mode 100644 index 1c6ba22e95..0000000000 --- a/apps/tlon-mobile/src/components/AddGroupSheet.tsx +++ /dev/null @@ -1,318 +0,0 @@ -import { - NavigationContainer, - NavigationContainerRef, - StackActions, -} from '@react-navigation/native'; -import { - NativeStackScreenProps, - createNativeStackNavigator, -} from '@react-navigation/native-stack'; -import { BRANCH_DOMAIN, BRANCH_KEY } from '@tloncorp/app/constants'; -import { useCurrentUserId } from '@tloncorp/app/hooks/useCurrentUser'; -import { QueryClientProvider, queryClient } from '@tloncorp/shared/dist/api'; -import * as db from '@tloncorp/shared/dist/db'; -import { - AppDataContextProvider, - Button, - ContactBook, - CreateGroupWidget, - GroupPreviewPane, - Icon, - InviteUsersWidget, - Sheet, - View, - ViewUserGroupsWidget, - XStack, - YStack, - triggerHaptic, - useContacts, - useTheme, -} from '@tloncorp/ui/src'; -import { - createContext, - useCallback, - useContext, - useEffect, - useRef, - useState, -} from 'react'; -import { KeyboardAvoidingView } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; - -interface AddGroupActions { - dismiss: () => void; - onCreatedGroup: ({ - group, - channel, - }: { - group: db.Group; - channel: db.Channel; - }) => void; - onScrollChange: (scrolling: boolean) => void; - screenKey?: number; - contacts?: db.Contact[] | null; -} -const ActionContext = createContext({} as AddGroupActions); - -type StackParamList = { - Home: undefined; - Root: undefined; - CreateGroup: undefined; - InviteUsers: { - group: db.Group; - onInviteComplete: () => void; - }; - ViewContactGroups: { - contactId: string; - }; - ViewGroupPreview: { - group: db.Group; - }; -}; -const Stack = createNativeStackNavigator(); - -export default function AddGroupSheet({ - open, - onOpenChange, - onCreatedGroup, -}: { - open: boolean; - onOpenChange: (open: boolean) => void; - onCreatedGroup: ({ - group, - channel, - }: { - group: db.Group; - channel: db.Channel; - }) => void; -}) { - const [screenScrolling, setScreenScrolling] = useState(false); - const theme = useTheme(); - const navigationRef = useRef>(null); - const [screenKey, setScreenKey] = useState(0); - const contacts = useContacts(); - - const dismiss = useCallback(() => { - if (navigationRef.current && navigationRef.current.canGoBack()) { - navigationRef.current.dispatch(StackActions.popToTop()); - } - onOpenChange(false); - // used for resetting components nested within screens after - // reopening - setTimeout(() => setScreenKey((key) => key + 1), 300); - }, [onOpenChange]); - - useEffect(() => { - if (open) { - triggerHaptic('sheetOpen'); - } - }, [open]); - - return ( - - - - - - - - - - - - - - - - - - - - - - - - ); -} - -function ScreenWrapper({ - children, - withoutSafe, -}: { - children: React.ReactNode; - withoutSafe?: boolean; -}) { - const insets = useSafeAreaInsets(); - return ( - - {children} - - ); -} - -function RootScreen(props: NativeStackScreenProps) { - const { onScrollChange, screenKey, contacts } = useContext(ActionContext); - const insets = useSafeAreaInsets(); - const onSelect = useCallback( - (contactId: string) => { - props.navigation.push('ViewContactGroups', { - contactId, - }); - }, - [props.navigation] - ); - - return ( - - {/* Unclear why we have to render another contacts provider here, but the screen context shows up empty */} - {/* on Android? */} - {/* */} - - - - - - - {/* */} - - ); -} - -function ViewContactGroupsScreen( - props: NativeStackScreenProps -) { - const { onScrollChange } = useContext(ActionContext); - const onSelectGroup = useCallback( - (group: db.Group) => - props.navigation.push('ViewGroupPreview', { - group, - }), - [props.navigation] - ); - - return ( - - - props.navigation.pop()} /> - - - - ); -} - -function ViewGroupPreviewScreen( - props: NativeStackScreenProps -) { - const { dismiss } = useContext(ActionContext); - return ( - - - - props.navigation.pop()} /> - - - - - ); -} - -function CreateGroupScreen( - props: NativeStackScreenProps -) { - const { onCreatedGroup, dismiss } = useContext(ActionContext); - const handleCreate = useCallback( - (args: { group: db.Group; channel: db.Channel }) => { - props.navigation.push('InviteUsers', { - group: args.group, - onInviteComplete: () => { - dismiss(); - onCreatedGroup(args); - }, - }); - }, - [dismiss, onCreatedGroup, props.navigation] - ); - return ( - - props.navigation.pop()} - onCreatedGroup={handleCreate} - /> - - ); -} - -function InviteUsersScreen( - props: NativeStackScreenProps -) { - const { contacts } = useContext(ActionContext); - const currentUserId = useCurrentUserId(); - - return ( - - - - - - ); -} diff --git a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx b/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx index 985e92340c..e9e604c0b8 100644 --- a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx +++ b/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx @@ -1,9 +1,6 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import ChatListScreen from '@tloncorp/app/features/top/ChatListScreen'; -import * as db from '@tloncorp/shared/dist/db'; -import { useCallback, useState } from 'react'; -import AddGroupSheet from '../components/AddGroupSheet'; import type { RootStackParamList } from '../types'; type ChatListControllerProps = NativeStackScreenProps< @@ -14,59 +11,29 @@ type ChatListControllerProps = NativeStackScreenProps< export function ChatListScreenController({ navigation, }: ChatListControllerProps) { - const [addGroupOpen, setAddGroupOpen] = useState(false); - const [startDmOpen, setStartDmOpen] = useState(false); - - const handleAddGroupOpenChange = useCallback((open: boolean) => { - if (!open) { - setAddGroupOpen(false); - } - }, []); - - const goToChannel = useCallback( - ({ channel }: { channel: db.Channel }) => { - setStartDmOpen(false); - setAddGroupOpen(false); - setTimeout(() => navigation.navigate('Channel', { channel }), 150); - }, - [navigation] - ); - - const handleGroupCreated = useCallback( - ({ channel }: { channel: db.Channel }) => goToChannel({ channel }), - [goToChannel] - ); - return ( - <> - { - navigation.push('Channel', { channel }); - }} - navigateToGroupChannels={(group) => { - navigation.navigate('GroupChannels', { group }); - }} - navigateToSelectedPost={(channel, postId) => { - navigation.navigate('Channel', { channel, selectedPostId: postId }); - }} - navigateToHome={() => { - navigation.navigate('ChatList'); - }} - navigateToNotifications={() => { - navigation.navigate('Activity'); - }} - navigateToProfile={() => { - navigation.navigate('Profile'); - }} - /> - - + { + navigation.push('Channel', { channel }); + }} + navigateToGroupChannels={(group) => { + navigation.navigate('GroupChannels', { group }); + }} + navigateToSelectedPost={(channel, postId) => { + navigation.navigate('Channel', { channel, selectedPostId: postId }); + }} + navigateToHome={() => { + navigation.navigate('ChatList'); + }} + navigateToNotifications={() => { + navigation.navigate('Activity'); + }} + navigateToProfile={() => { + navigation.navigate('Profile'); + }} + navigateToFindGroups={() => { + navigation.navigate('FindGroups'); + }} + /> ); } diff --git a/apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx b/apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx new file mode 100644 index 0000000000..655bfb322c --- /dev/null +++ b/apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx @@ -0,0 +1,15 @@ +import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { FindGroupsScreen } from '@tloncorp/app/features/top/FindGroupsScreen'; + +import type { RootStackParamList } from '../types'; + +type ChatListControllerProps = NativeStackScreenProps< + RootStackParamList, + 'ChatList' +>; + +export function FindGroupsScreenController({ + navigation, +}: ChatListControllerProps) { + return navigation.goBack()} />; +} diff --git a/apps/tlon-mobile/src/navigation/RootStack.tsx b/apps/tlon-mobile/src/navigation/RootStack.tsx index 1db814d75f..4db0b8bb8e 100644 --- a/apps/tlon-mobile/src/navigation/RootStack.tsx +++ b/apps/tlon-mobile/src/navigation/RootStack.tsx @@ -15,6 +15,7 @@ import { ChannelSearchScreenController } from '../controllers/ChannelSearchScree import { ChatListScreenController } from '../controllers/ChatListScreenController'; import { EditProfileScreenController } from '../controllers/EditProfileScreenController'; import { FeatureFlagScreenController } from '../controllers/FeatureFlagScreenController'; +import { FindGroupsScreenController } from '../controllers/FindGroupsScreenController'; import { GroupChannelsScreenController } from '../controllers/GroupChannelsScreenController'; import ImageViewerScreenController from '../controllers/ImageViewerScreenController'; import { ManageAccountScreenController } from '../controllers/ManageAccountScreenController'; @@ -68,6 +69,7 @@ export function RootStack() { {/* individual screens */} + } /> } /> + } /> { - if (!open) { - setAddGroupOpen(false); - } - }, []); - - const goToChannel = useCallback( - ({ channel }: { channel: db.Channel }) => { - setStartDmOpen(false); - setAddGroupOpen(false); - setTimeout( - () => navigate(`/group/${channel.groupId}/channel/${channel.id}`), - 150 - ); - }, - [navigate] - ); - - const handleGroupCreated = useCallback( - ({ channel }: { channel: db.Channel }) => goToChannel({ channel }), - [goToChannel] - ); const handleNavigateToChannel = useCallback( (channel: db.Channel, postId?: string | null) => { @@ -56,10 +29,7 @@ export function ChatListScreenController() { return ( <> { + navigateToChannel={(channel) => { navigate(`/dm/${channel.id}`); }} navigateToGroupChannels={(group) => { @@ -75,14 +45,10 @@ export function ChatListScreenController() { navigateToProfile={() => { navigate('/profile'); }} + navigateToFindGroups={() => { + navigate('/find-groups'); + }} /> - {/* - - */} ); } diff --git a/apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx b/apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx new file mode 100644 index 0000000000..f8f319b051 --- /dev/null +++ b/apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx @@ -0,0 +1,8 @@ +import { FindGroupsScreen } from '@tloncorp/app/features/top/FindGroupsScreen'; +import { useNavigate } from 'react-router'; + +export function FindGroupsScreenController() { + const navigate = useNavigate(); + + return navigate(-1)} />; +} diff --git a/packages/app/features/top/ChannelScreen.tsx b/packages/app/features/top/ChannelScreen.tsx index e34a0ca35d..f8778d9961 100644 --- a/packages/app/features/top/ChannelScreen.tsx +++ b/packages/app/features/top/ChannelScreen.tsx @@ -22,6 +22,7 @@ import { useChannelContext } from '../../hooks/useChannelContext'; import { useChannelNavigation } from '../../hooks/useChannelNavigation'; import { useChatSettingsNavigation } from '../../hooks/useChatSettingsNavigation'; import { useFocusEffect } from '../../hooks/useFocusEffect'; +import { useGroupActions } from '../../hooks/useGroupActions'; import { useIsFocused } from '../../hooks/useIsFocused'; const logger = createDevLogger('ChannelScreen', false); @@ -99,13 +100,10 @@ export default function ChannelScreen({ uploaderKey: `${currentChannelId}`, }); - const { - navigateToImage, - navigateToPost, - navigateToRef, - navigateToSearch, - performGroupAction, - } = useChannelNavigation({ channelId: currentChannelId }); + const { navigateToImage, navigateToPost, navigateToRef, navigateToSearch } = + useChannelNavigation({ channelId: currentChannelId }); + + const { performGroupAction } = useGroupActions(); const session = store.useCurrentSession(); const hasCachedNewest = useMemo(() => { diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx index 7597975975..4c62f6cf1b 100644 --- a/packages/app/features/top/ChatListScreen.tsx +++ b/packages/app/features/top/ChatListScreen.tsx @@ -2,19 +2,19 @@ import * as db from '@tloncorp/shared/dist/db'; import * as logic from '@tloncorp/shared/dist/logic'; import * as store from '@tloncorp/shared/dist/store'; import { + AddGroupSheet, Button, ChatList, ChatOptionsProvider, ChatOptionsSheet, ChatOptionsSheetMethods, - FloatingAddButton, + FloatingActionButton, GroupPreviewSheet, Icon, InviteUsersSheet, NavBarView, RequestsProvider, ScreenHeader, - StartDmSheet, View, WelcomeSheet, } from '@tloncorp/ui'; @@ -37,26 +37,23 @@ const ShowFiltersButton = ({ onPress }: { onPress: () => void }) => { }; export default function ChatListScreen({ - startDmOpen, - setStartDmOpen, - setAddGroupOpen, - navigateToDm, + navigateToChannel, navigateToGroupChannels, navigateToSelectedPost, navigateToHome, navigateToNotifications, navigateToProfile, + navigateToFindGroups, }: { - startDmOpen: boolean; - setStartDmOpen: (open: boolean) => void; - setAddGroupOpen: (open: boolean) => void; - navigateToDm: (channel: db.Channel) => void; + navigateToChannel: (channel: db.Channel) => void; navigateToGroupChannels: (group: db.Group) => void; navigateToSelectedPost: (channel: db.Channel, postId?: string | null) => void; navigateToHome: () => void; navigateToNotifications: () => void; navigateToProfile: () => void; + navigateToFindGroups: () => void; }) { + const [addGroupOpen, setAddGroupOpen] = useState(false); const [screenTitle, setScreenTitle] = useState('Home'); const [inviteSheetGroup, setInviteSheetGroup] = useState(); const chatOptionsSheetRef = useRef(null); @@ -121,17 +118,41 @@ export default function ChatListScreen({ }; }, [chats, pendingChats]); + const handleNavigateToFindGroups = useCallback(() => { + setAddGroupOpen(false); + navigateToFindGroups(); + }, [navigateToFindGroups]); + const goToDm = useCallback( - async (participants: string[]) => { + async (userId: string) => { const dmChannel = await store.upsertDmChannel({ - participants, + participants: [userId], }); - setStartDmOpen(false); - navigateToDm(dmChannel); + setAddGroupOpen(false); + navigateToChannel(dmChannel); + }, + [navigateToChannel, setAddGroupOpen] + ); + + const goToChannel = useCallback( + ({ channel }: { channel: db.Channel }) => { + setAddGroupOpen(false); + setTimeout(() => navigateToChannel(channel), 150); }, - [navigateToDm, setStartDmOpen] + [navigateToChannel] ); + const handleGroupCreated = useCallback( + ({ channel }: { channel: db.Channel }) => goToChannel({ channel }), + [goToChannel] + ); + + const handleAddGroupOpenChange = useCallback((open: boolean) => { + if (!open) { + setAddGroupOpen(false); + } + }, []); + const [isChannelSwitcherEnabled] = useFeatureFlag('channelSwitcher'); const onPressChat = useCallback( @@ -163,14 +184,14 @@ export default function ChatListScreen({ } }, []); - const handleDmOpenChange = useCallback( - (open: boolean) => { - if (!open) { - setStartDmOpen(false); - } - }, - [setStartDmOpen] - ); + // const handleDmOpenChange = useCallback( + // (open: boolean) => { + // if (!open) { + // setStartDmOpen(false); + // } + // }, + // [setStartDmOpen] + // ); const handleGroupPreviewSheetOpenChange = useCallback((open: boolean) => { if (!open) { @@ -280,9 +301,11 @@ export default function ChatListScreen({ width={'100%'} pointerEvents="box-none" > - { + setAddGroupOpen(true); + }} + icon={} /> - + + ); } diff --git a/packages/app/features/top/FindGroupsScreen.tsx b/packages/app/features/top/FindGroupsScreen.tsx new file mode 100644 index 0000000000..a9c9466ad1 --- /dev/null +++ b/packages/app/features/top/FindGroupsScreen.tsx @@ -0,0 +1,11 @@ +import { FindGroupsView } from '@tloncorp/ui'; + +import { useGroupActions } from '../../hooks/useGroupActions'; + +export function FindGroupsScreen({ onCancel }: { onCancel: () => void }) { + const { performGroupAction } = useGroupActions(); + + return ( + + ); +} diff --git a/packages/app/hooks/useChannelNavigation.native.ts b/packages/app/hooks/useChannelNavigation.native.ts index 51ad7e74e3..d5c5edc891 100644 --- a/packages/app/hooks/useChannelNavigation.native.ts +++ b/packages/app/hooks/useChannelNavigation.native.ts @@ -49,29 +49,10 @@ export const useChannelNavigation = ({ channelId }: { channelId: string }) => { }); }, [navigation, channelQuery.data]); - const performGroupAction = useCallback( - async (action: GroupPreviewAction, updatedGroup: db.Group) => { - if (action === 'goTo' && updatedGroup.lastPost?.channelId) { - const channel = await db.getChannel({ - id: updatedGroup.lastPost.channelId, - }); - if (channel) { - navigation.navigate('Channel', { channel }); - } - } - - if (action === 'joined') { - navigation.navigate('ChatList'); - } - }, - [navigation] - ); - return { navigateToPost, navigateToRef, navigateToImage, navigateToSearch, - performGroupAction, }; }; diff --git a/packages/app/hooks/useChannelNavigation.ts b/packages/app/hooks/useChannelNavigation.ts index fc6f37f5a8..284635cdea 100644 --- a/packages/app/hooks/useChannelNavigation.ts +++ b/packages/app/hooks/useChannelNavigation.ts @@ -68,29 +68,10 @@ export const useChannelNavigation = ({ channelId }: { channelId: string }) => { navigate(`/dm/${channelQuery.data.id}/search`); }, [navigate, channelQuery.data]); - const performGroupAction = useCallback( - async (action: GroupPreviewAction, updatedGroup: db.Group) => { - if (action === 'goTo' && updatedGroup.lastPost?.channelId) { - const channel = await db.getChannel({ - id: updatedGroup.lastPost.channelId, - }); - if (channel) { - navigate('/group/' + channel.groupId + '/channel/' + channel.id); - } - } - - if (action === 'joined') { - navigate('/'); - } - }, - [navigate] - ); - return { navigateToPost, navigateToRef, navigateToImage, navigateToSearch, - performGroupAction, }; }; diff --git a/packages/app/hooks/useGroupActions.native.tsx b/packages/app/hooks/useGroupActions.native.tsx new file mode 100644 index 0000000000..43b6a2ba56 --- /dev/null +++ b/packages/app/hooks/useGroupActions.native.tsx @@ -0,0 +1,33 @@ +import { useNavigation } from '@react-navigation/native'; +import * as db from '@tloncorp/shared/dist/db'; +import { GroupPreviewAction } from '@tloncorp/ui'; +import { useCallback } from 'react'; + +export const useGroupActions = () => { + const navigation = useNavigation< + // @ts-expect-error - TODO: pass navigation handlers into context + NativeStackNavigationProp + >(); + + const performGroupAction = useCallback( + async (action: GroupPreviewAction, updatedGroup: db.Group) => { + if (action === 'goTo' && updatedGroup.lastPost?.channelId) { + const channel = await db.getChannel({ + id: updatedGroup.lastPost.channelId, + }); + if (channel) { + navigation.navigate('Channel', { channel }); + } + } + + if (action === 'joined') { + navigation.navigate('ChatList'); + } + }, + [navigation] + ); + + return { + performGroupAction, + }; +}; diff --git a/packages/app/hooks/useGroupActions.tsx b/packages/app/hooks/useGroupActions.tsx new file mode 100644 index 0000000000..d2194a49fe --- /dev/null +++ b/packages/app/hooks/useGroupActions.tsx @@ -0,0 +1,30 @@ +import * as db from '@tloncorp/shared/dist/db'; +import { GroupPreviewAction } from '@tloncorp/ui'; +import { useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; + +export const useGroupActions = () => { + const navigate = useNavigate(); + + const performGroupAction = useCallback( + async (action: GroupPreviewAction, updatedGroup: db.Group) => { + if (action === 'goTo' && updatedGroup.lastPost?.channelId) { + const channel = await db.getChannel({ + id: updatedGroup.lastPost.channelId, + }); + if (channel) { + navigate('/group/' + channel.groupId + '/channel/' + channel.id); + } + } + + if (action === 'joined') { + navigate('/'); + } + }, + [navigate] + ); + + return { + performGroupAction, + }; +}; diff --git a/packages/ui/src/components/AddChats/AddDmSheet.tsx b/packages/ui/src/components/AddChats/AddDmSheet.tsx deleted file mode 100644 index 0a55cc7381..0000000000 --- a/packages/ui/src/components/AddChats/AddDmSheet.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import * as store from '@tloncorp/shared/dist/store'; -import { useCallback, useEffect, useState } from 'react'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { XStack, YStack, ZStack } from 'tamagui'; - -import { triggerHaptic } from '../../utils'; -import { ActionSheet } from '../ActionSheet'; -import { Button } from '../Button'; -import { ContactBook } from '../ContactBook'; -import { LoadingSpinner } from '../LoadingSpinner'; - -function StartDmSheetComponent({ - open, - onOpenChange, - goToDm, -}: { - open: boolean; - onOpenChange: (open: boolean) => void; - goToDm: (participants: string[]) => void; -}) { - const insets = useSafeAreaInsets(); - const [contentScrolling, setContentScrolling] = useState(false); - const [dmParticipants, setDmParticipants] = useState([]); - const [contactBookKey, setContactBookKey] = useState(0); - - useEffect(() => { - if (open) { - triggerHaptic('sheetOpen'); - } - }, [open]); - - const handleDismiss = useCallback(() => { - setDmParticipants([]); - onOpenChange(false); - // let close animate, then reset the contact book participants - setTimeout(() => { - setContactBookKey((key) => key + 1); - setDmParticipants([]); - }, 300); - }, [onOpenChange]); - - const handleGoToDm = useCallback(() => { - goToDm(dmParticipants); - onOpenChange(false); - setTimeout(() => { - setContactBookKey((key) => key + 1); - setDmParticipants([]); - }, 300); - }, [dmParticipants, goToDm, onOpenChange]); - - return ( - - - - - - - {dmParticipants.length > 0 && ( - - - - )} - - - - - - ); -} - -export const StartDmSheet = StartDmSheetComponent; - -function StartDMButton({ - participants, - onPress, -}: { - participants: string[]; - onPress: () => void; -}) { - const isMultiDm = participants.length > 1; - - store.useForceNegotiationUpdate(participants, 'chat'); - const { - match: negotiationMatch, - isLoading: negotiationLoading, - haveAllNegotiations, - } = store.useNegotiateMulti(participants, 'chat', 'chat'); - const multiDmVersionMismatch = !negotiationLoading && !negotiationMatch; - const shouldBlockInput = isMultiDm && multiDmVersionMismatch; - - return ( - - ); -} diff --git a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx index 9420c78ec4..5596e265af 100644 --- a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx +++ b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx @@ -12,6 +12,7 @@ import { Icon } from '../Icon'; export function CreateGroupWidget(props: { goBack: () => void; + invitees: string[]; onCreatedGroup: ({ group, channel, @@ -37,6 +38,11 @@ export function CreateGroupWidget(props: { title: groupName, shortCode, }); + + await store.inviteGroupMembers({ + groupId: group.id, + contactIds: props.invitees, + }); props.onCreatedGroup({ group, channel }); triggerHaptic('success'); } catch (e) { @@ -50,26 +56,28 @@ export function CreateGroupWidget(props: { props.goBack()} /> - Start a New Group + New Group - What is your group about? - {/* TODO: make tamagui input cohere */} - + + Group Name (Required) + {/* TODO: make tamagui input cohere */} + + void; + onCreatedGroup: ({ + group, + channel, + }: { + group: db.Group; + channel: db.Channel; + }) => void; + onScrollChange: (scrolling: boolean) => void; + screenKey?: number; + contacts?: db.Contact[] | null; +} +const ActionContext = createContext({} as AddGroupActions); + +type CurrentScreenKey = 'Root' | 'CreateGroup' | 'InviteUsers'; + +const SCREEN_WIDTH = Dimensions.get('window').width; + +interface TransitionWrapperProps { + children: React.ReactNode; + isActive: boolean; +} + +// This component is used to animate the transition between screens +// in a way that mimics React Navigation's stack navigator. +// It could probably use some tweaking. +// We could also probably extract it out into its own file. +const TransitionWrapper: React.FC = ({ + children, + isActive, +}) => { + const animatedStyles = useAnimatedStyle(() => { + return { + transform: [ + { + translateX: withTiming(isActive ? 0 : SCREEN_WIDTH, { + duration: 300, + easing: Easing.out(Easing.cubic), + }), + }, + ], + opacity: withTiming(isActive ? 1 : 0, { + duration: 300, + easing: Easing.out(Easing.cubic), + }), + }; + }, [isActive]); + + return ( + + {children} + + ); +}; + +const styles = StyleSheet.create({ + screen: { + ...StyleSheet.absoluteFillObject, + }, +}); + +export function AddGroupSheet({ + open, + onOpenChange, + onCreatedGroup, + onGoToDm, + navigateToFindGroups, +}: { + open: boolean; + onOpenChange: (open: boolean) => void; + onCreatedGroup: ({ + group, + channel, + }: { + group: db.Group; + channel: db.Channel; + }) => void; + onGoToDm: (userId: string) => void; + navigateToFindGroups: () => void; +}) { + const [screenScrolling, setScreenScrolling] = useState(false); + const [screenKey, setScreenKey] = useState(0); + const [currentScreen, setCurrentScreen] = useState('Root'); + const [groupInvitees, setGroupInvitees] = useState([]); + const contacts = useContacts(); + + const dismiss = useCallback(() => { + onOpenChange(false); + // used for resetting components nested within screens after + // reopening + setTimeout(() => setScreenKey((key) => key + 1), 300); + }, [onOpenChange]); + + useEffect(() => { + if (open) { + triggerHaptic('sheetOpen'); + } + }, [open]); + + const goToScreen = useCallback( + (screen: CurrentScreenKey) => { + setCurrentScreen(screen); + }, + [setCurrentScreen] + ); + + const screens: Record = useMemo( + () => ({ + Root: ( + + + + ), + CreateGroup: ( + + + + ), + InviteUsers: ( + + + + ), + }), + [goToScreen, onGoToDm, groupInvitees, currentScreen, navigateToFindGroups] + ); + + return ( + + + + + + + + + {Object.values(screens)} + + + + + + + ); +} + +function ScreenWrapper({ + children, + withoutSafe, +}: { + children: React.ReactNode; + withoutSafe?: boolean; +}) { + const insets = useSafeAreaInsets(); + return ( + + {children} + + ); +} + +function RootScreen({ + goToScreen, + goToDM, + goToFindGroups, +}: { + goToScreen: (screen: CurrentScreenKey) => void; + goToDM: (userId: string) => void; + goToFindGroups: () => void; +}) { + const { onScrollChange, screenKey } = useContext(ActionContext); + const onSelect = useCallback( + (contactId: string) => { + goToDM(contactId); + }, + [goToDM] + ); + + return ( + + + + goToScreen('InviteUsers'), + }} + /> + goToFindGroups(), + }} + /> + + + New Message + + + + + ); +} + +function CreateGroupScreen({ + invitees, + goToScreen, +}: { + invitees: string[]; + goToScreen: (screen: CurrentScreenKey) => void; +}) { + const { onCreatedGroup, dismiss } = useContext(ActionContext); + const handleCreate = useCallback( + (args: { group: db.Group; channel: db.Channel }) => { + onCreatedGroup(args); + + dismiss(); + }, + [dismiss, onCreatedGroup] + ); + + return ( + + goToScreen('InviteUsers')} + onCreatedGroup={handleCreate} + invitees={invitees} + /> + + ); +} + +function InviteUsersScreen({ + invitees, + setInvitees, + goToScreen, +}: { + invitees: string[]; + setInvitees: (invitees: string[]) => void; + goToScreen: (screen: CurrentScreenKey) => void; +}) { + const { contacts } = useContext(ActionContext); + const currentUserId = useCurrentUserId(); + const { onScrollChange, screenKey } = useContext(ActionContext); + + return ( + + + + + + Select Members + + { + setInvitees([]); + goToScreen('Root'); + }} + > + Skip + + + + + + + ); +} diff --git a/packages/ui/src/components/Buttons/TextButton.tsx b/packages/ui/src/components/Buttons/TextButton.tsx new file mode 100644 index 0000000000..1ebabfa712 --- /dev/null +++ b/packages/ui/src/components/Buttons/TextButton.tsx @@ -0,0 +1,15 @@ +import { Button, ButtonProps } from '../Button'; + +export function TextButton({ + onPress, + children, + ...props +}: ButtonProps & { + children: string; +}) { + return ( + + ); +} diff --git a/packages/ui/src/components/Buttons/index.ts b/packages/ui/src/components/Buttons/index.ts index ad4b80e70d..ee02d1331c 100644 --- a/packages/ui/src/components/Buttons/index.ts +++ b/packages/ui/src/components/Buttons/index.ts @@ -1 +1,2 @@ export * from './PrimaryButton'; +export * from './TextButton'; diff --git a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx index 45a8a24632..ef161bab10 100644 --- a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx +++ b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx @@ -1,9 +1,11 @@ import * as db from '@tloncorp/shared/dist/db'; import { useMemo, useState } from 'react'; -import { View as RNView } from 'react-native'; -import { SizableText } from 'tamagui'; +import { SizableText, YStack } from 'tamagui'; +import { useGroup } from '../../contexts'; import { useIsAdmin } from '../../utils'; +import { Button } from '../Button'; +import { InviteUsersSheet } from '../InviteUsersSheet'; export function EmptyChannelNotice({ channel, @@ -13,7 +15,10 @@ export function EmptyChannelNotice({ userId: string; }) { const isGroupAdmin = useIsAdmin(channel.groupId ?? '', userId); + const group = useGroup(channel.groupId ?? ''); + console.log('group', group); const [isFirstVisit] = useState(() => channel.lastViewedAt == null); + const [showInviteUsersSheet, setShowInviteUsersSheet] = useState(false); const isWelcomeChannel = !!channel.isDefaultWelcomeChannel; const noticeText = useMemo(() => { if (isGroupAdmin && isFirstVisit && isWelcomeChannel) { @@ -23,14 +28,22 @@ export function EmptyChannelNotice({ return 'There are no messages... yet.'; }, [isGroupAdmin, isFirstVisit, isWelcomeChannel]); - // this component is usually used within a list view, but there's a bug in RN - // with rotating empty placeholders if the list is inverted. This custom styling is a workaround - // that gives callers the ability to optionally account for that issue return ( - - - {noticeText} - - + <> + + + {noticeText} + + + + {}} + /> + ); } diff --git a/packages/ui/src/components/Channel/index.tsx b/packages/ui/src/components/Channel/index.tsx index e091c546e4..a9b321a208 100644 --- a/packages/ui/src/components/Channel/index.tsx +++ b/packages/ui/src/components/Channel/index.tsx @@ -412,7 +412,6 @@ export function Channel({ ? setShowAddGalleryPost(true) : setShowBigInput(true) } - label="New Post" icon={ void; onScrollChange?: (scrolling: boolean) => void; + showCancelButton?: boolean; + onPressCancel?: () => void; + explanationComponent?: React.ReactElement; }) { const contacts = useContacts(); const contactsIndex = useContactIndex(); @@ -39,6 +46,8 @@ export function ContactBook({ contactsIndex ?? {} ); + const Explanation = () => explanationComponent ?? null; + const [query, setQuery] = useState(''); const queryContacts = useSortedContacts({ contacts: contacts ?? [], @@ -113,7 +122,12 @@ export function ContactBook({ return ( {searchable && ( - + + {showCancelButton && ( + onPressCancel?.()}>Cancel + )} + + )} + {!showSearchResults && explanationComponent ? ( + + ) : ( + + )} - - - ); } diff --git a/packages/ui/src/components/FindGroupsView.tsx b/packages/ui/src/components/FindGroupsView.tsx new file mode 100644 index 0000000000..6bcddc245c --- /dev/null +++ b/packages/ui/src/components/FindGroupsView.tsx @@ -0,0 +1,98 @@ +import * as db from '@tloncorp/shared/dist/db'; +import { useCallback, useState } from 'react'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { Text, View, XStack, YStack } from 'tamagui'; + +import { + AppDataContextProvider, + useContacts, + useCurrentUserId, +} from '../contexts'; +import { ViewUserGroupsWidget } from './AddChats'; +import { ContactBook } from './ContactBook'; +import { GroupPreviewAction, GroupPreviewSheet } from './GroupPreviewSheet'; +import { Icon } from './Icon'; + +const GroupJoinExplanation = () => ( + + On Tlon, people host groups. + Look for groups hosted by people above. + +); + +type screens = 'FindGroups' | 'ViewGroupsByContact'; + +export function FindGroupsView({ + onCancel, + onGroupAction, +}: { + onCancel: () => void; + onGroupAction: (action: GroupPreviewAction, group: db.Group) => void; +}) { + const { top } = useSafeAreaInsets(); + const contacts = useContacts(); + const currentUserId = useCurrentUserId(); + const [screen, setScreen] = useState('FindGroups'); + const [viewGroupsForContact, setViewGroupsForContact] = useState< + string | null + >(null); + const [groupForPreview, setGroupForPreview] = useState(null); + + const onSelectGroup = useCallback( + (group: db.Group) => { + setGroupForPreview(group); + }, + [setGroupForPreview] + ); + + const handleGroupAction = useCallback( + (action: GroupPreviewAction, group: db.Group) => { + onGroupAction(action, group); + setGroupForPreview(null); + }, + [onGroupAction] + ); + + return ( + + {screen === 'FindGroups' ? ( + + { + setViewGroupsForContact(contact); + setScreen('ViewGroupsByContact'); + }} + showCancelButton + onPressCancel={onCancel} + explanationComponent={} + /> + + ) : ( + <> + + setScreen('FindGroups')} /> + + + + )} + { + if (!open) { + setGroupForPreview(null); + } + }} + onActionComplete={handleGroupAction} + group={groupForPreview ?? undefined} + /> + + ); +} diff --git a/packages/ui/src/components/FloatingActionButton.tsx b/packages/ui/src/components/FloatingActionButton.tsx index 85beed9372..af668141de 100644 --- a/packages/ui/src/components/FloatingActionButton.tsx +++ b/packages/ui/src/components/FloatingActionButton.tsx @@ -1,7 +1,6 @@ import { getSize } from '@tamagui/get-token'; import { cloneElement, useContext } from 'react'; import { - SizableText, SizeTokens, Stack, Text, @@ -14,31 +13,18 @@ import { export function FloatingActionButton({ onPress, icon, - label, }: { onPress: () => void; icon?: React.ReactNode; - label?: string; }) { return ( ); } @@ -57,7 +43,7 @@ const ButtonFrame = styled(Stack, { }, borderColor: '$border', borderWidth: 1, - borderRadius: '$m', + borderRadius: '$3xl', paddingVertical: '$s', paddingHorizontal: '$l', }); diff --git a/packages/ui/src/components/FloatingAddButton.native.tsx b/packages/ui/src/components/FloatingAddButton.native.tsx deleted file mode 100644 index f0ea34138e..0000000000 --- a/packages/ui/src/components/FloatingAddButton.native.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import ContextMenu from 'react-native-context-menu-view'; - -import { FloatingActionButton } from './FloatingActionButton'; -import { Icon } from './Icon'; - -export function FloatingAddButton({ - setAddGroupOpen, - setStartDmOpen, -}: { - setAddGroupOpen: (open: boolean) => void; - setStartDmOpen: (open: boolean) => void; -}) { - return ( - { - const { index } = event.nativeEvent; - if (index === 0) { - setAddGroupOpen(true); - } - if (index === 1) { - setStartDmOpen(true); - } - }} - > - } - label={'Add'} - onPress={() => {}} - /> - - ); -} diff --git a/packages/ui/src/components/FloatingAddButton.tsx b/packages/ui/src/components/FloatingAddButton.tsx deleted file mode 100644 index 262fb48f69..0000000000 --- a/packages/ui/src/components/FloatingAddButton.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { ListItem, Popover, YStack } from 'tamagui'; - -import { FloatingActionButton } from './FloatingActionButton'; -import { Icon } from './Icon'; - -export function FloatingAddButton({ - setAddGroupOpen, - setStartDmOpen, -}: { - setAddGroupOpen: (open: boolean) => void; - setStartDmOpen: (open: boolean) => void; -}) { - return ( - - - - } - label={'Add'} - onPress={() => {}} - /> - - - - - - setAddGroupOpen(true)} - /> - - - setStartDmOpen(true)} - /> - - - - - ); -} diff --git a/packages/ui/src/components/InviteUsersWidget.tsx b/packages/ui/src/components/InviteUsersWidget.tsx index d6d492d28f..50475c725d 100644 --- a/packages/ui/src/components/InviteUsersWidget.tsx +++ b/packages/ui/src/components/InviteUsersWidget.tsx @@ -8,7 +8,6 @@ import { useBranchDomain, useBranchKey, useCurrentUserId } from '../contexts'; import { useCopy } from '../hooks/useCopy'; import { Button } from './Button'; import { ContactBook } from './ContactBook'; -import { LoadingSpinner } from './LoadingSpinner'; const InviteUsersWidgetComponent = ({ group, diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx index fcf1fc1f8f..bd876acca1 100644 --- a/packages/ui/src/index.tsx +++ b/packages/ui/src/index.tsx @@ -1,7 +1,7 @@ +export * from './components/AddGroupSheet'; export * from './components/Activity/ActivityScreenView'; export { ActionSheet } from './components/ActionSheet'; export * from './components/AddChats'; -export * from './components/AddChats/AddDmSheet'; export * from './components/AppSetting'; export * from './components/Avatar'; export * from './components/BigInput'; @@ -33,8 +33,8 @@ export * from './components/EditableProfileImages'; export * from './components/Embed'; export * from './components/Emoji'; export * from './components/FeatureFlagScreenView'; +export * from './components/FindGroupsView'; export * from './components/FloatingActionButton'; -export * from './components/FloatingAddButton'; export * from './components/FormInput'; export * from './components/Form'; export * from './components/GalleryPost'; From a2ce6e0b1cdc561c00eab38116a1a3c409dc8879 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Tue, 17 Sep 2024 09:57:22 -0400 Subject: [PATCH 050/157] Posts, Channels: relocate overflow menu --- packages/ui/src/components/Channel/index.tsx | 2 +- packages/ui/src/components/PostScreenView.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/ui/src/components/Channel/index.tsx b/packages/ui/src/components/Channel/index.tsx index e091c546e4..be0feb0180 100644 --- a/packages/ui/src/components/Channel/index.tsx +++ b/packages/ui/src/components/Channel/index.tsx @@ -284,7 +284,7 @@ export function Channel({ showSearchButton={isChatChannel} goToSearch={goToSearch} showSpinner={isLoadingPosts} - showMenuButton={false} + showMenuButton={true} /> diff --git a/packages/ui/src/components/PostScreenView.tsx b/packages/ui/src/components/PostScreenView.tsx index b890e7da0f..be41acfbbd 100644 --- a/packages/ui/src/components/PostScreenView.tsx +++ b/packages/ui/src/components/PostScreenView.tsx @@ -112,7 +112,6 @@ export function PostScreenView({ showSearchButton={false} post={parentPost ?? undefined} mode={headerMode} - showMenuButton={true} /> {parentPost ? ( From e7ba3d3c0f042bdb5dab4b27e65d91cefb5eee94 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 10:13:05 -0400 Subject: [PATCH 051/157] stash --- apps/tlon-mobile/ios/Podfile.lock | 4 ++-- apps/tlon-mobile/src/hooks/useDeepLinkListener.ts | 4 ---- packages/app/contexts/branch.tsx | 11 +++++++---- tsconfig.json | 1 - 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/tlon-mobile/ios/Podfile.lock b/apps/tlon-mobile/ios/Podfile.lock index f6e8a99cc4..a45765f3ad 100644 --- a/apps/tlon-mobile/ios/Podfile.lock +++ b/apps/tlon-mobile/ios/Podfile.lock @@ -1743,7 +1743,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 BranchSDK: cb046c2714b03e573484ce9e349e2ddbad7016e8 - DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 + DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 EASClient: a42ee8bf36c93b3128352faf2ae49405ab4f80bd EXApplication: 137189a3f149b4e8e546884629392c3efc94cbd3 EXAV: e4f6137431ddc4cb025895046bfefa9612025c35 @@ -1792,7 +1792,7 @@ SPEC CHECKSUMS: FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f + glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 hermes-engine: ed62e0dcd013bf4a3b487f164feec1c4e705b5b5 diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index e2befc6ea4..752eb6d66d 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -1,5 +1,3 @@ -import { useNavigation } from '@react-navigation/native'; -import type { NavigationProp } from '@react-navigation/native'; import { useBranch, useSignupParams } from '@tloncorp/app/contexts/branch'; import { useShip } from '@tloncorp/app/contexts/ship'; import { inviteShipWithLure } from '@tloncorp/app/lib/hostingApi'; @@ -7,8 +5,6 @@ import { trackError } from '@tloncorp/app/utils/posthog'; import { useEffect } from 'react'; import { Alert } from 'react-native'; -import { RootStackParamList } from '../types'; - export const useDeepLinkListener = () => { const { ship } = useShip(); const signupParams = useSignupParams(); diff --git a/packages/app/contexts/branch.tsx b/packages/app/contexts/branch.tsx index 0c2e4366df..4e25553823 100644 --- a/packages/app/contexts/branch.tsx +++ b/packages/app/contexts/branch.tsx @@ -1,4 +1,4 @@ -import { DeepLinkMetadata } from '@tloncorp/shared/dist'; +import { DeepLinkMetadata, createDevLogger } from '@tloncorp/shared/dist'; import { extractLureMetadata } from '@tloncorp/shared/src/logic'; import { type ReactNode, @@ -40,6 +40,8 @@ const INITIAL_STATE: State = { const STORAGE_KEY = 'lure'; +const logger = createDevLogger('deeplink', true); + const saveLure = async (lure: Lure) => storage.save({ key: STORAGE_KEY, data: JSON.stringify(lure) }); @@ -109,11 +111,11 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { onOpenComplete: ({ params }) => { // Handle Branch link click if (params?.['+clicked_branch_link']) { - console.debug('[branch] Detected Branch link click'); + logger.log('detected Branch link click'); if (params.lure) { // Link had a lure field embedded - console.debug('[branch] Detected lure link:', params.lure); + logger.log('detected lure link:', params.lure); const nextLure: Lure = { lure: { ...extractLureMetadata(params), @@ -121,6 +123,7 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { }, priorityToken: params.token as string | undefined, }; + console.log(`bl: setting next lure`, nextLure); setState({ ...nextLure, deepLinkPath: undefined, @@ -129,7 +132,7 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { } else if (params.wer) { // Link had a wer (deep link) field embedded const deepLinkPath = getPathFromWer(params.wer as string); - console.debug('[branch] Detected deep link:', deepLinkPath); + console.debug('detected deep link:', deepLinkPath); setState({ deepLinkPath, lure: undefined, diff --git a/tsconfig.json b/tsconfig.json index bbe85811dc..6755f6847b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,4 +44,3 @@ } ] } - From 978dabf4f3bda38d50a5dd4c987c8c37d6d3d976 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 10:41:14 -0400 Subject: [PATCH 052/157] prevent double lure invite --- apps/tlon-mobile/src/hooks/useDeepLinkListener.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index 752eb6d66d..75d843a047 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -2,20 +2,22 @@ import { useBranch, useSignupParams } from '@tloncorp/app/contexts/branch'; import { useShip } from '@tloncorp/app/contexts/ship'; import { inviteShipWithLure } from '@tloncorp/app/lib/hostingApi'; import { trackError } from '@tloncorp/app/utils/posthog'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { Alert } from 'react-native'; export const useDeepLinkListener = () => { + const isInvitingRef = useRef(false); const { ship } = useShip(); const signupParams = useSignupParams(); const { clearLure, lure } = useBranch(); // If lure is present, invite it and mark as handled useEffect(() => { - if (ship && lure) { + if (ship && lure && !isInvitingRef.current) { (async () => { try { console.log(`bl: inviting ship with lure`, ship, signupParams.lureId); + isInvitingRef.current = true; await inviteShipWithLure({ ship, lure: signupParams.lureId }); Alert.alert( '', @@ -36,9 +38,10 @@ export const useDeepLinkListener = () => { if (err instanceof Error) { trackError(err); } + } finally { + clearLure(); + isInvitingRef.current = false; } - - clearLure(); })(); } }, [ship, signupParams, clearLure, lure]); From bec7534e4acb231a8efb0cc5c640fe7ca6b59c39 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Tue, 17 Sep 2024 10:04:52 -0500 Subject: [PATCH 053/157] Clean up imports in AddGroupSheet, fix issue with InviteUsersSheet not rendering in EmptyChannelNotice, add InviteUsersSheet to GroupChannelsScreen, other cleanup --- apps/tlon-mobile/ios/Podfile.lock | 4 +- packages/app/features/top/ChatListScreen.tsx | 9 ---- .../app/features/top/GroupChannelsScreen.tsx | 24 ++++++++- packages/ui/src/components/AddGroupSheet.tsx | 45 ++++++++-------- .../components/Channel/EmptyChannelNotice.tsx | 5 +- .../ui/src/components/InviteUsersSheet.tsx | 51 ++++++++++++------- 6 files changed, 82 insertions(+), 56 deletions(-) diff --git a/apps/tlon-mobile/ios/Podfile.lock b/apps/tlon-mobile/ios/Podfile.lock index f6e8a99cc4..a45765f3ad 100644 --- a/apps/tlon-mobile/ios/Podfile.lock +++ b/apps/tlon-mobile/ios/Podfile.lock @@ -1743,7 +1743,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 BranchSDK: cb046c2714b03e573484ce9e349e2ddbad7016e8 - DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 + DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 EASClient: a42ee8bf36c93b3128352faf2ae49405ab4f80bd EXApplication: 137189a3f149b4e8e546884629392c3efc94cbd3 EXAV: e4f6137431ddc4cb025895046bfefa9612025c35 @@ -1792,7 +1792,7 @@ SPEC CHECKSUMS: FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f + glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 hermes-engine: ed62e0dcd013bf4a3b487f164feec1c4e705b5b5 diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx index 4c62f6cf1b..cb3fc4e7f9 100644 --- a/packages/app/features/top/ChatListScreen.tsx +++ b/packages/app/features/top/ChatListScreen.tsx @@ -184,15 +184,6 @@ export default function ChatListScreen({ } }, []); - // const handleDmOpenChange = useCallback( - // (open: boolean) => { - // if (!open) { - // setStartDmOpen(false); - // } - // }, - // [setStartDmOpen] - // ); - const handleGroupPreviewSheetOpenChange = useCallback((open: boolean) => { if (!open) { setSelectedGroup(null); diff --git a/packages/app/features/top/GroupChannelsScreen.tsx b/packages/app/features/top/GroupChannelsScreen.tsx index 86870968ad..52fb460161 100644 --- a/packages/app/features/top/GroupChannelsScreen.tsx +++ b/packages/app/features/top/GroupChannelsScreen.tsx @@ -1,7 +1,11 @@ import * as db from '@tloncorp/shared/dist/db'; import * as store from '@tloncorp/shared/dist/store'; -import { ChatOptionsProvider, GroupChannelsScreenView } from '@tloncorp/ui'; -import { useCallback, useMemo } from 'react'; +import { + ChatOptionsProvider, + GroupChannelsScreenView, + InviteUsersSheet, +} from '@tloncorp/ui'; +import { useCallback, useMemo, useState } from 'react'; import { useChatSettingsNavigation } from '../../hooks/useChatSettingsNavigation'; import { useCurrentUserId } from '../../hooks/useCurrentUser'; @@ -21,6 +25,9 @@ export function GroupChannelsScreen({ const { data: pins } = store.usePins({ enabled: isFocused, }); + const [inviteSheetGroup, setInviteSheetGroup] = useState( + null + ); const pinnedItems = useMemo(() => { return pins ?? []; @@ -42,6 +49,9 @@ export function GroupChannelsScreen({ groupId={groupParam.id} pinned={pinnedItems} useGroup={store.useGroup} + onPressInvite={(group) => { + setInviteSheetGroup(group); + }} {...useChatSettingsNavigation()} > + { + if (!open) { + setInviteSheetGroup(null); + } + }} + group={inviteSheetGroup ?? undefined} + onInviteComplete={() => setInviteSheetGroup(null)} + /> ); } diff --git a/packages/ui/src/components/AddGroupSheet.tsx b/packages/ui/src/components/AddGroupSheet.tsx index bb504d81b3..ed05826abb 100644 --- a/packages/ui/src/components/AddGroupSheet.tsx +++ b/packages/ui/src/components/AddGroupSheet.tsx @@ -1,23 +1,5 @@ import { QueryClientProvider, queryClient } from '@tloncorp/shared/dist/api'; import * as db from '@tloncorp/shared/dist/db'; -import { - ActionSheet, - AppDataContextProvider, - Button, - ContactBook, - CreateGroupWidget, - Icon, - Sheet, - Text, - TextButton, - View, - XStack, - YStack, - isWeb, - triggerHaptic, - useContacts, - useCurrentUserId, -} from '@tloncorp/ui'; import { createContext, useCallback, @@ -33,6 +15,21 @@ import Animated, { withTiming, } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { Text, View, XStack, YStack, isWeb } from 'tamagui'; + +import { + AppDataContextProvider, + useContacts, + useCurrentUserId, +} from '../contexts'; +import { triggerHaptic } from '../utils'; +import { ActionSheet } from './ActionSheet'; +import { CreateGroupWidget } from './AddChats'; +import { Button } from './Button'; +import { TextButton } from './Buttons'; +import { ContactBook } from './ContactBook'; +import { Icon } from './Icon'; +import { Sheet } from './Sheet'; interface AddGroupActions { dismiss: () => void; @@ -144,7 +141,7 @@ export function AddGroupSheet({ const screens: Record = useMemo( () => ({ Root: ( - + ), CreateGroup: ( - + ), InviteUsers: ( - + channel.lastViewedAt == null); const [showInviteUsersSheet, setShowInviteUsersSheet] = useState(false); const isWelcomeChannel = !!channel.isDefaultWelcomeChannel; @@ -40,9 +39,9 @@ export function EmptyChannelNotice({ setShowInviteUsersSheet(open)} group={group} - onInviteComplete={() => {}} + onInviteComplete={() => setShowInviteUsersSheet(false)} /> ); diff --git a/packages/ui/src/components/InviteUsersSheet.tsx b/packages/ui/src/components/InviteUsersSheet.tsx index 7fd182c0f3..e1ff207e1e 100644 --- a/packages/ui/src/components/InviteUsersSheet.tsx +++ b/packages/ui/src/components/InviteUsersSheet.tsx @@ -1,5 +1,6 @@ import * as db from '@tloncorp/shared/dist/db'; -import React from 'react'; +import React, { useRef } from 'react'; +import { Modal } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { InviteUsersWidget } from './InviteUsersWidget'; @@ -17,30 +18,42 @@ const InviteUsersSheetComponent = ({ onInviteComplete: () => void; }) => { const { bottom } = useSafeAreaInsets(); + const hasOpened = useRef(open); - if (!group) { - return null; + if (!hasOpened.current && open) { + hasOpened.current = true; } + if (!hasOpened.current || !group) return null; + return ( - onOpenChange(false)} + transparent + animationType="none" > - - - - - - + + + + + + + ); }; From 4366f44b0f2275bf5c482f38aaf1f22134a95c14 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 13:15:08 -0400 Subject: [PATCH 054/157] refetch lure until we have a valid token --- packages/shared/src/store/lure.ts | 60 +++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index 1da5c644de..e43602a6a5 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -149,7 +149,7 @@ export const useLureState = create((set, get) => ({ return en; }); }, prevLure?.enabled), - // url + // url (includes the token as last element of the path) asyncWithDefault(async () => { lureLogger.log(performance.now(), 'fetching url', flag); return scry({ @@ -180,11 +180,17 @@ export const useLureState = create((set, get) => ({ ), ]); - lureLogger.log('fetched', flag, enabled, url, metadata, outstandingPoke); + lureLogger.log('fetched', { + flag, + enabled, + url, + metadata, + outstandingPoke, + }); let deepLinkUrl: string | undefined; lureLogger.log('enabled', enabled); - if (enabled) { + if (enabled && checkLureToken(url)) { const currentUserId = getCurrentUserId(); const group = await db.getGroup({ id: flag }); const user = await db.getContact({ id: currentUserId }); @@ -229,7 +235,7 @@ const selLure = (flag: string) => (s: LureState) => ({ lure: s.lures[flag] || { fetched: false, url: '' }, bait: s.bait, }); -const { shouldLoad, newAttempt, finished } = getPreviewTracker(30 * 1000); + export function useLure({ flag, branchDomain, @@ -241,25 +247,34 @@ export function useLure({ branchKey: string; disableLoading?: boolean; }) { + const fetchLure = useLureState((state) => state.fetchLure); const { bait, lure } = useLureState(selLure(flag)); lureLogger.log('bait', bait); lureLogger.log('lure', lure); - useEffect(() => { - if (!bait || disableLoading || !shouldLoad(flag)) { - lureLogger.log('skipping', flag, bait, disableLoading, !shouldLoad(flag)); - return; - } - - lureLogger.log('fetching', flag, branchDomain, branchKey); + const canCheckForUpdate = useMemo(() => { + return Boolean(bait && !disableLoading); + }, [bait, disableLoading]); - newAttempt(flag); - useLureState - .getState() - .fetchLure(flag, branchDomain, branchKey) - .finally(() => finished(flag)); - }, [bait, flag, branchDomain, branchKey, disableLoading]); + const uninitialized = useMemo(() => { + return Boolean( + (lure.enabled || !lure.fetched) && + (!lure.url || !checkLureToken(lure.url) || !lure.deepLinkUrl) + ); + }, [lure]); + + lureLogger.log('lure fetcher', { canCheckForUpdate, uninitialized }); + useQuery({ + queryKey: ['lureFetcher', flag], + queryFn: async () => { + lureLogger.log('fetching', flag, branchDomain, branchKey); + await fetchLure(flag, branchDomain, branchKey); + return true; + }, + enabled: canCheckForUpdate && uninitialized, + refetchInterval: 5000, + }); const toggle = async (meta: GroupMeta) => { lureLogger.log('toggling', flag, meta, branchDomain, branchKey); @@ -347,7 +362,7 @@ export function useLureLinkStatus({ return 'disabled'; } - if (!url || !fetched || !checked) { + if (!url || !checkLureToken(url) || !fetched || !checked) { lureLogger.log('loading', fetched, checked, url); return 'loading'; } @@ -363,3 +378,12 @@ export function useLureLinkStatus({ return { status, shareUrl: deepLinkUrl ?? url, toggle }; } + +// hack: we get an intermediate state while generating lure links where +// the returned token will be incorrect. Once it's a @uv we know +// we have the right one +function checkLureToken(url: string | undefined) { + if (!url) return false; + const token = url.split('/').pop(); + return token && token.startsWith('0v'); +} From b2802808ff872db491e204284463ce2e46b29058 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Tue, 17 Sep 2024 12:25:06 -0500 Subject: [PATCH 055/157] Add fixtures, fix skip button action --- apps/tlon-mobile/cosmos.imports.ts | 196 ++++++++---------- .../src/fixtures/AddGroupSheet.fixture.tsx | 20 ++ .../src/fixtures/FindGroups.fixture.tsx | 14 ++ .../src/fixtures/InviteUsersSheet.fixture.tsx | 19 ++ packages/ui/src/components/AddGroupSheet.tsx | 2 +- 5 files changed, 146 insertions(+), 105 deletions(-) create mode 100644 apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx create mode 100644 apps/tlon-mobile/src/fixtures/FindGroups.fixture.tsx create mode 100644 apps/tlon-mobile/src/fixtures/InviteUsersSheet.fixture.tsx diff --git a/apps/tlon-mobile/cosmos.imports.ts b/apps/tlon-mobile/cosmos.imports.ts index f4da6171f0..46c970ac9f 100644 --- a/apps/tlon-mobile/cosmos.imports.ts +++ b/apps/tlon-mobile/cosmos.imports.ts @@ -1,61 +1,66 @@ // This file is automatically generated by Cosmos. Add it to .gitignore and // only edit if you know what you're doing. + import { RendererConfig, UserModuleWrappers } from 'react-cosmos-core'; import * as fixture0 from './src/App.fixture'; -import * as fixture48 from './src/fixtures/ActionSheet/AddGalleryPostSheet.fixture'; -import * as fixture47 from './src/fixtures/ActionSheet/AttachmentSheet.fixture'; -import * as fixture46 from './src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture'; -import * as fixture45 from './src/fixtures/ActionSheet/CreateChannelSheet.fixture'; -import * as fixture44 from './src/fixtures/ActionSheet/DeleteSheet.fixture'; -import * as fixture43 from './src/fixtures/ActionSheet/EditSectionNameSheet.fixture'; -import * as fixture42 from './src/fixtures/ActionSheet/GenericActionSheet.fixture'; -import * as fixture41 from './src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture'; -import * as fixture40 from './src/fixtures/ActionSheet/GroupPreviewSheet.fixture'; -import * as fixture39 from './src/fixtures/ActionSheet/ProfileSheet.fixture'; -import * as fixture38 from './src/fixtures/ActionSheet/SendPostRetrySheet.fixture'; -import * as fixture34 from './src/fixtures/Activity.fixture'; -import * as fixture33 from './src/fixtures/AttachmentPreviewList.fixture'; -import * as fixture32 from './src/fixtures/AudioEmbed.fixture'; -import * as fixture31 from './src/fixtures/Avatar.fixture'; -import * as fixture30 from './src/fixtures/BlockSectionList.fixture'; -import * as fixture29 from './src/fixtures/Button.fixture'; -import * as fixture28 from './src/fixtures/Channel.fixture'; -import * as fixture27 from './src/fixtures/ChannelDivider.fixture'; -import * as fixture26 from './src/fixtures/ChannelHeader.fixture'; -import * as fixture25 from './src/fixtures/ChannelSwitcherSheet.fixture'; -import * as fixture24 from './src/fixtures/ChatMessage.fixture'; -import * as fixture23 from './src/fixtures/ContactList.fixture'; -import * as fixture37 from './src/fixtures/DetailView/ChatDetailView.fixture'; -import * as fixture36 from './src/fixtures/DetailView/GalleryDetailView.fixture'; -import * as fixture35 from './src/fixtures/DetailView/NotebookDetailView.fixture'; -import * as fixture22 from './src/fixtures/Form.fixture'; -import * as fixture21 from './src/fixtures/GalleryPost.fixture'; -import * as fixture20 from './src/fixtures/GroupList.fixture'; -import * as fixture19 from './src/fixtures/GroupListItem.fixture'; -import * as fixture18 from './src/fixtures/ImageViewer.fixture'; -import * as fixture17 from './src/fixtures/Input.fixture'; -import * as fixture16 from './src/fixtures/InputToolbar.fixture'; -import * as fixture15 from './src/fixtures/MessageActions.fixture'; -import * as fixture14 from './src/fixtures/MessageInput.fixture'; -import * as fixture13 from './src/fixtures/MetaEditorScreen.fixture'; -import * as fixture12 from './src/fixtures/OutsideEmbed.fixture'; -import * as fixture11 from './src/fixtures/PostReference.fixture'; -import * as fixture10 from './src/fixtures/PostScreen.fixture'; -import * as fixture9 from './src/fixtures/ProfileBlock.fixture'; -import * as fixture8 from './src/fixtures/ProfileSheet.fixture'; -import * as fixture7 from './src/fixtures/ProfileWidget.fixture'; -import * as fixture6 from './src/fixtures/ReferenceSkeleton.fixture'; -import * as fixture5 from './src/fixtures/ScreenHeader.fixture'; -import * as fixture4 from './src/fixtures/SearchBar.fixture'; -import * as fixture3 from './src/fixtures/Text.fixture'; -import * as fixture2 from './src/fixtures/VideoEmbed.fixture'; import * as fixture1 from './src/fixtures/ViewReactionsSheet.fixture'; +import * as fixture2 from './src/fixtures/VideoEmbed.fixture'; +import * as fixture3 from './src/fixtures/Text.fixture'; +import * as fixture4 from './src/fixtures/SearchBar.fixture'; +import * as fixture5 from './src/fixtures/ScreenHeader.fixture'; +import * as fixture6 from './src/fixtures/ReferenceSkeleton.fixture'; +import * as fixture7 from './src/fixtures/ProfileWidget.fixture'; +import * as fixture8 from './src/fixtures/ProfileSheet.fixture'; +import * as fixture9 from './src/fixtures/ProfileBlock.fixture'; +import * as fixture10 from './src/fixtures/PostScreen.fixture'; +import * as fixture11 from './src/fixtures/PostReference.fixture'; +import * as fixture12 from './src/fixtures/OutsideEmbed.fixture'; +import * as fixture13 from './src/fixtures/MetaEditorScreen.fixture'; +import * as fixture14 from './src/fixtures/MessageInput.fixture'; +import * as fixture15 from './src/fixtures/MessageActions.fixture'; +import * as fixture16 from './src/fixtures/InviteUsersSheet.fixture'; +import * as fixture17 from './src/fixtures/InputToolbar.fixture'; +import * as fixture18 from './src/fixtures/Input.fixture'; +import * as fixture19 from './src/fixtures/ImageViewer.fixture'; +import * as fixture20 from './src/fixtures/GroupListItem.fixture'; +import * as fixture21 from './src/fixtures/GroupList.fixture'; +import * as fixture22 from './src/fixtures/GalleryPost.fixture'; +import * as fixture23 from './src/fixtures/Form.fixture'; +import * as fixture24 from './src/fixtures/FindGroups.fixture'; +import * as fixture25 from './src/fixtures/ContactList.fixture'; +import * as fixture26 from './src/fixtures/ChatMessage.fixture'; +import * as fixture27 from './src/fixtures/ChannelSwitcherSheet.fixture'; +import * as fixture28 from './src/fixtures/ChannelHeader.fixture'; +import * as fixture29 from './src/fixtures/ChannelDivider.fixture'; +import * as fixture30 from './src/fixtures/Channel.fixture'; +import * as fixture31 from './src/fixtures/Button.fixture'; +import * as fixture32 from './src/fixtures/BlockSectionList.fixture'; +import * as fixture33 from './src/fixtures/Avatar.fixture'; +import * as fixture34 from './src/fixtures/AudioEmbed.fixture'; +import * as fixture35 from './src/fixtures/AttachmentPreviewList.fixture'; +import * as fixture36 from './src/fixtures/AddGroupSheet.fixture'; +import * as fixture37 from './src/fixtures/Activity.fixture'; +import * as fixture38 from './src/fixtures/DetailView/NotebookDetailView.fixture'; +import * as fixture39 from './src/fixtures/DetailView/GalleryDetailView.fixture'; +import * as fixture40 from './src/fixtures/DetailView/ChatDetailView.fixture'; +import * as fixture41 from './src/fixtures/ActionSheet/SendPostRetrySheet.fixture'; +import * as fixture42 from './src/fixtures/ActionSheet/ProfileSheet.fixture'; +import * as fixture43 from './src/fixtures/ActionSheet/GroupPreviewSheet.fixture'; +import * as fixture44 from './src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture'; +import * as fixture45 from './src/fixtures/ActionSheet/GenericActionSheet.fixture'; +import * as fixture46 from './src/fixtures/ActionSheet/EditSectionNameSheet.fixture'; +import * as fixture47 from './src/fixtures/ActionSheet/DeleteSheet.fixture'; +import * as fixture48 from './src/fixtures/ActionSheet/CreateChannelSheet.fixture'; +import * as fixture49 from './src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture'; +import * as fixture50 from './src/fixtures/ActionSheet/AttachmentSheet.fixture'; +import * as fixture51 from './src/fixtures/ActionSheet/AddGalleryPostSheet.fixture'; + import * as decorator0 from './src/fixtures/cosmos.decorator'; export const rendererConfig: RendererConfig = { - playgroundUrl: 'http://localhost:5001', - rendererUrl: null, + "playgroundUrl": "http://localhost:5000", + "rendererUrl": null }; const fixtures = { @@ -75,67 +80,50 @@ const fixtures = { 'src/fixtures/MetaEditorScreen.fixture.tsx': { module: fixture13 }, 'src/fixtures/MessageInput.fixture.tsx': { module: fixture14 }, 'src/fixtures/MessageActions.fixture.tsx': { module: fixture15 }, - 'src/fixtures/InputToolbar.fixture.tsx': { module: fixture16 }, - 'src/fixtures/Input.fixture.tsx': { module: fixture17 }, - 'src/fixtures/ImageViewer.fixture.tsx': { module: fixture18 }, - 'src/fixtures/GroupListItem.fixture.tsx': { module: fixture19 }, - 'src/fixtures/GroupList.fixture.tsx': { module: fixture20 }, - 'src/fixtures/GalleryPost.fixture.tsx': { module: fixture21 }, - 'src/fixtures/Form.fixture.tsx': { module: fixture22 }, - 'src/fixtures/ContactList.fixture.tsx': { module: fixture23 }, - 'src/fixtures/ChatMessage.fixture.tsx': { module: fixture24 }, - 'src/fixtures/ChannelSwitcherSheet.fixture.tsx': { module: fixture25 }, - 'src/fixtures/ChannelHeader.fixture.tsx': { module: fixture26 }, - 'src/fixtures/ChannelDivider.fixture.tsx': { module: fixture27 }, - 'src/fixtures/Channel.fixture.tsx': { module: fixture28 }, - 'src/fixtures/Button.fixture.tsx': { module: fixture29 }, - 'src/fixtures/BlockSectionList.fixture.tsx': { module: fixture30 }, - 'src/fixtures/Avatar.fixture.tsx': { module: fixture31 }, - 'src/fixtures/AudioEmbed.fixture.tsx': { module: fixture32 }, - 'src/fixtures/AttachmentPreviewList.fixture.tsx': { module: fixture33 }, - 'src/fixtures/Activity.fixture.tsx': { module: fixture34 }, - 'src/fixtures/DetailView/NotebookDetailView.fixture.tsx': { - module: fixture35, - }, - 'src/fixtures/DetailView/GalleryDetailView.fixture.tsx': { - module: fixture36, - }, - 'src/fixtures/DetailView/ChatDetailView.fixture.tsx': { module: fixture37 }, - 'src/fixtures/ActionSheet/SendPostRetrySheet.fixture.tsx': { - module: fixture38, - }, - 'src/fixtures/ActionSheet/ProfileSheet.fixture.tsx': { module: fixture39 }, - 'src/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx': { - module: fixture40, - }, - 'src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture.tsx': { - module: fixture41, - }, - 'src/fixtures/ActionSheet/GenericActionSheet.fixture.tsx': { - module: fixture42, - }, - 'src/fixtures/ActionSheet/EditSectionNameSheet.fixture.tsx': { - module: fixture43, - }, - 'src/fixtures/ActionSheet/DeleteSheet.fixture.tsx': { module: fixture44 }, - 'src/fixtures/ActionSheet/CreateChannelSheet.fixture.tsx': { - module: fixture45, - }, - 'src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture.tsx': { - module: fixture46, - }, - 'src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx': { module: fixture47 }, - 'src/fixtures/ActionSheet/AddGalleryPostSheet.fixture.tsx': { - module: fixture48, - }, + 'src/fixtures/InviteUsersSheet.fixture.tsx': { module: fixture16 }, + 'src/fixtures/InputToolbar.fixture.tsx': { module: fixture17 }, + 'src/fixtures/Input.fixture.tsx': { module: fixture18 }, + 'src/fixtures/ImageViewer.fixture.tsx': { module: fixture19 }, + 'src/fixtures/GroupListItem.fixture.tsx': { module: fixture20 }, + 'src/fixtures/GroupList.fixture.tsx': { module: fixture21 }, + 'src/fixtures/GalleryPost.fixture.tsx': { module: fixture22 }, + 'src/fixtures/Form.fixture.tsx': { module: fixture23 }, + 'src/fixtures/FindGroups.fixture.tsx': { module: fixture24 }, + 'src/fixtures/ContactList.fixture.tsx': { module: fixture25 }, + 'src/fixtures/ChatMessage.fixture.tsx': { module: fixture26 }, + 'src/fixtures/ChannelSwitcherSheet.fixture.tsx': { module: fixture27 }, + 'src/fixtures/ChannelHeader.fixture.tsx': { module: fixture28 }, + 'src/fixtures/ChannelDivider.fixture.tsx': { module: fixture29 }, + 'src/fixtures/Channel.fixture.tsx': { module: fixture30 }, + 'src/fixtures/Button.fixture.tsx': { module: fixture31 }, + 'src/fixtures/BlockSectionList.fixture.tsx': { module: fixture32 }, + 'src/fixtures/Avatar.fixture.tsx': { module: fixture33 }, + 'src/fixtures/AudioEmbed.fixture.tsx': { module: fixture34 }, + 'src/fixtures/AttachmentPreviewList.fixture.tsx': { module: fixture35 }, + 'src/fixtures/AddGroupSheet.fixture.tsx': { module: fixture36 }, + 'src/fixtures/Activity.fixture.tsx': { module: fixture37 }, + 'src/fixtures/DetailView/NotebookDetailView.fixture.tsx': { module: fixture38 }, + 'src/fixtures/DetailView/GalleryDetailView.fixture.tsx': { module: fixture39 }, + 'src/fixtures/DetailView/ChatDetailView.fixture.tsx': { module: fixture40 }, + 'src/fixtures/ActionSheet/SendPostRetrySheet.fixture.tsx': { module: fixture41 }, + 'src/fixtures/ActionSheet/ProfileSheet.fixture.tsx': { module: fixture42 }, + 'src/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx': { module: fixture43 }, + 'src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture.tsx': { module: fixture44 }, + 'src/fixtures/ActionSheet/GenericActionSheet.fixture.tsx': { module: fixture45 }, + 'src/fixtures/ActionSheet/EditSectionNameSheet.fixture.tsx': { module: fixture46 }, + 'src/fixtures/ActionSheet/DeleteSheet.fixture.tsx': { module: fixture47 }, + 'src/fixtures/ActionSheet/CreateChannelSheet.fixture.tsx': { module: fixture48 }, + 'src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture.tsx': { module: fixture49 }, + 'src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx': { module: fixture50 }, + 'src/fixtures/ActionSheet/AddGalleryPostSheet.fixture.tsx': { module: fixture51 } }; const decorators = { - 'src/fixtures/cosmos.decorator.tsx': { module: decorator0 }, + 'src/fixtures/cosmos.decorator.tsx': { module: decorator0 } }; export const moduleWrappers: UserModuleWrappers = { lazy: false, fixtures, - decorators, + decorators }; diff --git a/apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx b/apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx new file mode 100644 index 0000000000..dcf157917e --- /dev/null +++ b/apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx @@ -0,0 +1,20 @@ +import { AddGroupSheet, AppDataContextProvider } from '@tloncorp/ui'; + +import { FixtureWrapper } from './FixtureWrapper'; +import { initialContacts } from './fakeData'; + +export default { + basic: ( + + + {}} + onGoToDm={() => {}} + onCreatedGroup={() => {}} + navigateToFindGroups={() => {}} + /> + + + ), +}; diff --git a/apps/tlon-mobile/src/fixtures/FindGroups.fixture.tsx b/apps/tlon-mobile/src/fixtures/FindGroups.fixture.tsx new file mode 100644 index 0000000000..d7fc516998 --- /dev/null +++ b/apps/tlon-mobile/src/fixtures/FindGroups.fixture.tsx @@ -0,0 +1,14 @@ +import { AppDataContextProvider, FindGroupsView } from '@tloncorp/ui'; + +import { FixtureWrapper } from './FixtureWrapper'; +import { initialContacts } from './fakeData'; + +export default { + basic: ( + + + {}} onGroupAction={() => {}} /> + + + ), +}; diff --git a/apps/tlon-mobile/src/fixtures/InviteUsersSheet.fixture.tsx b/apps/tlon-mobile/src/fixtures/InviteUsersSheet.fixture.tsx new file mode 100644 index 0000000000..1bc60309e6 --- /dev/null +++ b/apps/tlon-mobile/src/fixtures/InviteUsersSheet.fixture.tsx @@ -0,0 +1,19 @@ +import { AppDataContextProvider, InviteUsersSheet } from '@tloncorp/ui'; + +import { FixtureWrapper } from './FixtureWrapper'; +import { group, initialContacts } from './fakeData'; + +export default { + basic: ( + + + {}} + onInviteComplete={() => {}} + group={group} + /> + + + ), +}; diff --git a/packages/ui/src/components/AddGroupSheet.tsx b/packages/ui/src/components/AddGroupSheet.tsx index ed05826abb..71abdde5a7 100644 --- a/packages/ui/src/components/AddGroupSheet.tsx +++ b/packages/ui/src/components/AddGroupSheet.tsx @@ -341,7 +341,7 @@ function InviteUsersScreen({ { setInvitees([]); - goToScreen('Root'); + goToScreen('CreateGroup'); }} > Skip From 4b81b26c453155114bac5832aedf22b754b6b0a8 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 13:28:32 -0400 Subject: [PATCH 056/157] dont pop system alert on bite --- apps/tlon-mobile/src/hooks/useDeepLinkListener.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index 75d843a047..45159c1ad7 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -16,20 +16,9 @@ export const useDeepLinkListener = () => { if (ship && lure && !isInvitingRef.current) { (async () => { try { - console.log(`bl: inviting ship with lure`, ship, signupParams.lureId); isInvitingRef.current = true; + console.log(`inviting ship with lure`, ship, signupParams.lureId); await inviteShipWithLure({ ship, lure: signupParams.lureId }); - Alert.alert( - '', - 'Your invitation to the group is on its way. It will appear in the Groups list.', - [ - { - text: 'OK', - onPress: () => null, - }, - ], - { cancelable: true } - ); } catch (err) { console.error( '[useDeepLinkListener] Error inviting ship with lure:', From 5327591e559e5cdc6573bf25ab73de57e79f2c9a Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 13:34:03 -0400 Subject: [PATCH 057/157] cleanup --- .../src/screens/Onboarding/InventoryCheckScreen.tsx | 1 - .../src/screens/Onboarding/ReserveShipScreen.tsx | 13 +++---------- .../src/screens/Onboarding/SignUpEmailScreen.tsx | 5 ----- .../src/screens/Onboarding/SignUpPasswordScreen.tsx | 1 - packages/app/contexts/branch.tsx | 1 - 5 files changed, 3 insertions(+), 18 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx index fe0ec58e11..7c7bba0159 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx @@ -33,7 +33,6 @@ export const InventoryCheckScreen = ({ navigation }: Props) => { priorityToken: signupParams.priorityToken, }); if (enabled) { - console.log(`bl: inv check lure`, signupParams.lureId); navigation.navigate('SignUpEmail'); } else { navigation.navigate('JoinWaitList', {}); diff --git a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx index 82fb82014a..1a2c33d433 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx @@ -40,17 +40,14 @@ export const ReserveShipScreen = ({ state: 'loading', }); const { setShip } = useShip(); - const { clearLure } = useBranch(); const startShip = useCallback( async (shipIds: string[]) => { // Fetch statuses for the user's ships and start any required booting/resuming const shipsWithStatus = await getShipsWithStatus(shipIds); if (!shipsWithStatus) { - // return setState({ - // state: 'error', - // error: "Sorry, we couldn't find an active ship for your account.", - // }); + // you can only have gotten to this screen if a new hosting account was created and ship + // was reserved. If we don't see the ship status, assume it's still booting return setState({ state: 'booting' }); } @@ -119,10 +116,6 @@ export const ReserveShipScreen = ({ } } - // We are done using the saved lure link, it can be cleared before dropping user in the app - // TODO: should this be removed? - // clearLure(); - // Set the ship info in the main context to navigate to chat view setShip({ ship, @@ -130,7 +123,7 @@ export const ReserveShipScreen = ({ authCookie, }); }, - [user, signUpExtras, navigation, clearLure, setShip] + [user, signUpExtras, navigation, setShip] ); const reserveShip = useCallback( diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx index 2829a67737..80801c754f 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx @@ -53,11 +53,6 @@ export const SignUpEmailScreen = ({ navigation, route: { params } }: Props) => { setIsSubmitting(true); try { - console.log( - `bl: getting hosting availability`, - email, - signupParams.lureId - ); const { enabled, validEmail } = await getHostingAvailability({ email, lure: signupParams.lureId, diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx index 1e3a84436c..898014c629 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx @@ -109,7 +109,6 @@ export const SignUpPasswordScreen = ({ } try { - console.log(`bl: signing up hosting user`, signupParams.lureId); await signUpHostingUser({ email, password, diff --git a/packages/app/contexts/branch.tsx b/packages/app/contexts/branch.tsx index 4e25553823..d9c72b1a79 100644 --- a/packages/app/contexts/branch.tsx +++ b/packages/app/contexts/branch.tsx @@ -123,7 +123,6 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { }, priorityToken: params.token as string | undefined, }; - console.log(`bl: setting next lure`, nextLure); setState({ ...nextLure, deepLinkPath: undefined, From bb73cc00a5c3b92124d2733ac3156a2385865dd5 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 14:10:04 -0400 Subject: [PATCH 058/157] pull priority token from one spot --- .../src/screens/Onboarding/InventoryCheckScreen.tsx | 3 +-- .../src/screens/Onboarding/SignUpEmailScreen.tsx | 9 ++------- .../src/screens/Onboarding/SignUpPasswordScreen.tsx | 2 +- packages/app/lib/hostingApi.ts | 4 ++-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx index 7c7bba0159..9ac68fbce8 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/InventoryCheckScreen.tsx @@ -1,6 +1,5 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { DEFAULT_LURE, DEFAULT_PRIORITY_TOKEN } from '@tloncorp/app/constants'; -import { useBranch, useSignupParams } from '@tloncorp/app/contexts/branch'; +import { useSignupParams } from '@tloncorp/app/contexts/branch'; import { getHostingAvailability } from '@tloncorp/app/lib/hostingApi'; import { trackError } from '@tloncorp/app/utils/posthog'; import { diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx index 80801c754f..8b68985687 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpEmailScreen.tsx @@ -1,11 +1,6 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { EMAIL_REGEX } from '@tloncorp/app/constants'; import { - DEFAULT_LURE, - DEFAULT_PRIORITY_TOKEN, - EMAIL_REGEX, -} from '@tloncorp/app/constants'; -import { - useBranch, useLureMetadata, useSignupParams, } from '@tloncorp/app/contexts/branch'; @@ -56,7 +51,7 @@ export const SignUpEmailScreen = ({ navigation, route: { params } }: Props) => { const { enabled, validEmail } = await getHostingAvailability({ email, lure: signupParams.lureId, - priorityToken: 'testing2', + priorityToken: signupParams.priorityToken, }); if (!enabled) { diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx index 898014c629..5e49d600f8 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx @@ -114,7 +114,7 @@ export const SignUpPasswordScreen = ({ password, recaptchaToken, lure: signupParams.lureId, - priorityToken: 'testing2', + priorityToken: signupParams.priorityToken, }); } catch (err) { console.error('Error signing up user:', err); diff --git a/packages/app/lib/hostingApi.ts b/packages/app/lib/hostingApi.ts index 5e023d3e8b..4ba13924a7 100644 --- a/packages/app/lib/hostingApi.ts +++ b/packages/app/lib/hostingApi.ts @@ -140,8 +140,8 @@ export const signUpHostingUser = async (params: { body: JSON.stringify({ email: params.email, password: params.password, - lure: params.lure || DEFAULT_LURE, - priorityToken: 'testing2', + lure: params.lure, + priorityToken: params.priorityToken, recaptcha: { recaptchaToken: { token: params.recaptchaToken || '' }, recaptchaPlatform: Platform.OS, From 077862115bc5adc7e663eccbce8fe44522ce6707 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 14:12:50 -0400 Subject: [PATCH 059/157] remove commented out code --- .../tlon-mobile/src/hooks/useDeepLinkListener.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index 45159c1ad7..b753085916 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -3,7 +3,6 @@ import { useShip } from '@tloncorp/app/contexts/ship'; import { inviteShipWithLure } from '@tloncorp/app/lib/hostingApi'; import { trackError } from '@tloncorp/app/utils/posthog'; import { useEffect, useRef } from 'react'; -import { Alert } from 'react-native'; export const useDeepLinkListener = () => { const isInvitingRef = useRef(false); @@ -34,19 +33,4 @@ export const useDeepLinkListener = () => { })(); } }, [ship, signupParams, clearLure, lure]); - - // If deep link clicked, broadcast that navigation update to the webview and mark as handled - // useEffect(() => { - // TODO: hook up deep links without webview - // if (deepLinkPath && webviewContext.appLoaded) { - // console.debug( - // '[useDeepLinkListener] Setting webview path:', - // deepLinkPath - // ); - // webviewContext.setGotoPath(deepLinkPath); - // const tab = parseActiveTab(deepLinkPath) ?? 'Groups'; - // navigation.navigate(tab, { screen: 'Webview' }); - // clearDeepLink(); - // } - // }, [deepLinkPath, navigation, clearDeepLink]); }; From cf713319998e417c03ece3a8f636761a0f52d114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?b=E1=B5=A3=E1=B5=A2=E2=82=90=E2=82=99?= <90741358+latter-bolden@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:14:39 -0400 Subject: [PATCH 060/157] Update packages/shared/src/logic/branch.ts Co-authored-by: Hunter Miller --- packages/shared/src/logic/branch.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/shared/src/logic/branch.ts b/packages/shared/src/logic/branch.ts index 3b2f9c9b2c..c39d18967b 100644 --- a/packages/shared/src/logic/branch.ts +++ b/packages/shared/src/logic/branch.ts @@ -97,9 +97,6 @@ export const createDeepLink = async ({ if (!fallbackUrl || !path) { return undefined; } - // if (type === 'lure' && !urbit.whomIsFlag(path)) { - // return undefined; - // } if (type === 'wer') { const parts = path.split('/'); const isDMLure = From 0a4ba545fcaf7f0b3f65085e745c5decb42c6eed Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 17 Sep 2024 18:26:20 +0000 Subject: [PATCH 061/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index ea4e69ad06..c3dbadc797 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0vje6d9.dk50f.2e9m3.htalj.s9dl9.glob' 0vje6d9.dk50f.2e9m3.htalj.s9dl9] + glob-http+['https://bootstrap.urbit.org/glob-0v1.2r42d.suu7m.tijgl.siagv.eh04m.glob' 0v1.2r42d.suu7m.tijgl.siagv.eh04m] base+'groups' version+[6 3 0] website+'https://tlon.io' From 34da63682c08dda34e3dc69e40642e3814e0fbc6 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 17 Sep 2024 18:48:17 +0000 Subject: [PATCH 062/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index e98cc8b77f..5e1cccc936 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v5.b3vui.av67d.sk82u.om7ka.2e9au.glob' 0v5.b3vui.av67d.sk82u.om7ka.2e9au] + glob-http+['https://bootstrap.urbit.org/glob-0v6.6f6v6.n4k3o.l7rol.pea3l.m9o8u.glob' 0v6.6f6v6.n4k3o.l7rol.pea3l.m9o8u] base+'groups' version+[6 3 0] website+'https://tlon.io' From 6a63ca7d525f6ca9d14f8bb69554d18a3199dacf Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Tue, 17 Sep 2024 16:13:20 -0400 Subject: [PATCH 063/157] profile updates (#3919) * profile screen wip * fix android group selector crash * fix profile header centering * add basic clear option to profile uploaders * fix profile screen back button --- apps/tlon-mobile/cosmos.imports.ts | 20 +- .../AppSettingsScreenController.tsx | 35 --- .../EditProfileScreenController.tsx | 7 +- .../controllers/ProfileScreenController.tsx | 29 +- .../ActionSheet/AttachmentSheet.fixture.tsx | 6 +- .../src/fixtures/ProfileWidget.fixture.tsx | 46 --- .../fixtures/UserProfileScreen.fixture.tsx | 46 +++ apps/tlon-mobile/src/navigation/RootStack.tsx | 2 - apps/tlon-web-new/src/app.tsx | 2 - .../AppSettingsScreenController.tsx | 32 --- .../controllers/ProfileScreenController.tsx | 27 +- .../features/settings/AppSettingsScreen.tsx | 90 ------ .../features/settings/EditProfileScreen.tsx | 12 +- .../app/features/settings/ProfileScreen.tsx | 92 +++--- packages/app/features/top/ActivityScreen.tsx | 12 +- packages/app/features/top/ChatListScreen.tsx | 12 +- .../app/features/top/UserProfileScreen.tsx | 11 +- .../app/features/top/useConnectionStatus.tsx | 17 ++ packages/shared/src/api/index.ts | 1 + packages/shared/src/api/urbit.ts | 11 +- packages/shared/src/api/vitalsApi.ts | 65 +++++ packages/shared/src/urbit/index.ts | 1 + packages/shared/src/urbit/vitals.ts | 99 +++++++ packages/ui/src/assets/Tlon.svg | 3 + packages/ui/src/assets/icons/Info.svg | 3 + packages/ui/src/assets/icons/index.ts | 1 + packages/ui/src/components/AddGalleryPost.tsx | 6 +- .../ui/src/components/AttachmentSheet.tsx | 58 ++-- .../ui/src/components/BigInput.native.tsx | 6 +- packages/ui/src/components/BigInput.tsx | 6 +- packages/ui/src/components/BioDisplay.tsx | 12 - .../src/components/EditProfileScreenView.tsx | 178 ++++++------ .../src/components/EditableProfileImages.tsx | 53 +++- .../src/components/FavoriteGroupsDisplay.tsx | 15 +- packages/ui/src/components/Form/Form.tsx | 2 +- .../src/components/Form/controlledFields.tsx | 28 ++ .../ui/src/components/GroupSelectorSheet.tsx | 53 ++-- .../MessageInput/AttachmentButton.native.tsx | 6 +- .../MessageInput/AttachmentButton.tsx | 6 +- packages/ui/src/components/NavBarView.tsx | 10 +- .../ui/src/components/ProfileScreenView.tsx | 199 +++++++------ packages/ui/src/components/ScreenHeader.tsx | 11 +- packages/ui/src/components/TlonLogo.tsx | 17 ++ .../src/components/UserProfileScreenView.tsx | 269 ++++++++++++++---- packages/ui/src/components/WidgetPane.ts | 12 +- 45 files changed, 970 insertions(+), 659 deletions(-) delete mode 100644 apps/tlon-mobile/src/controllers/AppSettingsScreenController.tsx delete mode 100644 apps/tlon-mobile/src/fixtures/ProfileWidget.fixture.tsx create mode 100644 apps/tlon-mobile/src/fixtures/UserProfileScreen.fixture.tsx delete mode 100644 apps/tlon-web-new/src/controllers/AppSettingsScreenController.tsx delete mode 100644 packages/app/features/settings/AppSettingsScreen.tsx create mode 100644 packages/app/features/top/useConnectionStatus.tsx create mode 100644 packages/shared/src/api/vitalsApi.ts create mode 100644 packages/shared/src/urbit/vitals.ts create mode 100644 packages/ui/src/assets/Tlon.svg create mode 100644 packages/ui/src/assets/icons/Info.svg delete mode 100644 packages/ui/src/components/BioDisplay.tsx create mode 100644 packages/ui/src/components/TlonLogo.tsx diff --git a/apps/tlon-mobile/cosmos.imports.ts b/apps/tlon-mobile/cosmos.imports.ts index f4da6171f0..a2f399f3eb 100644 --- a/apps/tlon-mobile/cosmos.imports.ts +++ b/apps/tlon-mobile/cosmos.imports.ts @@ -44,11 +44,11 @@ import * as fixture11 from './src/fixtures/PostReference.fixture'; import * as fixture10 from './src/fixtures/PostScreen.fixture'; import * as fixture9 from './src/fixtures/ProfileBlock.fixture'; import * as fixture8 from './src/fixtures/ProfileSheet.fixture'; -import * as fixture7 from './src/fixtures/ProfileWidget.fixture'; -import * as fixture6 from './src/fixtures/ReferenceSkeleton.fixture'; -import * as fixture5 from './src/fixtures/ScreenHeader.fixture'; -import * as fixture4 from './src/fixtures/SearchBar.fixture'; -import * as fixture3 from './src/fixtures/Text.fixture'; +import * as fixture7 from './src/fixtures/ReferenceSkeleton.fixture'; +import * as fixture6 from './src/fixtures/ScreenHeader.fixture'; +import * as fixture5 from './src/fixtures/SearchBar.fixture'; +import * as fixture4 from './src/fixtures/Text.fixture'; +import * as fixture3 from './src/fixtures/UserProfileScreen.fixture'; import * as fixture2 from './src/fixtures/VideoEmbed.fixture'; import * as fixture1 from './src/fixtures/ViewReactionsSheet.fixture'; import * as decorator0 from './src/fixtures/cosmos.decorator'; @@ -62,11 +62,11 @@ const fixtures = { 'src/App.fixture.tsx': { module: fixture0 }, 'src/fixtures/ViewReactionsSheet.fixture.tsx': { module: fixture1 }, 'src/fixtures/VideoEmbed.fixture.tsx': { module: fixture2 }, - 'src/fixtures/Text.fixture.tsx': { module: fixture3 }, - 'src/fixtures/SearchBar.fixture.tsx': { module: fixture4 }, - 'src/fixtures/ScreenHeader.fixture.tsx': { module: fixture5 }, - 'src/fixtures/ReferenceSkeleton.fixture.tsx': { module: fixture6 }, - 'src/fixtures/ProfileWidget.fixture.tsx': { module: fixture7 }, + 'src/fixtures/UserProfileScreen.fixture.tsx': { module: fixture3 }, + 'src/fixtures/Text.fixture.tsx': { module: fixture4 }, + 'src/fixtures/SearchBar.fixture.tsx': { module: fixture5 }, + 'src/fixtures/ScreenHeader.fixture.tsx': { module: fixture6 }, + 'src/fixtures/ReferenceSkeleton.fixture.tsx': { module: fixture7 }, 'src/fixtures/ProfileSheet.fixture.tsx': { module: fixture8 }, 'src/fixtures/ProfileBlock.fixture.tsx': { module: fixture9 }, 'src/fixtures/PostScreen.fixture.tsx': { module: fixture10 }, diff --git a/apps/tlon-mobile/src/controllers/AppSettingsScreenController.tsx b/apps/tlon-mobile/src/controllers/AppSettingsScreenController.tsx deleted file mode 100644 index 4584323e61..0000000000 --- a/apps/tlon-mobile/src/controllers/AppSettingsScreenController.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { AppSettingsScreen } from '@tloncorp/app/features/settings/AppSettingsScreen'; -import { useCallback } from 'react'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function AppSettingsScreenController(props: Props) { - const onManageAccountPressed = useCallback(() => { - props.navigation.navigate('ManageAccount'); - }, [props.navigation]); - - const onAppInfoPressed = useCallback(() => { - props.navigation.navigate('AppInfo'); - }, [props.navigation]); - - const onPushNotifPressed = useCallback(() => { - props.navigation.navigate('PushNotificationSettings'); - }, [props.navigation]); - - const onBlockedUsersPressed = useCallback(() => { - props.navigation.navigate('BlockedUsers'); - }, [props.navigation]); - - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx b/apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx index 9cf43ecfbb..61446e5b2f 100644 --- a/apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx +++ b/apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx @@ -1,15 +1,10 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'; import { EditProfileScreen } from '@tloncorp/app/features/settings/EditProfileScreen'; -import { useCallback } from 'react'; import { RootStackParamList } from '../types'; type Props = NativeStackScreenProps; export function EditProfileScreenController(props: Props) { - const onGoBack = useCallback(() => { - props.navigation.goBack(); - }, [props.navigation]); - - return ; + return props.navigation.goBack()} />; } diff --git a/apps/tlon-mobile/src/controllers/ProfileScreenController.tsx b/apps/tlon-mobile/src/controllers/ProfileScreenController.tsx index 551780927b..4502125a14 100644 --- a/apps/tlon-mobile/src/controllers/ProfileScreenController.tsx +++ b/apps/tlon-mobile/src/controllers/ProfileScreenController.tsx @@ -2,6 +2,7 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'; import ProfileScreen from '@tloncorp/app/features/settings/ProfileScreen'; import { useHandleLogout } from '@tloncorp/app/hooks/useHandleLogout'; import { resetDb } from '@tloncorp/app/lib/nativeDb'; +import { useCallback } from 'react'; import { RootStackParamList } from '../types'; @@ -10,17 +11,39 @@ type Props = NativeStackScreenProps; export function ProfileScreenController(props: Props) { const handleLogout = useHandleLogout({ resetDb }); + const onManageAccountPressed = useCallback(() => { + props.navigation.navigate('ManageAccount'); + }, [props.navigation]); + + const onAppInfoPressed = useCallback(() => { + props.navigation.navigate('AppInfo'); + }, [props.navigation]); + + const onPushNotifPressed = useCallback(() => { + props.navigation.navigate('PushNotificationSettings'); + }, [props.navigation]); + + const onBlockedUsersPressed = useCallback(() => { + props.navigation.navigate('BlockedUsers'); + }, [props.navigation]); + return ( props.navigation.navigate('AppSettings')} navigateToEditProfile={() => props.navigation.navigate('EditProfile')} navigateToErrorReport={() => props.navigation.navigate('WompWomp')} - navigateToProfile={(userId: string) => + navigateToContactProfile={(userId: string) => props.navigation.navigate('UserProfile', { userId }) } + navigateToProfileSettings={() => { + console.log('profile'); + props.navigation.navigate('Profile'); + }} navigateToHome={() => props.navigation.navigate('ChatList')} navigateToNotifications={() => props.navigation.navigate('Activity')} - navigateToSettings={() => props.navigation.navigate('Profile')} + navigateToManageAccount={onManageAccountPressed} + navigateToAppInfo={onAppInfoPressed} + navigateToNotificationSettings={onPushNotifPressed} + navigateToBlockedUsers={onBlockedUsersPressed} handleLogout={handleLogout} /> ); diff --git a/apps/tlon-mobile/src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx b/apps/tlon-mobile/src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx index 85fa02f4f7..5593463ca4 100644 --- a/apps/tlon-mobile/src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx +++ b/apps/tlon-mobile/src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx @@ -4,9 +4,9 @@ import AttachmentSheet from '@tloncorp/ui/src/components/AttachmentSheet'; export default ( {}} - setShowAttachmentSheet={() => {}} - showAttachmentSheet={true} + onAttachmentsSet={() => {}} + onOpenChange={() => {}} + isOpen={true} /> ); diff --git a/apps/tlon-mobile/src/fixtures/ProfileWidget.fixture.tsx b/apps/tlon-mobile/src/fixtures/ProfileWidget.fixture.tsx deleted file mode 100644 index 293a2419f2..0000000000 --- a/apps/tlon-mobile/src/fixtures/ProfileWidget.fixture.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ProfileDisplayWidget, View } from '@tloncorp/ui'; - -import { FixtureWrapper } from './FixtureWrapper'; -import { brianContact, danContact, markContact } from './fakeData'; - -export default { - 'with cover': () => ( - - - - - - ), - 'with nickname': () => ( - - - - - - ), - base: () => ( - - - - - - ), -}; diff --git a/apps/tlon-mobile/src/fixtures/UserProfileScreen.fixture.tsx b/apps/tlon-mobile/src/fixtures/UserProfileScreen.fixture.tsx new file mode 100644 index 0000000000..da8a08e716 --- /dev/null +++ b/apps/tlon-mobile/src/fixtures/UserProfileScreen.fixture.tsx @@ -0,0 +1,46 @@ +import { faker } from '@faker-js/faker'; +import * as db from '@tloncorp/shared/dist/db'; +import { + AppDataContextProvider, + UserProfileScreenView, +} from '@tloncorp/ui/src'; + +import { FixtureWrapper } from './FixtureWrapper'; +import { group } from './fakeData'; + +const exampleContacts: Record = { + zod: { + id: '~fabled-faster', + nickname: 'É. Urcades', + color: faker.color.rgb(), + bio: faker.lorem.paragraphs(2), + avatarImage: faker.image.avatar(), + coverImage: faker.image.urlLoremFlickr(), + pinnedGroups: [ + { + contactId: '~fabled-faster', + groupId: group.id, + group, + }, + ], + }, +}; + +function ProfileScreenFixture() { + return ( + + + {}} + connectionStatus={{ complete: true, status: 'yes' }} + /> + + + ); +} + +export default ; diff --git a/apps/tlon-mobile/src/navigation/RootStack.tsx b/apps/tlon-mobile/src/navigation/RootStack.tsx index 1db814d75f..9fa50559d3 100644 --- a/apps/tlon-mobile/src/navigation/RootStack.tsx +++ b/apps/tlon-mobile/src/navigation/RootStack.tsx @@ -6,7 +6,6 @@ import { Platform, StatusBar } from 'react-native'; import { ActivityScreenController } from '../controllers/ActivityScreenController'; import { AppInfoScreenController } from '../controllers/AppInfoScreenController'; -import { AppSettingsScreenController } from '../controllers/AppSettingsScreenController'; import { BlockedUsersScreenController } from '../controllers/BlockedUsersScreenController'; import { ChannelMembersScreenController } from '../controllers/ChannelMembersScreenController'; import { ChannelMetaScreenController } from '../controllers/ChannelMetaScreenController'; @@ -84,7 +83,6 @@ export function RootStack() { options={{ animation: 'fade' }} /> - } /> } /> - } /> } diff --git a/apps/tlon-web-new/src/controllers/AppSettingsScreenController.tsx b/apps/tlon-web-new/src/controllers/AppSettingsScreenController.tsx deleted file mode 100644 index de9eda25d3..0000000000 --- a/apps/tlon-web-new/src/controllers/AppSettingsScreenController.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { AppSettingsScreen } from '@tloncorp/app/features/settings/AppSettingsScreen'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function AppSettingsScreenController() { - const navigate = useNavigate(); - const onManageAccountPressed = useCallback(() => { - navigate('/settings/manage-account'); - }, [navigate]); - - const onAppInfoPressed = useCallback(() => { - navigate('/settings/app-info'); - }, [navigate]); - - const onPushNotifPressed = useCallback(() => { - navigate('/settings/push-notifications'); - }, [navigate]); - - const onBlockedUsersPressed = useCallback(() => { - navigate('/settings/blocked-users'); - }, [navigate]); - - return ( - navigate(-1)} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/ProfileScreenController.tsx b/apps/tlon-web-new/src/controllers/ProfileScreenController.tsx index 9db8b14af9..c6974abddd 100644 --- a/apps/tlon-web-new/src/controllers/ProfileScreenController.tsx +++ b/apps/tlon-web-new/src/controllers/ProfileScreenController.tsx @@ -1,18 +1,39 @@ import ProfileScreen from '@tloncorp/app/features/settings/ProfileScreen'; +import { useCallback } from 'react'; import { useNavigate } from 'react-router'; export function ProfileScreenController() { const navigate = useNavigate(); + const onManageAccountPressed = useCallback(() => { + navigate('/settings/manage-account'); + }, [navigate]); + + const onAppInfoPressed = useCallback(() => { + navigate('/settings/app-info'); + }, [navigate]); + + const onPushNotifPressed = useCallback(() => { + navigate('/settings/push-notifications'); + }, [navigate]); + + const onBlockedUsersPressed = useCallback(() => { + navigate('/settings/blocked-users'); + }, [navigate]); return ( navigate('/settings')} navigateToEditProfile={() => navigate('/profile/edit')} navigateToErrorReport={() => navigate('/bug-report')} - navigateToProfile={(userId: string) => navigate(`/profile/${userId}`)} + navigateToContactProfile={(userId: string) => + navigate(`/profile/${userId}`) + } navigateToHome={() => navigate('/')} navigateToNotifications={() => navigate('/activity')} - navigateToSettings={() => navigate('/profile')} + navigateToProfileSettings={() => navigate('/profile')} + navigateToManageAccount={onManageAccountPressed} + navigateToAppInfo={onAppInfoPressed} + navigateToNotificationSettings={onPushNotifPressed} + navigateToBlockedUsers={onBlockedUsersPressed} /> ); } diff --git a/packages/app/features/settings/AppSettingsScreen.tsx b/packages/app/features/settings/AppSettingsScreen.tsx deleted file mode 100644 index 0eb575cccb..0000000000 --- a/packages/app/features/settings/AppSettingsScreen.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { GenericHeader, IconType, ListItem, View, YStack } from '@tloncorp/ui'; -import { useEffect, useState } from 'react'; -import { ScrollView } from 'react-native-gesture-handler'; - -import { getHostingToken, getHostingUserId } from '../../utils/hosting'; - -export function AppSettingsScreen({ - onManageAccountPressed, - onAppInfoPressed, - onPushNotifPressed, - onBlockedUsersPressed, - onGoBack, -}: { - onManageAccountPressed: () => void; - onAppInfoPressed: () => void; - onPushNotifPressed: () => void; - onBlockedUsersPressed: () => void; - onGoBack: () => void; -}) { - const [hasHostedAuth, setHasHostedAuth] = useState(false); - - useEffect(() => { - async function getHostingInfo() { - const [cookie, userId] = await Promise.all([ - getHostingToken(), - getHostingUserId(), - ]); - if (cookie && userId) { - setHasHostedAuth(true); - } - } - getHostingInfo(); - }, []); - - return ( - - - - - - - - {hasHostedAuth && ( - - )} - - - - ); -} - -function AppInfoListItem({ - onPress, - title, - icon, -}: { - onPress: () => void; - title: string; - icon: IconType; -}) { - return ( - - - - {title} - - - - - ); -} diff --git a/packages/app/features/settings/EditProfileScreen.tsx b/packages/app/features/settings/EditProfileScreen.tsx index 7c0f3a2e61..b214166080 100644 --- a/packages/app/features/settings/EditProfileScreen.tsx +++ b/packages/app/features/settings/EditProfileScreen.tsx @@ -1,7 +1,11 @@ import * as api from '@tloncorp/shared/dist/api'; import * as db from '@tloncorp/shared/dist/db'; import * as store from '@tloncorp/shared/dist/store'; -import { EditProfileScreenView, GroupsProvider, View } from '@tloncorp/ui'; +import { + AttachmentProvider, + EditProfileScreenView, + GroupsProvider, +} from '@tloncorp/ui'; import { useCallback } from 'react'; export function EditProfileScreen({ onGoBack }: { onGoBack: () => void }) { @@ -27,14 +31,12 @@ export function EditProfileScreen({ onGoBack }: { onGoBack: () => void }) { return ( - + - + ); } diff --git a/packages/app/features/settings/ProfileScreen.tsx b/packages/app/features/settings/ProfileScreen.tsx index 4cba5b7cc8..5c086efd25 100644 --- a/packages/app/features/settings/ProfileScreen.tsx +++ b/packages/app/features/settings/ProfileScreen.tsx @@ -1,76 +1,84 @@ import { NavBarView, ProfileScreenView, View } from '@tloncorp/ui'; -import { useCallback } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useDMLureLink } from '../../hooks/useBranchLink'; import { useCurrentUserId } from '../../hooks/useCurrentUser'; +import { getHostingToken, getHostingUserId } from '../../utils/hosting'; export default function ProfileScreen({ - navigateToAppSettings, navigateToEditProfile, navigateToErrorReport, - navigateToProfile, + navigateToContactProfile, navigateToHome, navigateToNotifications, - navigateToSettings, + navigateToProfileSettings, + navigateToBlockedUsers, + navigateToManageAccount, + navigateToAppInfo, + navigateToNotificationSettings, handleLogout, }: { - navigateToAppSettings: () => void; navigateToEditProfile: () => void; navigateToErrorReport: () => void; - navigateToProfile: (userId: string) => void; + navigateToContactProfile: (userId: string) => void; navigateToHome: () => void; navigateToNotifications: () => void; - navigateToSettings: () => void; + navigateToProfileSettings: () => void; + navigateToAppInfo?: () => void; + navigateToNotificationSettings: () => void; + navigateToBlockedUsers: () => void; + navigateToManageAccount: () => void; handleLogout?: () => void; }) { const currentUserId = useCurrentUserId(); - - const onAppSettingsPressed = useCallback(() => { - navigateToAppSettings(); - }, [navigateToAppSettings]); - - const onEditProfilePressed = useCallback(() => { - navigateToEditProfile(); - }, [navigateToEditProfile]); - - const onSendBugReportPressed = useCallback(() => { - navigateToErrorReport(); - }, [navigateToErrorReport]); - - const onViewProfilePressed = useCallback(() => { - navigateToProfile(currentUserId); - }, [currentUserId, navigateToProfile]); - const { dmLink } = useDMLureLink(); + const hasHostedAuth = useHasHostedAuth(); + + const handleViewProfile = useCallback(() => { + navigateToContactProfile(currentUserId); + }, [currentUserId, navigateToContactProfile]); return ( { - if (handleLogout) { - handleLogout(); - } - }} - onSendBugReportPressed={onSendBugReportPressed} - onViewProfile={onViewProfilePressed} + onEditProfilePressed={navigateToEditProfile} + onLogoutPressed={handleLogout} + onSendBugReportPressed={navigateToErrorReport} + onAppInfoPressed={navigateToAppInfo} + onNotificationSettingsPressed={navigateToNotificationSettings} + onBlockedUsersPressed={navigateToBlockedUsers} + onManageAccountPressed={navigateToManageAccount} + onViewProfile={handleViewProfile} dmLink={dmLink} /> { - navigateToHome(); - }} - navigateToNotifications={() => { - navigateToNotifications(); - }} - navigateToProfile={() => { - navigateToSettings(); - }} + navigateToHome={navigateToHome} + navigateToNotifications={navigateToNotifications} + navigateToProfileSettings={navigateToProfileSettings} currentRoute="Profile" currentUserId={currentUserId} /> ); } + +function useHasHostedAuth() { + const [hasHostedAuth, setHasHostedAuth] = useState(false); + + useEffect(() => { + async function getHostingInfo() { + const [cookie, userId] = await Promise.all([ + getHostingToken(), + getHostingUserId(), + ]); + if (cookie && userId) { + setHasHostedAuth(true); + } + } + getHostingInfo(); + }, []); + + return hasHostedAuth; +} diff --git a/packages/app/features/top/ActivityScreen.tsx b/packages/app/features/top/ActivityScreen.tsx index ca3904a6c1..75e0ccf3ea 100644 --- a/packages/app/features/top/ActivityScreen.tsx +++ b/packages/app/features/top/ActivityScreen.tsx @@ -76,15 +76,9 @@ export function ActivityScreen({ refresh={handleRefreshActivity} /> { - navigateToChatList(); - }} - navigateToNotifications={() => { - navigateToActivity(); - }} - navigateToProfile={() => { - navigateToProfile(); - }} + navigateToHome={navigateToChatList} + navigateToNotifications={navigateToActivity} + navigateToProfileSettings={navigateToProfile} currentRoute="Activity" currentUserId={currentUserId} /> diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx index 7597975975..06213ea5e0 100644 --- a/packages/app/features/top/ChatListScreen.tsx +++ b/packages/app/features/top/ChatListScreen.tsx @@ -308,15 +308,9 @@ export default function ChatListScreen({ /> { - navigateToHome(); - }} - navigateToNotifications={() => { - navigateToNotifications(); - }} - navigateToProfile={() => { - navigateToProfile(); - }} + navigateToHome={navigateToHome} + navigateToNotifications={navigateToNotifications} + navigateToProfileSettings={navigateToProfile} currentRoute="ChatList" currentUserId={currentUser} /> diff --git a/packages/app/features/top/UserProfileScreen.tsx b/packages/app/features/top/UserProfileScreen.tsx index cdbc223d7b..fedc43865f 100644 --- a/packages/app/features/top/UserProfileScreen.tsx +++ b/packages/app/features/top/UserProfileScreen.tsx @@ -3,10 +3,10 @@ import { AppDataContextProvider, NavigationProvider, UserProfileScreenView, - View, } from '@tloncorp/ui'; import { useCurrentUserId } from '../../hooks/useCurrentUser'; +import { useConnectionStatus } from './useConnectionStatus'; export function UserProfileScreen({ userId, @@ -19,6 +19,7 @@ export function UserProfileScreen({ }) { const currentUserId = useCurrentUserId(); const { data: contacts } = store.useContacts(); + const connectionStatus = useConnectionStatus(userId); return ( - - - + ); diff --git a/packages/app/features/top/useConnectionStatus.tsx b/packages/app/features/top/useConnectionStatus.tsx new file mode 100644 index 0000000000..dac4d91895 --- /dev/null +++ b/packages/app/features/top/useConnectionStatus.tsx @@ -0,0 +1,17 @@ +import * as api from '@tloncorp/shared/dist/api'; +import { debounce } from 'lodash'; +import { ConnectionStatus } from 'packages/shared/dist/api'; +import { useEffect, useState } from 'react'; + +export const useConnectionStatus = (contactId: string) => { + const [connectionStatus, setConnectionStatus] = + useState(null); + + useEffect(() => { + api.checkConnectionStatus( + contactId, + debounce(setConnectionStatus, 500, { trailing: true }) + ); + }, [contactId]); + return connectionStatus; +}; diff --git a/packages/shared/src/api/index.ts b/packages/shared/src/api/index.ts index 54f04d55fc..b89ea29f41 100644 --- a/packages/shared/src/api/index.ts +++ b/packages/shared/src/api/index.ts @@ -15,3 +15,4 @@ export * from './settingsApi'; export * from './activityApi'; export * from './harkApi'; export * from './storageApi'; +export * from './vitalsApi'; diff --git a/packages/shared/src/api/urbit.ts b/packages/shared/src/api/urbit.ts index bd767c9318..8754ed226d 100644 --- a/packages/shared/src/api/urbit.ts +++ b/packages/shared/src/api/urbit.ts @@ -138,7 +138,7 @@ export function subscribe( printEndpoint(endpoint) ); - clientInstance.subscribe({ + return clientInstance.subscribe({ app: endpoint.app, path: endpoint.path, event: (event: any, mark: string, id?: number) => { @@ -176,6 +176,15 @@ export function subscribe( }); } +export const unsubscribe = (subcriptionId: number) => { + if (!clientInstance) { + throw new Error( + 'Tried to unsubscribe, but Urbit client is not initialized' + ); + } + clientInstance.unsubscribe(subcriptionId); +}; + export const subscribeOnce = async ( endpoint: UrbitEndpoint, timeout?: number diff --git a/packages/shared/src/api/vitalsApi.ts b/packages/shared/src/api/vitalsApi.ts new file mode 100644 index 0000000000..a87f75e09f --- /dev/null +++ b/packages/shared/src/api/vitalsApi.ts @@ -0,0 +1,65 @@ +import * as ub from '../urbit'; +import { poke, scry, subscribe, unsubscribe } from './urbit'; + +export const getLastConnectionStatus = async (contactId: string) => { + const result = await scry({ + app: 'vitals', + path: `/ship/${contactId}`, + }); + return toConnectionStatus(result); +}; + +export const checkConnectionStatus = async ( + contactId: string, + callback: (data: ConnectionStatus) => void +) => { + const subscription = await subscribe( + { + app: 'vitals', + path: `/status/${contactId}`, + }, + (e) => { + callback(toConnectionStatus(e)); + if ('complete' in e.status) { + setTimeout(() => unsubscribe(subscription), 1000); + } + } + ); + + return poke({ + app: 'vitals', + mark: 'run-check', + json: contactId, + }); +}; + +export type ConnectionState = + | 'yes' + | 'crash' + | 'no-data' + | 'no-dns' + | 'no-our-planet' + | 'no-our-galaxy' + | 'no-sponsor-hit' + | 'no-sponsor-miss' + | 'no-their-galaxy' + | 'setting-up' + | 'trying-dns' + | 'trying-local' + | 'trying-target' + | 'trying-sponsor'; + +export type ConnectionStatus = { + status: ConnectionState; + complete: boolean; +}; + +export const toConnectionStatus = ( + data: ub.ConnectionUpdate +): ConnectionStatus => { + if ('complete' in data.status) { + return { complete: true, status: data.status.complete }; + } else { + return { complete: false, status: data.status.pending }; + } +}; diff --git a/packages/shared/src/urbit/index.ts b/packages/shared/src/urbit/index.ts index e8a344ea44..28b81ca429 100644 --- a/packages/shared/src/urbit/index.ts +++ b/packages/shared/src/urbit/index.ts @@ -28,3 +28,4 @@ export * from './volume'; export * from './utils'; export * from './settings'; export * from './storage'; +export * from './vitals'; diff --git a/packages/shared/src/urbit/vitals.ts b/packages/shared/src/urbit/vitals.ts new file mode 100644 index 0000000000..73163f3324 --- /dev/null +++ b/packages/shared/src/urbit/vitals.ts @@ -0,0 +1,99 @@ +export interface Connected { + complete: 'yes'; +} + +export interface YetToCheck { + complete: 'no-data'; +} + +export interface NoDNS { + complete: 'no-dns'; +} + +export interface Crash { + complete: 'crash'; + crash: string[][]; +} + +export interface NoOurPlanet { + complete: 'no-our-planet'; + 'last-contact': number; +} + +export interface NoOurGalaxy { + complete: 'no-our-galaxy'; + 'last-contact': number; +} + +export interface NoSponsorHit { + complete: 'no-sponsor-hit'; + ship: string; +} + +export interface NoSponsorMiss { + complete: 'no-sponsor-miss'; + ship: string; +} + +export interface NoTheirGalaxy { + complete: 'no-their-galaxy'; + 'last-contact': number; +} + +export type ConnectionCompleteStatusKey = + | 'yes' + | 'crash' + | 'no-data' + | 'no-dns' + | 'no-our-planet' + | 'no-our-galaxy' + | 'no-sponsor-hit' + | 'no-sponsor-miss' + | 'no-their-galaxy'; + +export interface CompleteStatus { + complete: ConnectionCompleteStatusKey; +} + +export type ConnectionCompleteStatus = + | Connected + | YetToCheck + | Crash + | NoDNS + | NoOurPlanet + | NoOurGalaxy + | NoSponsorHit + | NoSponsorMiss + | NoTheirGalaxy; + +export type ConnectionPendingStatusKey = + | 'setting-up' + | 'trying-dns' + | 'trying-local' + | 'trying-target' + | 'trying-sponsor'; + +export type ConnectionPendingStatus = + | { + pending: Exclude; + } + | { + pending: 'trying-sponsor'; + ship: string; + }; + +export type ConnectionStatus = + | ConnectionCompleteStatus + | ConnectionPendingStatus; + +export interface ConnectionUpdate { + status: ConnectionStatus; + timestamp: number; +} + +export interface ConnectivityCheckOptions { + useStale?: boolean; + enabled?: boolean; + staleTime?: number; + waitToDisplay?: number; +} diff --git a/packages/ui/src/assets/Tlon.svg b/packages/ui/src/assets/Tlon.svg new file mode 100644 index 0000000000..1de906c6fa --- /dev/null +++ b/packages/ui/src/assets/Tlon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/Info.svg b/packages/ui/src/assets/icons/Info.svg new file mode 100644 index 0000000000..ef384a6071 --- /dev/null +++ b/packages/ui/src/assets/icons/Info.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/index.ts b/packages/ui/src/assets/icons/index.ts index ae94ac9679..b75d9a3332 100644 --- a/packages/ui/src/assets/icons/index.ts +++ b/packages/ui/src/assets/icons/index.ts @@ -46,6 +46,7 @@ export { default as HomeFilled } from './HomeFilled.svg'; export { default as IndentDecrease } from './IndentDecrease.svg'; export { default as IndentIncrease } from './IndentIncrease.svg'; export { default as Italic } from './Italic.svg'; +export { default as Info } from './Info.svg'; export { default as Keyboard } from './Keyboard.svg'; export { default as Label } from './Label.svg'; export { default as LeftSidebar } from './LeftSidebar.svg'; diff --git a/packages/ui/src/components/AddGalleryPost.tsx b/packages/ui/src/components/AddGalleryPost.tsx index c7006c1e77..3d80885fe0 100644 --- a/packages/ui/src/components/AddGalleryPost.tsx +++ b/packages/ui/src/components/AddGalleryPost.tsx @@ -52,9 +52,9 @@ export default function AddGalleryPost({ actions={actions} /> ); diff --git a/packages/ui/src/components/AttachmentSheet.tsx b/packages/ui/src/components/AttachmentSheet.tsx index 450846c615..d8e1131102 100644 --- a/packages/ui/src/components/AttachmentSheet.tsx +++ b/packages/ui/src/components/AttachmentSheet.tsx @@ -1,26 +1,30 @@ import { MessageAttachments } from '@tloncorp/shared/dist/api'; import * as ImagePicker from 'expo-image-picker'; -import { useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; -import { ActionGroup, ActionSheet } from './ActionSheet'; +import { ActionGroup, ActionSheet, createActionGroup } from './ActionSheet'; import { ListItem } from './ListItem'; export default function AttachmentSheet({ - showAttachmentSheet, - setShowAttachmentSheet, - setImage, + isOpen: showAttachmentSheet, + onOpenChange: onOpenChange, + showClearOption, + onClearAttachments, + onAttachmentsSet: onAttachmentsSet, }: { - showAttachmentSheet: boolean; - setShowAttachmentSheet: (open: boolean) => void; - setImage: (attachments: MessageAttachments) => void; + isOpen: boolean; + showClearOption?: boolean; + onClearAttachments?: () => void; + onOpenChange: (open: boolean) => void; + onAttachmentsSet: (attachments: MessageAttachments) => void; }) { const [mediaLibraryPermissionStatus, requestMediaLibraryPermission] = ImagePicker.useMediaLibraryPermissions(); const [cameraPermissionStatus, requestCameraPermission] = ImagePicker.useCameraPermissions(); - const takePicture = async () => { - setShowAttachmentSheet(false); + const takePicture = useCallback(async () => { + onOpenChange(false); // The image picker is attempting to mount inside the sheet, but // the sheet closes before the picker can mount. This adds // a slight timeout to let the picker have enough time to mount. @@ -33,12 +37,12 @@ export default function AttachmentSheet({ }); if (!result.canceled) { - setImage(result.assets); + onAttachmentsSet(result.assets); } - }; + }, [onAttachmentsSet, onOpenChange]); - const pickImage = async () => { - setShowAttachmentSheet(false); + const pickImage = useCallback(async () => { + onOpenChange(false); // See the comment above about the picker not mounting in time. await new Promise((resolve) => setTimeout(resolve, 300)); const result = await ImagePicker.launchImageLibraryAsync({ @@ -49,9 +53,9 @@ export default function AttachmentSheet({ }); if (!result.canceled) { - setImage(result.assets); + onAttachmentsSet(result.assets); } - }; + }, [onAttachmentsSet, onOpenChange]); useEffect(() => { if ( @@ -72,10 +76,10 @@ export default function AttachmentSheet({ requestCameraPermission, ]); - const actionGroups: ActionGroup[] = [ - { - accent: 'neutral', - actions: [ + const actionGroups: ActionGroup[] = useMemo( + () => [ + createActionGroup( + 'neutral', { title: 'Photo Library', description: 'Choose a photo from your library', @@ -86,9 +90,15 @@ export default function AttachmentSheet({ description: 'Use your camera to take a photo', action: takePicture, }, - ], - }, - ]; + showClearOption && { + title: 'Clear', + description: 'Remove attached media', + action: onClearAttachments, + } + ), + ], + [onClearAttachments, pickImage, showClearOption, takePicture] + ); const title = 'Attach a file'; const subtitle = 'Choose a file to attach'; @@ -96,7 +106,7 @@ export default function AttachmentSheet({ return ( setShowAttachmentSheet(open)} + onOpenChange={(open: boolean) => onOpenChange(open)} > diff --git a/packages/ui/src/components/BigInput.native.tsx b/packages/ui/src/components/BigInput.native.tsx index 8fcf8b12ca..6c4507172d 100644 --- a/packages/ui/src/components/BigInput.native.tsx +++ b/packages/ui/src/components/BigInput.native.tsx @@ -176,9 +176,9 @@ export function BigInput({ )} {channelType === 'notebook' && showAttachmentSheet && ( )} diff --git a/packages/ui/src/components/BigInput.tsx b/packages/ui/src/components/BigInput.tsx index ccdda32ffa..21b58c024e 100644 --- a/packages/ui/src/components/BigInput.tsx +++ b/packages/ui/src/components/BigInput.tsx @@ -183,9 +183,9 @@ export function BigInput({ ) */} {channelType === 'notebook' && showAttachmentSheet && ( )} diff --git a/packages/ui/src/components/BioDisplay.tsx b/packages/ui/src/components/BioDisplay.tsx deleted file mode 100644 index b51f5ea2a4..0000000000 --- a/packages/ui/src/components/BioDisplay.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { SizableText } from 'tamagui'; - -import { WidgetPane } from './WidgetPane'; - -export function BioDisplay({ bio }: { bio: string }) { - return ( - - About - {bio.length ? bio : 'An enigma'} - - ); -} diff --git a/packages/ui/src/components/EditProfileScreenView.tsx b/packages/ui/src/components/EditProfileScreenView.tsx index a30ddc4c52..b3a0d2d9dd 100644 --- a/packages/ui/src/components/EditProfileScreenView.tsx +++ b/packages/ui/src/components/EditProfileScreenView.tsx @@ -1,6 +1,5 @@ import * as api from '@tloncorp/shared/dist/api'; import * as db from '@tloncorp/shared/dist/db'; -import { ImagePickerAsset } from 'expo-image-picker'; import { useCallback, useState } from 'react'; import { useForm } from 'react-hook-form'; import { Keyboard } from 'react-native'; @@ -8,13 +7,16 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { ScrollView, View, YStack } from 'tamagui'; import { useContact, useCurrentUserId } from '../contexts'; -import { AttachmentProvider } from '../contexts/attachment'; import { EditablePofileImages } from './EditableProfileImages'; import { FavoriteGroupsDisplay } from './FavoriteGroupsDisplay'; -import { FormTextInput } from './FormInput'; -import { GenericHeader } from './GenericHeader'; +import { + ControlledField, + ControlledTextField, + ControlledTextareaField, +} from './Form'; import KeyboardAvoidingView from './KeyboardAvoidingView'; -import { SaveButton } from './MetaEditorScreenView'; +import { ScreenHeader } from './ScreenHeader'; +import { Text } from './TextV2'; interface Props { onGoBack: () => void; @@ -22,8 +24,6 @@ interface Props { profile: api.ProfileUpdate | null; pinnedGroups?: db.Group[] | null; }) => void; - uploadAsset: (asset: ImagePickerAsset) => Promise; - canUpload: boolean; } export function EditProfileScreenView(props: Props) { @@ -47,6 +47,7 @@ export function EditProfileScreenView(props: Props) { bio: userContact?.bio ?? '', avatarImage: userContact?.avatarImage ?? '', coverImage: userContact?.coverImage ?? '', + pinnedGroups: userContact?.pinnedGroups?.map((pin) => pin.group) ?? [], }, }); @@ -61,7 +62,6 @@ export function EditProfileScreenView(props: Props) { !initialPinnedIds.every((id) => newPinnedIds.includes(id)); if (isDirty) { - console.log(`form is dirty, handling submit?`); return handleSubmit((formData) => { props.onSaveProfile({ profile: formData, @@ -78,25 +78,48 @@ export function EditProfileScreenView(props: Props) { } }, [handleSubmit, isDirty, pinnedGroups, props, userContact?.pinnedGroups]); + const handlePressDone = () => { + props.onGoBack(); + onSavePressed(); + }; + + const handlePressCancel = () => { + props.onGoBack(); + }; + return ( - - - } - /> - - - + + + + + Cancel + + + Edit Profile + + + Done + + + + + + + + + + + - - Nickname - - - - - Bio - - + - - - - - - - - + ( + + )} + /> + + + + + ); } diff --git a/packages/ui/src/components/EditableProfileImages.tsx b/packages/ui/src/components/EditableProfileImages.tsx index 3cc7c06008..48ede4e14f 100644 --- a/packages/ui/src/components/EditableProfileImages.tsx +++ b/packages/ui/src/components/EditableProfileImages.tsx @@ -114,6 +114,16 @@ export function EditablePofileImages({ [attachingTo, attachAssets] ); + const handleAttachmentCleared = useCallback(() => { + if (attachingTo === 'cover') { + setLocalCoverUrl(''); + onSetCoverUrl(''); + } else if (attachingTo === 'icon') { + setLocalIconUrl(''); + onSetIconUrl(''); + } + }, [attachingTo, onSetCoverUrl, onSetIconUrl]); + return ( @@ -124,18 +134,26 @@ export function EditablePofileImages({ onPress={handleCoverPress} disabled={!canUpload} > - + {localCoverUrl ? ( + + ) : null} + {coverIsUploading && ( - + {/* Profile Icon */} @@ -198,9 +216,14 @@ export function EditablePofileImages({ ); diff --git a/packages/ui/src/components/FavoriteGroupsDisplay.tsx b/packages/ui/src/components/FavoriteGroupsDisplay.tsx index 38395c275a..b368806642 100644 --- a/packages/ui/src/components/FavoriteGroupsDisplay.tsx +++ b/packages/ui/src/components/FavoriteGroupsDisplay.tsx @@ -83,17 +83,13 @@ export function FavoriteGroupsDisplay(props: { return ( - - Favorite Groups - {compositeGroups.length === 0 ? ( <> ) : ( @@ -147,11 +143,6 @@ export function FavoriteGroupsDisplay(props: { : '$secondaryBackground', }} > - Add a group diff --git a/packages/ui/src/components/Form/Form.tsx b/packages/ui/src/components/Form/Form.tsx index 6d42cfd59d..a58674efba 100644 --- a/packages/ui/src/components/Form/Form.tsx +++ b/packages/ui/src/components/Form/Form.tsx @@ -51,7 +51,7 @@ export const FieldLabel = React.memo( color: '$tertiaryText', size: '$label/m', paddingHorizontal: '$xl', - paddingVertical: '$l', + paddingBottom: '$l', }) ); diff --git a/packages/ui/src/components/Form/controlledFields.tsx b/packages/ui/src/components/Form/controlledFields.tsx index 7aed992dde..cb98f2691e 100644 --- a/packages/ui/src/components/Form/controlledFields.tsx +++ b/packages/ui/src/components/Form/controlledFields.tsx @@ -78,6 +78,34 @@ export const ControlledTextField = < return ; }; +export const ControlledTextareaField = < + TFieldValues extends FieldValues, + TName extends Path, +>( + props: { inputProps?: ComponentProps } & Omit< + ControlledFieldProps, + 'renderInput' + > +) => { + const renderInput = useCallback( + ({ + field: { onChange, ...field }, + }: UseControllerReturn) => { + return ( + + ); + }, + [props.inputProps] + ); + return ; +}; + export const ControlledRadioField = < TFieldValues extends FieldValues, TName extends Path, diff --git a/packages/ui/src/components/GroupSelectorSheet.tsx b/packages/ui/src/components/GroupSelectorSheet.tsx index e62646fdc2..04b2d4ec16 100644 --- a/packages/ui/src/components/GroupSelectorSheet.tsx +++ b/packages/ui/src/components/GroupSelectorSheet.tsx @@ -5,6 +5,7 @@ import { NativeSyntheticEvent, SectionListRenderItemInfo, } from 'react-native'; +import { Modal } from 'react-native'; import { Stack, View, YStack } from 'tamagui'; import { AlphaSegmentedGroups } from '../hooks/groupsSorters'; @@ -137,29 +138,35 @@ export function GroupSelectorSheet(props: SheetProps) { }, [props.open]); return ( - props.onOpenChange(false)} > - - - - - {props.TopContent} - - - - + + + + + + {props.TopContent} + + + + + ); } diff --git a/packages/ui/src/components/MessageInput/AttachmentButton.native.tsx b/packages/ui/src/components/MessageInput/AttachmentButton.native.tsx index 6d6f931361..968cc073b3 100644 --- a/packages/ui/src/components/MessageInput/AttachmentButton.native.tsx +++ b/packages/ui/src/components/MessageInput/AttachmentButton.native.tsx @@ -29,9 +29,9 @@ export default function AttachmentButton({ ); diff --git a/packages/ui/src/components/MessageInput/AttachmentButton.tsx b/packages/ui/src/components/MessageInput/AttachmentButton.tsx index eefda972fa..56a555b862 100644 --- a/packages/ui/src/components/MessageInput/AttachmentButton.tsx +++ b/packages/ui/src/components/MessageInput/AttachmentButton.tsx @@ -30,9 +30,9 @@ export default function AttachmentButton({ ); diff --git a/packages/ui/src/components/NavBarView.tsx b/packages/ui/src/components/NavBarView.tsx index 08bfa2aef4..8c24be9958 100644 --- a/packages/ui/src/components/NavBarView.tsx +++ b/packages/ui/src/components/NavBarView.tsx @@ -5,13 +5,13 @@ import { AvatarNavIcon, NavBar, NavIcon } from './NavBar'; export const NavBarView = ({ navigateToHome, navigateToNotifications, - navigateToProfile, + navigateToProfileSettings, currentRoute, currentUserId, }: { navigateToHome: () => void; navigateToNotifications: () => void; - navigateToProfile: () => void; + navigateToProfileSettings: () => void; currentRoute: string; currentUserId: string; }) => { @@ -29,19 +29,19 @@ export const NavBarView = ({ // hasUnreads={(unreadCount?.channels ?? 0) > 0} // intentionally leave undotted for now hasUnreads={false} - onPress={() => navigateToHome()} + onPress={navigateToHome} /> navigateToNotifications()} + onPress={navigateToNotifications} /> navigateToProfile()} + onPress={navigateToProfileSettings} /> ); diff --git a/packages/ui/src/components/ProfileScreenView.tsx b/packages/ui/src/components/ProfileScreenView.tsx index 5351dc8f2e..3e2d5fadd5 100644 --- a/packages/ui/src/components/ProfileScreenView.tsx +++ b/packages/ui/src/components/ProfileScreenView.tsx @@ -1,39 +1,33 @@ -import * as db from '@tloncorp/shared/dist/db'; import { useFeatureFlag } from 'posthog-react-native'; -import { Alert, Dimensions, Share, TouchableOpacity } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { - ScrollView, - SizableText, - Stack, - View, - YStack, - getTokens, -} from 'tamagui'; +import { ReactElement } from 'react'; +import { Alert, Share } from 'react-native'; +import { ScrollView, View, YStack } from 'tamagui'; -import { useContact } from '../contexts'; +import { ContactAvatar } from './Avatar'; import { IconType } from './Icon'; import { ListItem } from './ListItem'; -import ProfileCover from './ProfileCover'; -import ProfileRow from './ProfileRow'; +import { ScreenHeader } from './ScreenHeader'; +import { TlonLogo } from './TlonLogo'; interface Props { currentUserId: string; - onAppSettingsPressed?: () => void; + hasHostedAuth: boolean; onEditProfilePressed?: () => void; + onAppInfoPressed?: () => void; + onNotificationSettingsPressed: () => void; + onBlockedUsersPressed: () => void; + onManageAccountPressed: () => void; onViewProfile?: () => void; - onLogoutPressed: () => void; + onLogoutPressed?: () => void; onSendBugReportPressed?: () => void; dmLink?: string; } export function ProfileScreenView(props: Props) { - const { top } = useSafeAreaInsets(); - const contact = useContact(props.currentUserId); const showDmLure = useFeatureFlag('share-dm-lure'); // TODO: Add logout back in when we figure out TLON-2098. - const onLogoutPress = () => { + const handleLogoutPressed = () => { Alert.alert('Log out', 'Are you sure you want to log out?', [ { text: 'Cancel', @@ -65,119 +59,116 @@ export function ProfileScreenView(props: Props) { }; return ( - - - - - {contact ? ( - - ) : ( - - - - )} - - - - - - Edit - - - - - - + <> + + Settings + + + + } + title="Edit profile" + subtitle={props.currentUserId} + onPress={props.onEditProfilePressed} + rightIcon={'ChevronRight'} + /> {showDmLure && props.dmLink !== '' && ( { - onShare(); - }} + leftIcon="Send" + rightIcon={'ChevronRight'} + onPress={onShare} + /> + )} + + + {props.hasHostedAuth && ( + + + + } + onPress={props.onManageAccountPressed} /> )} - - - - ); -} - -export function ProfileDisplayWidget({ - contact, - contactId, -}: { - contact: db.Contact; - contactId: string; -}) { - const coverSize = - Dimensions.get('window').width - getTokens().space.$xl.val * 2; - if (contact.coverImage) { - return ( - - - - - ); - } - - return ; + + + ); } function ProfileAction({ - icon, + leftIcon, + rightIcon, title, - hideCaret, + subtitle, onPress, }: { - icon: IconType; + leftIcon: IconType | ReactElement; + rightIcon?: IconType | ReactElement; title: string; - hideCaret?: boolean; onPress?: () => void; - tint?: boolean; + subtitle?: string; }) { return ( - + {typeof leftIcon === 'string' ? ( + + ) : ( + leftIcon + )} {title} + {subtitle && {subtitle}} - {!hideCaret && ( - - )} + {rightIcon ? ( + typeof rightIcon === 'string' ? ( + + ) : ( + rightIcon + ) + ) : null} ); } diff --git a/packages/ui/src/components/ScreenHeader.tsx b/packages/ui/src/components/ScreenHeader.tsx index 50cdb864d3..9e7215f920 100644 --- a/packages/ui/src/components/ScreenHeader.tsx +++ b/packages/ui/src/components/ScreenHeader.tsx @@ -21,7 +21,7 @@ export const ScreenHeaderComponent = ({ const { top } = useSafeAreaInsets(); return ( - + void }) => { return ( - + ); @@ -63,17 +63,16 @@ const HeaderBackButton = ({ onPress }: { onPress?: () => void }) => { const HeaderTitle = styled(Text, { size: '$label/2xl', textAlign: 'left', - fontWeight: '500', - flex: 1, + width: '100%', }); const HeaderControls = styled(XStack, { position: 'absolute', - top: '$m', + top: 0, bottom: 0, - height: '100%', gap: '$m', alignItems: 'center', + zIndex: 1, variants: { side: { left: { diff --git a/packages/ui/src/components/TlonLogo.tsx b/packages/ui/src/components/TlonLogo.tsx new file mode 100644 index 0000000000..7f8432df74 --- /dev/null +++ b/packages/ui/src/components/TlonLogo.tsx @@ -0,0 +1,17 @@ +import { styled } from 'tamagui'; + +import TlonSvg from '../assets/Tlon.svg'; + +export const TlonLogo = styled( + TlonSvg, + { + color: '$primaryText', + }, + { + accept: { + color: 'color', + width: 'size', + height: 'size', + }, + } +); diff --git a/packages/ui/src/components/UserProfileScreenView.tsx b/packages/ui/src/components/UserProfileScreenView.tsx index e4af02d5ca..27031379c2 100644 --- a/packages/ui/src/components/UserProfileScreenView.tsx +++ b/packages/ui/src/components/UserProfileScreenView.tsx @@ -1,71 +1,220 @@ +import * as api from '@tloncorp/shared/dist/api'; import * as db from '@tloncorp/shared/dist/db'; import * as store from '@tloncorp/shared/dist/store'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { ScrollView, SizableText, View, XStack, YStack } from 'tamagui'; +import { + ScrollView, + View, + XStack, + YStack, + styled, + useWindowDimensions, +} from 'tamagui'; import { useContact, useCurrentUserId, useNavigation } from '../contexts'; import { useCopy } from '../hooks/useCopy'; import { triggerHaptic } from '../utils'; -import { ContactAvatar } from './Avatar'; -import { BioDisplay } from './BioDisplay'; +import { ContactAvatar, GroupAvatar } from './Avatar'; import { Button } from './Button'; -import ContactName from './ContactName'; -import { FavoriteGroupsDisplay } from './FavoriteGroupsDisplay'; -import { GenericHeader } from './GenericHeader'; +import { ContactName } from './ContactNameV2'; import { Icon } from './Icon'; +import { ScreenHeader } from './ScreenHeader'; +import { Text } from './TextV2'; +import { WidgetPane } from './WidgetPane'; interface Props { userId: string; onBack: () => void; + connectionStatus: api.ConnectionStatus | null; } export function UserProfileScreenView(props: Props) { const insets = useSafeAreaInsets(); const currentUserId = useCurrentUserId(); const userContact = useContact(props.userId); - const hasBio = !!userContact?.bio?.length; - const favoriteGroups = useMemo(() => { - return userContact?.pinnedGroups?.map((g) => g.group) ?? []; + const pinnedGroups = useMemo(() => { + return ( + userContact?.pinnedGroups?.flatMap((g) => (g.group ? [g.group] : [])) ?? + [] + ); }, [userContact?.pinnedGroups]); + const windowDimensions = useWindowDimensions(); + + const nodeStatus = !props.connectionStatus?.complete + ? 'pending' + : props.connectionStatus.status === 'yes' + ? 'online' + : 'offline'; + + const sponsorStatus = !props.connectionStatus?.complete + ? 'pending' + : props.connectionStatus.status === 'yes' + ? 'online' + : ['no-their-galaxy', 'no-sponsor-miss', 'no-sponsor-hit'].includes( + props.connectionStatus.status + ) + ? 'offline' + : 'online'; + return ( - - - - - - - - - {currentUserId !== props.userId ? ( - - - - ) : null} - - - {hasBio ? : null} - {favoriteGroups.length ? ( - - ) : null} - - {!hasBio && !favoriteGroups.length ? ( - - - Nothing to see here... - - - ) : null} - + + } + > + Profile + + + + + {userContact?.status && } + + {currentUserId !== props.userId ? ( + + ) : null} + + + + + + {pinnedGroups.map((group, i) => { + return ( + + + + + {group.title} + + + {i === 0 && ( + + {group.description} + + )} + + + ); + })} ); } +function StatusBlock({ + status, + label, +}: { + status: 'online' | 'offline' | 'pending'; + label: string; +}) { + const windowDimensions = useWindowDimensions(); + + return ( + + + + {label} + + {statusText(status)} + + + + ); +} + +function statusText(status: 'online' | 'offline' | 'pending') { + return status === 'online' + ? 'Online' + : status === 'offline' + ? 'Offline' + : 'Pending'; +} + +function StatusIndicator({ + label, + status, +}: { + label: string; + status: 'online' | 'offline' | 'pending'; + children?: React.ReactNode; + onPress?: () => void; +}) { + return ( + + {status === 'pending' ? ( + + ? + + ) : ( + + )} + + ); +} + +const PaddedBlock = styled(YStack, { + borderRadius: '$2xl', + padding: '$2xl', + gap: '$l', + justifyContent: 'center', + backgroundColor: '$background', +}); + +export function BioDisplay({ bio }: { bio: string }) { + return ( + + About + + {bio.length ? bio : 'An enigma'} + + + ); +} + function UserInfoRow(props: { userId: string; hasNickname: boolean }) { const { didCopy, doCopy } = useCopy(props.userId); @@ -75,17 +224,30 @@ function UserInfoRow(props: { userId: string; hasNickname: boolean }) { }, [doCopy]); return ( - - - + + + {props.hasNickname ? ( <> - + {didCopy ? ( ) : ( - + )} @@ -124,7 +286,7 @@ function ProfileButtons(props: { userId: string; contact: db.Contact | null }) { }, [props.contact]); return ( - + void }) { return ( ); } diff --git a/packages/ui/src/components/WidgetPane.ts b/packages/ui/src/components/WidgetPane.ts index 42b36f89b6..28ad88fa7a 100644 --- a/packages/ui/src/components/WidgetPane.ts +++ b/packages/ui/src/components/WidgetPane.ts @@ -1,15 +1,17 @@ -import { SizableText, styled, withStaticProperties } from 'tamagui'; -import { YStack } from 'tamagui'; +import { YStack, styled, withStaticProperties } from 'tamagui'; + +import { Text } from './TextV2'; const WidgetPaneFrame = styled(YStack, { backgroundColor: '$background', - borderRadius: '$3xl', + borderRadius: '$l', padding: '$2xl', }); -const WidgetPaneTitle = styled(SizableText, { +const WidgetPaneTitle = styled(Text, { color: '$tertiaryText', - fontSize: '$l', + size: '$label/xl', + trimmed: false, marginBottom: '$m', }); From ab27cbd13e27b07f61f6c81fb4a60dd826ed354c Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Tue, 17 Sep 2024 23:44:34 -0400 Subject: [PATCH 064/157] move signup values into context, use context instead of prop passing between screens, run post signup in authenticated app bootstrapping --- apps/tlon-mobile/src/App.main.tsx | 35 +++---- .../src/components/AuthenticatedApp.tsx | 10 +- .../screens/Onboarding/ReserveShipScreen.tsx | 83 ++++++++--------- .../screens/Onboarding/SetNicknameScreen.tsx | 20 ++-- .../screens/Onboarding/SetTelemetryScreen.tsx | 9 +- apps/tlon-mobile/src/types.ts | 7 +- packages/app/contexts/signup.tsx | 91 +++++++++++++++++++ packages/app/hooks/usePostSignup.ts | 45 +++++++++ 8 files changed, 228 insertions(+), 72 deletions(-) create mode 100644 packages/app/contexts/signup.tsx create mode 100644 packages/app/hooks/usePostSignup.ts diff --git a/apps/tlon-mobile/src/App.main.tsx b/apps/tlon-mobile/src/App.main.tsx index 83849bde04..7719595235 100644 --- a/apps/tlon-mobile/src/App.main.tsx +++ b/apps/tlon-mobile/src/App.main.tsx @@ -13,6 +13,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; import ErrorBoundary from '@tloncorp/app/ErrorBoundary'; import { BranchProvider, useBranch } from '@tloncorp/app/contexts/branch'; import { ShipProvider, useShip } from '@tloncorp/app/contexts/ship'; +import { SignupProvider } from '@tloncorp/app/contexts/signup'; import { useIsDarkMode } from '@tloncorp/app/hooks/useIsDarkMode'; import { useScreenOptions } from '@tloncorp/app/hooks/useScreenOptions'; import { useMigrations } from '@tloncorp/app/lib/nativeDb'; @@ -208,23 +209,25 @@ export default function ConnectedApp(props: Props) { enable: process.env.NODE_ENV !== 'test', }} > - - - - - - - + + + + + + + + - {__DEV__ && ( - - )} - - - - + {__DEV__ && ( + + )} + + + + + diff --git a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx index 9aca190538..eba008adf3 100644 --- a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx +++ b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx @@ -1,9 +1,11 @@ import crashlytics from '@react-native-firebase/crashlytics'; import { useShip } from '@tloncorp/app/contexts/ship'; +import { useSignupContext } from '@tloncorp/app/contexts/signup'; import useAppForegrounded from '@tloncorp/app/hooks/useAppForegrounded'; import { useCurrentUserId } from '@tloncorp/app/hooks/useCurrentUser'; import { useNavigationLogging } from '@tloncorp/app/hooks/useNavigationLogger'; import { useNetworkLogger } from '@tloncorp/app/hooks/useNetworkLogger'; +import { usePostSignup } from '@tloncorp/app/hooks/usePostSignup'; import { configureClient } from '@tloncorp/app/lib/api'; import { PlatformState } from '@tloncorp/app/lib/platformHelpers'; import { AppDataProvider } from '@tloncorp/app/provider/AppDataProvider'; @@ -29,6 +31,8 @@ function AuthenticatedApp({ const { ship, shipUrl } = useShip(); const currentUserId = useCurrentUserId(); const session = store.useCurrentSession(); + const signupContext = useSignupContext(); + const handlePostSignup = usePostSignup(); useNotificationListener(notificationListenerProps); useDeepLinkListener(); useNavigationLogging(); @@ -50,8 +54,12 @@ function AuthenticatedApp({ store.setErrorTrackingUserId(currentUserId); } + if (signupContext.didSignup) { + handlePostSignup(); + } + sync.syncStart(); - }, [currentUserId, ship, shipUrl]); + }, [currentUserId, handlePostSignup, ship, shipUrl, signupContext.didSignup]); useAppForegrounded(() => { // only run these updates if we've initialized the session diff --git a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx index 1a2c33d433..61336ca7b6 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx @@ -30,7 +30,7 @@ type Props = NativeStackScreenProps; export const ReserveShipScreen = ({ navigation, route: { - params: { user, signUpExtras }, + params: { user }, }, }: Props) => { const [{ state, error }, setState] = useState<{ @@ -54,15 +54,16 @@ export const ReserveShipScreen = ({ const { status, shipId } = shipsWithStatus; // If user is in the sign up flow, send them to fill out some extra details - if ( - signUpExtras?.nickname === undefined && - signUpExtras?.telemetry === undefined - ) { - return navigation.navigate('SetNickname', { - user: await getHostingUser(user.id), - signUpExtras: { nickname: preSig(shipId) }, - }); - } + // TODO: ???? + // if ( + // signUpExtras?.nickname === undefined && + // signUpExtras?.telemetry === undefined + // ) { + // return navigation.navigate('SetNickname', { + // user: await getHostingUser(user.id), + // signUpExtras: { nickname: preSig(shipId) }, + // }); + // } // If it's not ready, show the booting message if (status !== 'Ready') { @@ -83,38 +84,38 @@ export const ReserveShipScreen = ({ const ship = getShipFromCookie(authCookie); configureApi(ship, shipUrl); - if (signUpExtras?.nickname) { - try { - await updateNickname(signUpExtras.nickname); - } catch (err) { - console.error('Error setting nickname:', err); - if (err instanceof Error) { - trackError(err); - } - } - } + // if (signUpExtras?.nickname) { + // try { + // await updateNickname(signUpExtras.nickname); + // } catch (err) { + // console.error('Error setting nickname:', err); + // if (err instanceof Error) { + // trackError(err); + // } + // } + // } - if (signUpExtras?.notificationToken) { - try { - await connectNotifyProvider(signUpExtras.notificationToken); - } catch (err) { - console.error('Error connecting push notifications provider:', err); - if (err instanceof Error) { - trackError(err); - } - } - } + // if (signUpExtras?.notificationToken) { + // try { + // await connectNotifyProvider(signUpExtras.notificationToken); + // } catch (err) { + // console.error('Error connecting push notifications provider:', err); + // if (err instanceof Error) { + // trackError(err); + // } + // } + // } - if (signUpExtras?.telemetry !== undefined) { - try { - await updateTelemetrySetting(signUpExtras.telemetry); - } catch (err) { - console.error('Error setting telemetry:', err); - if (err instanceof Error) { - trackError(err); - } - } - } + // if (signUpExtras?.telemetry !== undefined) { + // try { + // await updateTelemetrySetting(signUpExtras.telemetry); + // } catch (err) { + // console.error('Error setting telemetry:', err); + // if (err instanceof Error) { + // trackError(err); + // } + // } + // } // Set the ship info in the main context to navigate to chat view setShip({ @@ -123,7 +124,7 @@ export const ReserveShipScreen = ({ authCookie, }); }, - [user, signUpExtras, navigation, setShip] + [setShip] ); const reserveShip = useCallback( diff --git a/apps/tlon-mobile/src/screens/Onboarding/SetNicknameScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SetNicknameScreen.tsx index 45f3daa8c2..61d2109d97 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SetNicknameScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SetNicknameScreen.tsx @@ -1,4 +1,5 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { useSignupContext } from '@tloncorp/app/contexts/signup'; import { requestNotificationToken } from '@tloncorp/app/lib/notifications'; import { trackError } from '@tloncorp/app/utils/posthog'; import { @@ -26,7 +27,7 @@ type FormData = { export const SetNicknameScreen = ({ navigation, route: { - params: { user, signUpExtras }, + params: { user }, }, }: Props) => { const { @@ -36,19 +37,24 @@ export const SetNicknameScreen = ({ setValue, } = useForm({ defaultValues: { - nickname: signUpExtras.nickname, + nickname: '', notificationToken: undefined, }, }); + const signupContext = useSignupContext(); + const onSubmit = handleSubmit(({ nickname, notificationToken }) => { + if (nickname) { + signupContext.setNickname(nickname); + } + + if (notificationToken) { + signupContext.setNotificationToken(notificationToken); + } + navigation.navigate('SetTelemetry', { user, - signUpExtras: { - ...signUpExtras, - nickname, - notificationToken, - }, }); }); diff --git a/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx index 13f3fa7c06..178cfc3235 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx @@ -8,6 +8,7 @@ import { XStack, YStack, } from '@tloncorp/ui'; +import { useSignupContext } from 'packages/app/contexts/signup'; import { usePostHog } from 'posthog-react-native'; import { useCallback, useState } from 'react'; import { Switch } from 'react-native'; @@ -20,13 +21,16 @@ type Props = NativeStackScreenProps; export const SetTelemetryScreen = ({ navigation, route: { - params: { user, signUpExtras }, + params: { user }, }, }: Props) => { const [isEnabled, setIsEnabled] = useState(true); const postHog = usePostHog(); + const signupContext = useSignupContext(); const handleNext = useCallback(() => { + signupContext.setTelemetry(isEnabled); + if (!isEnabled) { postHog?.optOut(); branch.disableTracking(true); @@ -34,9 +38,8 @@ export const SetTelemetryScreen = ({ navigation.push('ReserveShip', { user, - signUpExtras: { ...signUpExtras, telemetry: isEnabled }, }); - }, [isEnabled, user, postHog, navigation, signUpExtras]); + }, [isEnabled, user, postHog, navigation, signupContext]); return ( diff --git a/apps/tlon-mobile/src/types.ts b/apps/tlon-mobile/src/types.ts index 4e2e676f1a..45e52cc4bc 100644 --- a/apps/tlon-mobile/src/types.ts +++ b/apps/tlon-mobile/src/types.ts @@ -99,10 +99,9 @@ export type OnboardingStackParamList = { JoinWaitList: { email?: string }; RequestPhoneVerify: { user: User }; CheckVerify: { user: User }; - ReserveShip: { user: User; signUpExtras?: SignUpExtras }; - SetNickname: { user: User; signUpExtras: SignUpExtras }; - SetNotifications: { user: User; signUpExtras: SignUpExtras }; - SetTelemetry: { user: User; signUpExtras: SignUpExtras }; + ReserveShip: { user: User }; + SetNickname: { user: User }; + SetTelemetry: { user: User }; TlonLogin: undefined; ShipLogin: undefined; ResetPassword: { email?: string }; diff --git a/packages/app/contexts/signup.tsx b/packages/app/contexts/signup.tsx new file mode 100644 index 0000000000..9b5e3c2427 --- /dev/null +++ b/packages/app/contexts/signup.tsx @@ -0,0 +1,91 @@ +import { createContext, useCallback, useContext, useState } from 'react'; + +interface SignupValues { + nickname?: string; + notificationToken?: string; + telemetry?: boolean; + didSignup?: boolean; +} + +interface SignupContext extends SignupValues { + setNickname: (nickname: string | undefined) => void; + setNotificationToken: (notificationToken: string | undefined) => void; + setTelemetry: (telemetry: boolean) => void; + setDidSignup: (didSignup: boolean) => void; + clear: () => void; +} + +const defaultContext = { + nickname: undefined, + setNickname: () => {}, + setNotificationToken: () => {}, + setTelemetry: () => {}, + setDidSignup: () => {}, + clear: () => {}, +}; + +const SignupContext = createContext(defaultContext); + +export const SignupProvider = ({ children }: { children: React.ReactNode }) => { + const [values, setValues] = useState({}); + + const setNickname = useCallback((nickname: string | undefined) => { + setValues((current) => ({ + ...current, + nickname, + })); + }, []); + + const setNotificationToken = useCallback( + (notificationToken: string | undefined) => { + setValues((current) => ({ + ...current, + notificationToken, + })); + }, + [] + ); + + const setTelemetry = useCallback((telemetry: boolean) => { + setValues((current) => ({ + ...current, + telemetry, + })); + }, []); + + const setDidSignup = useCallback((didSignup: boolean) => { + setValues((current) => ({ + ...current, + didSignup, + })); + }, []); + + const clear = useCallback(() => { + setValues({}); + }, []); + + return ( + + {children} + + ); +}; + +export function useSignupContext() { + const context = useContext(SignupContext); + + if (!context) { + throw new Error('useSignupContext must be used within a SignupProvider'); + } + + return context; +} diff --git a/packages/app/hooks/usePostSignup.ts b/packages/app/hooks/usePostSignup.ts new file mode 100644 index 0000000000..526f706dc4 --- /dev/null +++ b/packages/app/hooks/usePostSignup.ts @@ -0,0 +1,45 @@ +import { updateTelemetrySetting } from '@tloncorp/shared/dist/api'; +import * as store from '@tloncorp/shared/dist/store'; +import { createDevLogger } from 'packages/shared/dist'; +import { useCallback } from 'react'; + +import { useSignupContext } from '../contexts/signup'; +import { connectNotifyProvider } from '../lib/notificationsApi'; + +const logger = createDevLogger('postSignup', false); + +export function usePostSignup() { + const signupContext = useSignupContext(); + + const handlePostSignup = useCallback(async () => { + if (signupContext.nickname) { + try { + await store.updateCurrentUserProfile({ + nickname: signupContext.nickname, + }); + } catch (e) { + logger.error('Failed to set nickname', e); + } + } + + if (typeof signupContext.telemetry !== 'undefined') { + try { + await updateTelemetrySetting(signupContext.telemetry); + } catch (e) { + logger.error('Failed to set telemetry', e); + } + } + + if (signupContext.notificationToken) { + try { + await connectNotifyProvider(signupContext.notificationToken); + } catch (e) { + logger.error('Failed to connect notify provider', e); + } + } + + signupContext.clear(); + }, [signupContext]); + + return handlePostSignup; +} From 600420cb1ae4492950dccab04da64ce99be356a5 Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 18 Sep 2024 15:47:38 +0200 Subject: [PATCH 065/157] channel-utils: include +first-inline utility --- desk/app/expose.hoon | 26 ++------------------------ desk/lib/channel-utils.hoon | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index aee43eb2d6..b4f646117b 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -40,11 +40,12 @@ `con.for.u.for :: ::TODO if we render replies then we can "unroll" whole chat threads too (: + ::TODO just key off the kind-data, no? |^ ?+ p.nest ~ %chat ?> ?=(%chat -.kind-data.msg) =/ title=tape - (trip (rap 3 (turn (first-inline content.msg) flatten-inline:u))) + (trip (rap 3 (turn (first-inline:u content.msg) flatten-inline:u))) %- some %: build "chat" (heads title ~) @@ -1964,29 +1965,6 @@ $(n (mod n 10)) == -- - :: - ++ first-inline - |= content=story:d - ^- (list inline:d) - ?~ content ~ - ?: ?=(%inline -.i.content) - p.i.content - ?+ -.p.i.content $(content t.content) - %header q.p.i.content ::REVIEW questionable - :: - %listing - |- - ?- -.p.p.i.content - %list ::TODO or check listing first? - ?. =(~ r.p.p.i.content) - r.p.p.i.content - ?~ q.p.p.i.content ~ - =/ r $(p.p.i.content i.q.p.p.i.content) - ?. =(~ r) r - $(q.p.p.i.content t.q.p.p.i.content) - %item p.p.p.i.content - == - == -- :: ++ post-from-cite diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index c37859dc34..a95b9f5e78 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -430,7 +430,30 @@ %task (rap 3 (turn q.i flatten-inline)) %break '\0a' == -:: :: +:: +++ first-inline + |= content=story:c + ^- (list inline:c) + ?~ content ~ + ?: ?=(%inline -.i.content) + p.i.content + ?+ -.p.i.content $(content t.content) + %header q.p.i.content ::REVIEW questionable + :: + %listing + |- + ?- -.p.p.i.content + %list ::TODO or check listing first? + ?. =(~ r.p.p.i.content) + r.p.p.i.content + ?~ q.p.p.i.content ~ + =/ r $(p.p.i.content i.q.p.p.i.content) + ?. =(~ r) r + $(q.p.p.i.content t.q.p.p.i.content) + %item p.p.p.i.content + == + == +:: ++ en-manx ::NOTE more commonly, marl, but that's just (list manx) |% ++ content story From 338b7ba674a7a97ee8adbbcd3f7b24f76b890490 Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 18 Sep 2024 15:48:21 +0200 Subject: [PATCH 066/157] expose: generate public profile page widget (wip) Still a work in progress. No styling, no collection item rendering. --- desk/app/expose.hoon | 90 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index b4f646117b..88788f5f55 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -1986,6 +1986,79 @@ %+ weld base /posts/post/(scot %ud (rash i.t.wer dum:ag))/channel-post-2 == + :: + ++ update-widget + |= [[our=@p now=@da] open=(set cite:c)] + ^- (list card) + ?. .^(? %gu /(scot %p our)/profile/(scot %da now)/$) + ~ + ~> %bout.[0 'updating expose widget'] + =; widget=[%0 desc=@t %marl marl] + =/ =cage noun+!>([%command %update-widget %groups %expose-all widget]) + [%pass /profile/widget/all %agent [our %profile] %poke cage]~ + :^ %0 'Publicized content' %marl + ^- marl + :: + =/ cis=(list cite:c) + ::REVIEW maybe limit to the latest n? + %+ sort ~(tap in open) + :: newest first (assumes id nr in path is a timestamp) + :: + |= [a=cite:c b=cite:c] + ?. ?=([%chan * ?(%msg %note %curio) @ *] a) | + ?. ?=([%chan * ?(%msg %note %curio) @ *] b) & + ?~ aa=(rush i.t.wer.a dum:ag) | + ?~ bb=(rush i.t.wer.b dum:ag) & + (gth u.aa u.bb) + =/ style=@t + ''' + /* TODO + prefer scoping rules to just elements under #groups--expose-all, + otherwise style may affect other widgets on the profile + (alternatively do inline style attributes) + */ + #groups--expose-all { + border: 1px solid red; + } + #groups--expose-all a { + display: block; + margin: 1em; + } + #groups--expose-all .exposed { + border: 1px solid black; + } + ''' + :- ;style:"{(trip style)}" + %+ murn cis + |= ref=cite:c + ^- (unit manx) + =/ pon=(unit [=nest:g:c =post:d]) + (post-from-cite our now ref) + ?~ pon ~ + %- some + =/ link=tape + (spud (print:c ref)) + =, post.u.pon + =/ desc=tape + ::TODO also want to know if there's.. more content, so we can + :: conditionally render the ellipses? + (trip (rap 3 (turn (first-inline:u content) flatten-inline:u))) + ?- -.kind-data.post.u.pon + %chat + ;a.exposed.chat/"/expose{link}" + ;span:"{desc}" + == + :: + %diary + ;a.exposed.diary/"/expose{link}" + ::TODO image background? + ;h3:"{(trip title.kind-data)}" + ;span:"{desc}" + == + :: + %heap + ;a.exposed.heap/"/expose{link}":"TODO gallery item?" + == -- :: ++ hutils :: http request utils @@ -2051,7 +2124,8 @@ ++ on-load |= ole=vase ^- (quip card _this) - [~ this(state !<(state-0 ole))] + =. state !<(state-0 ole) + [(update-widget:e [our now]:bowl open) this] :: ++ on-poke |= [=mark =vase] @@ -2072,9 +2146,10 @@ ((render-post:e [our now]:bowl) u.msg) ?> ?=(^ pag) =. open (~(put in open) ref) - =/ url=@t (cat 3 '/expose' (spat path.act)) - :_ this :_ ~ - %+ store:hutils url + :_ this + :_ (update-widget:e [our now]:bowl open) + %+ store:hutils + (cat 3 '/expose' (spat path.act)) `[| %payload (paint:hutils %page u.pag)] :: %hide @@ -2083,9 +2158,10 @@ ?. (~(has in open) ref) [~ this] =. open (~(del in open) ref) - =/ url=@t (cat 3 '/expose' (spat path.act)) - :_ this :_ ~ - %+ store:hutils url + :_ this + :_ (update-widget:e [our now]:bowl open) + %+ store:hutils + (cat 3 '/expose' (spat path.act)) :^ ~ | %payload [[404 ~] `(as-octs:mimes:html 'not found')] == From 0eb42e19971c41cf0919e3eea6b92255df11fd17 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Wed, 18 Sep 2024 08:50:00 -0500 Subject: [PATCH 067/157] Update AddGroupSheet and InviteUserSheet to use ActionSheet, some other style tweaks --- .../src/fixtures/Button.fixture.tsx | 14 +++ .../components/AddChats/CreateGroupWidget.tsx | 33 +++---- packages/ui/src/components/AddGroupSheet.tsx | 86 +++++++++---------- .../ui/src/components/InviteUsersSheet.tsx | 37 ++------ 4 files changed, 76 insertions(+), 94 deletions(-) diff --git a/apps/tlon-mobile/src/fixtures/Button.fixture.tsx b/apps/tlon-mobile/src/fixtures/Button.fixture.tsx index 27ab27549d..438fe159c5 100644 --- a/apps/tlon-mobile/src/fixtures/Button.fixture.tsx +++ b/apps/tlon-mobile/src/fixtures/Button.fixture.tsx @@ -17,4 +17,18 @@ export default { ), + hero: () => ( + + + + ), + heroDisabled: () => ( + + + + ), }; diff --git a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx index 5596e265af..c48f7a83b1 100644 --- a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx +++ b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx @@ -2,12 +2,11 @@ import { createShortCodeFromTitle } from '@tloncorp/shared/dist'; import * as db from '@tloncorp/shared/dist/db'; import * as store from '@tloncorp/shared/dist/store'; import { useCallback, useState } from 'react'; -import { TextInput } from 'react-native'; -import { getTokenValue } from 'tamagui'; -import { SizableText, XStack, YStack, useTheme } from 'tamagui'; +import { SizableText, XStack, YStack } from 'tamagui'; import { triggerHaptic } from '../../utils'; import { PrimaryButton } from '../Buttons'; +import { Field, TextInput } from '../Form'; import { Icon } from '../Icon'; export function CreateGroupWidget(props: { @@ -23,7 +22,6 @@ export function CreateGroupWidget(props: { }) { const [loading, setLoading] = useState(false); const [groupName, setGroupName] = useState(''); - const theme = useTheme(); const onCreateGroup = useCallback(async () => { const shortCode = createShortCodeFromTitle(groupName); @@ -60,23 +58,16 @@ export function CreateGroupWidget(props: { - Group Name (Required) - {/* TODO: make tamagui input cohere */} - + + + void; @@ -174,36 +173,31 @@ export function AddGroupSheet({ ); return ( - - - - - - - - - {Object.values(screens)} - - - - - - + + + + + {Object.values(screens)} + + + + + ); } @@ -216,13 +210,13 @@ function ScreenWrapper({ }) { const insets = useSafeAreaInsets(); return ( - {children} - + ); } @@ -245,7 +239,7 @@ function RootScreen({ return ( - + - - New Message - - + + New Message + + + + ); @@ -335,9 +331,9 @@ function InviteUsersScreen({ - + Select Members - + { setInvitees([]); diff --git a/packages/ui/src/components/InviteUsersSheet.tsx b/packages/ui/src/components/InviteUsersSheet.tsx index e1ff207e1e..c59b6e3341 100644 --- a/packages/ui/src/components/InviteUsersSheet.tsx +++ b/packages/ui/src/components/InviteUsersSheet.tsx @@ -1,10 +1,9 @@ import * as db from '@tloncorp/shared/dist/db'; import React, { useRef } from 'react'; -import { Modal } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { ActionSheet } from './ActionSheet'; import { InviteUsersWidget } from './InviteUsersWidget'; -import { Sheet } from './Sheet'; const InviteUsersSheetComponent = ({ open, @@ -27,33 +26,15 @@ const InviteUsersSheetComponent = ({ if (!hasOpened.current || !group) return null; return ( - onOpenChange(false)} - transparent - animationType="none" - > - + - - - - - - - + + + ); }; From 1212ebbbc618052cc197a27aefbab14b5ce9b14c Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 18 Sep 2024 11:09:56 -0400 Subject: [PATCH 068/157] leave nickname redirect in, set didSignup after creating hosting account --- .../screens/Onboarding/ReserveShipScreen.tsx | 20 +++++++++---------- .../Onboarding/SignUpPasswordScreen.tsx | 3 +++ packages/app/hooks/usePostSignup.ts | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx index 61336ca7b6..5e9a0e81a6 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx @@ -20,6 +20,7 @@ import { } from '@tloncorp/shared/dist/api'; import { Spinner, Text, View, YStack } from '@tloncorp/ui'; import { preSig } from '@urbit/aura'; +import { useSignupContext } from 'packages/app/contexts/signup'; import { useCallback, useEffect, useState } from 'react'; import { Alert } from 'react-native'; @@ -39,6 +40,7 @@ export const ReserveShipScreen = ({ }>({ state: 'loading', }); + const signupContext = useSignupContext(); const { setShip } = useShip(); const startShip = useCallback( @@ -54,16 +56,14 @@ export const ReserveShipScreen = ({ const { status, shipId } = shipsWithStatus; // If user is in the sign up flow, send them to fill out some extra details - // TODO: ???? - // if ( - // signUpExtras?.nickname === undefined && - // signUpExtras?.telemetry === undefined - // ) { - // return navigation.navigate('SetNickname', { - // user: await getHostingUser(user.id), - // signUpExtras: { nickname: preSig(shipId) }, - // }); - // } + if ( + signupContext.nickname === undefined && + signupContext.telemetry === undefined + ) { + return navigation.navigate('SetNickname', { + user: await getHostingUser(user.id), + }); + } // If it's not ready, show the booting message if (status !== 'Ready') { diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx index 5e49d600f8..9ef99485bd 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx @@ -31,6 +31,7 @@ import { View, YStack, } from '@tloncorp/ui'; +import { useSignupContext } from 'packages/app/contexts/signup'; import { useEffect, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; @@ -51,6 +52,7 @@ export const SignUpPasswordScreen = ({ }, }: Props) => { const [isSubmitting, setIsSubmitting] = useState(false); + const signupContext = useSignupContext(); const signupParams = useSignupParams(); const lureMeta = useLureMetadata(); const { @@ -116,6 +118,7 @@ export const SignUpPasswordScreen = ({ lure: signupParams.lureId, priorityToken: signupParams.priorityToken, }); + signupContext.setDidSignup(true); } catch (err) { console.error('Error signing up user:', err); if (err instanceof Error) { diff --git a/packages/app/hooks/usePostSignup.ts b/packages/app/hooks/usePostSignup.ts index 526f706dc4..aa104613aa 100644 --- a/packages/app/hooks/usePostSignup.ts +++ b/packages/app/hooks/usePostSignup.ts @@ -6,7 +6,7 @@ import { useCallback } from 'react'; import { useSignupContext } from '../contexts/signup'; import { connectNotifyProvider } from '../lib/notificationsApi'; -const logger = createDevLogger('postSignup', false); +const logger = createDevLogger('postSignup', true); export function usePostSignup() { const signupContext = useSignupContext(); From 577a4fe4973cad9ed0816d71060785e812f51e11 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 18 Sep 2024 11:13:53 -0400 Subject: [PATCH 069/157] fix imports --- apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx | 2 +- apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx | 2 +- .../tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx | 2 +- packages/app/hooks/usePostSignup.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx index 5e9a0e81a6..3b88b976e8 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx @@ -1,6 +1,7 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { useBranch } from '@tloncorp/app/contexts/branch'; import { useShip } from '@tloncorp/app/contexts/ship'; +import { useSignupContext } from '@tloncorp/app/contexts/signup'; import { allocateReservedShip, getHostingUser, @@ -20,7 +21,6 @@ import { } from '@tloncorp/shared/dist/api'; import { Spinner, Text, View, YStack } from '@tloncorp/ui'; import { preSig } from '@urbit/aura'; -import { useSignupContext } from 'packages/app/contexts/signup'; import { useCallback, useEffect, useState } from 'react'; import { Alert } from 'react-native'; diff --git a/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx index 178cfc3235..2761bc9096 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SetTelemetryScreen.tsx @@ -1,4 +1,5 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { useSignupContext } from '@tloncorp/app/contexts/signup'; import { Button, GenericHeader, @@ -8,7 +9,6 @@ import { XStack, YStack, } from '@tloncorp/ui'; -import { useSignupContext } from 'packages/app/contexts/signup'; import { usePostHog } from 'posthog-react-native'; import { useCallback, useState } from 'react'; import { Switch } from 'react-native'; diff --git a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx index 9ef99485bd..25fc4d6558 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/SignUpPasswordScreen.tsx @@ -10,6 +10,7 @@ import { useLureMetadata, useSignupParams, } from '@tloncorp/app/contexts/branch'; +import { useSignupContext } from '@tloncorp/app/contexts/signup'; import { logInHostingUser, signUpHostingUser, @@ -31,7 +32,6 @@ import { View, YStack, } from '@tloncorp/ui'; -import { useSignupContext } from 'packages/app/contexts/signup'; import { useEffect, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; diff --git a/packages/app/hooks/usePostSignup.ts b/packages/app/hooks/usePostSignup.ts index aa104613aa..87778ea6e1 100644 --- a/packages/app/hooks/usePostSignup.ts +++ b/packages/app/hooks/usePostSignup.ts @@ -1,6 +1,6 @@ +import { createDevLogger } from '@tloncorp/shared/dist'; import { updateTelemetrySetting } from '@tloncorp/shared/dist/api'; import * as store from '@tloncorp/shared/dist/store'; -import { createDevLogger } from 'packages/shared/dist'; import { useCallback } from 'react'; import { useSignupContext } from '../contexts/signup'; From 8b4a076acc9105726514e0950739ac01954f47c6 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 18 Sep 2024 11:19:21 -0400 Subject: [PATCH 070/157] remove commented out and dead code --- .../screens/Onboarding/ReserveShipScreen.tsx | 46 +++---------------- packages/shared/src/api/userApi.ts | 9 ---- 2 files changed, 7 insertions(+), 48 deletions(-) diff --git a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx index 3b88b976e8..a62d21435e 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/ReserveShipScreen.tsx @@ -1,5 +1,4 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { useBranch } from '@tloncorp/app/contexts/branch'; import { useShip } from '@tloncorp/app/contexts/ship'; import { useSignupContext } from '@tloncorp/app/contexts/signup'; import { @@ -10,17 +9,13 @@ import { getShipsWithStatus, reserveShip as reserveShipApi, } from '@tloncorp/app/lib/hostingApi'; -import { connectNotifyProvider } from '@tloncorp/app/lib/notificationsApi'; import { trackError, trackOnboardingAction } from '@tloncorp/app/utils/posthog'; import { getShipFromCookie, getShipUrl } from '@tloncorp/app/utils/ship'; import { configureApi, getLandscapeAuthCookie, - updateNickname, - updateTelemetrySetting, } from '@tloncorp/shared/dist/api'; import { Spinner, Text, View, YStack } from '@tloncorp/ui'; -import { preSig } from '@urbit/aura'; import { useCallback, useEffect, useState } from 'react'; import { Alert } from 'react-native'; @@ -84,39 +79,6 @@ export const ReserveShipScreen = ({ const ship = getShipFromCookie(authCookie); configureApi(ship, shipUrl); - // if (signUpExtras?.nickname) { - // try { - // await updateNickname(signUpExtras.nickname); - // } catch (err) { - // console.error('Error setting nickname:', err); - // if (err instanceof Error) { - // trackError(err); - // } - // } - // } - - // if (signUpExtras?.notificationToken) { - // try { - // await connectNotifyProvider(signUpExtras.notificationToken); - // } catch (err) { - // console.error('Error connecting push notifications provider:', err); - // if (err instanceof Error) { - // trackError(err); - // } - // } - // } - - // if (signUpExtras?.telemetry !== undefined) { - // try { - // await updateTelemetrySetting(signUpExtras.telemetry); - // } catch (err) { - // console.error('Error setting telemetry:', err); - // if (err instanceof Error) { - // trackError(err); - // } - // } - // } - // Set the ship info in the main context to navigate to chat view setShip({ ship, @@ -124,7 +86,13 @@ export const ReserveShipScreen = ({ authCookie, }); }, - [setShip] + [ + navigation, + setShip, + signupContext.nickname, + signupContext.telemetry, + user.id, + ] ); const reserveShip = useCallback( diff --git a/packages/shared/src/api/userApi.ts b/packages/shared/src/api/userApi.ts index 2c3869d0eb..ad9b6b2d9e 100644 --- a/packages/shared/src/api/userApi.ts +++ b/packages/shared/src/api/userApi.ts @@ -1,14 +1,5 @@ import { poke } from './urbit'; -export const updateNickname = async (nickname: string) => - poke({ - app: 'contacts', - mark: 'contact-action', - json: { - edit: [{ nickname }], - }, - }); - export const updateTelemetrySetting = async (isEnabled: boolean) => poke({ app: 'settings', From 35e86c188b9b711fae363401615694e505d9b79e Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Wed, 18 Sep 2024 11:03:14 -0500 Subject: [PATCH 071/157] rectify options sheets for groups/channels/dms/multi-dms --- .../ui/src/components/ChatOptionsSheet.tsx | 75 ++++++++++++++----- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/packages/ui/src/components/ChatOptionsSheet.tsx b/packages/ui/src/components/ChatOptionsSheet.tsx index 7fd37aa8a9..0f84733d00 100644 --- a/packages/ui/src/components/ChatOptionsSheet.tsx +++ b/packages/ui/src/components/ChatOptionsSheet.tsx @@ -250,16 +250,13 @@ export function GroupOptions({ title: 'Members', endIcon: 'ChevronRight', action: () => { - if (!group) { - return; - } onPressGroupMembers?.(group.id); sheetRef.current.setOpen(false); }, }; const metadataAction: Action = { - title: 'Edit metadata', + title: 'Edit group info', action: () => { sheetRef.current.setOpen(false); onPressGroupMeta?.(group.id); @@ -276,23 +273,22 @@ export function GroupOptions({ endIcon: 'ChevronRight', }; + if (currentUserIsAdmin) { + actionGroups.push({ + accent: 'neutral', + actions: [manageChannelsAction, managePrivacyAction, metadataAction], + }); + } + actionGroups.push({ accent: 'neutral', actions: - group && currentUserIsAdmin - ? [ - manageChannelsAction, - managePrivacyAction, - goToMembersAction, - inviteAction, - metadataAction, - ] - : group.privacy === 'public' || group.privacy === 'private' - ? [goToMembersAction, inviteAction] - : [goToMembersAction], + group.privacy === 'public' || group.privacy === 'private' + ? [goToMembersAction, inviteAction] + : [goToMembersAction], }); - if (group && !group.currentUserIsHost) { + if (!group.currentUserIsHost) { actionGroups.push({ accent: 'negative', actions: [ @@ -383,11 +379,23 @@ export function ChannelOptions({ id: channel?.groupId ?? undefined, }); const { data: currentVolumeLevel } = store.useChannelVolumeLevel(channel.id); + const currentUser = useCurrentUserId(); const sheet = useSheet(); const sheetRef = useRef(sheet); sheetRef.current = sheet; - const { onPressChannelMembers, onPressChannelMeta } = useChatOptions() ?? {}; + const { onPressChannelMembers, onPressChannelMeta, onPressManageChannels } = + useChatOptions() ?? {}; + + const currentUserIsAdmin = useMemo( + () => + group?.members?.some( + (m) => + m.contactId === currentUser && + m.roles?.some((r) => r.roleId === 'admin') + ) ?? false, + [currentUser, group?.members] + ); const { disableNicknames } = useCalm(); const title = useMemo(() => { @@ -515,7 +523,7 @@ export function ChannelOptions({ }, }, { - title: 'Edit metadata', + title: 'Edit group info', endIcon: 'ChevronRight', action: () => { if (!channel) { @@ -529,6 +537,26 @@ export function ChannelOptions({ } as ActionGroup, ] : []), + ...(currentUserIsAdmin + ? [ + { + accent: 'neutral', + actions: [ + { + title: 'Manage channels', + endIcon: 'ChevronRight', + action: () => { + if (!group) { + return; + } + onPressManageChannels?.(group.id); + sheetRef.current.setOpen(false); + }, + }, + ], + } as ActionGroup, + ] + : []), { accent: 'negative', actions: [ @@ -562,7 +590,16 @@ export function ChannelOptions({ ], }, ]; - }, [channel, onPressChannelMembers, onPressChannelMeta, setPane, title]); + }, [ + channel, + onPressChannelMembers, + onPressChannelMeta, + setPane, + title, + currentUserIsAdmin, + group, + onPressManageChannels, + ]); return ( Date: Wed, 18 Sep 2024 11:59:50 -0500 Subject: [PATCH 072/157] Make sure admins can see invite button for all group types, move 'edit group info' out to its own group for groupDms --- .../ui/src/components/ChatOptionsSheet.tsx | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/ui/src/components/ChatOptionsSheet.tsx b/packages/ui/src/components/ChatOptionsSheet.tsx index 0f84733d00..61cfe519e3 100644 --- a/packages/ui/src/components/ChatOptionsSheet.tsx +++ b/packages/ui/src/components/ChatOptionsSheet.tsx @@ -280,13 +280,20 @@ export function GroupOptions({ }); } - actionGroups.push({ - accent: 'neutral', - actions: - group.privacy === 'public' || group.privacy === 'private' - ? [goToMembersAction, inviteAction] - : [goToMembersAction], - }); + if (currentUserIsAdmin) { + actionGroups.push({ + accent: 'neutral', + actions: [goToMembersAction, inviteAction], + }); + } else { + actionGroups.push({ + accent: 'neutral', + actions: + group.privacy === 'public' || group.privacy === 'private' + ? [goToMembersAction, inviteAction] + : [goToMembersAction], + }); + } if (!group.currentUserIsHost) { actionGroups.push({ @@ -512,24 +519,33 @@ export function ChannelOptions({ accent: 'neutral', actions: [ { - title: 'Members', + title: 'Edit group info', endIcon: 'ChevronRight', action: () => { if (!channel) { return; } - onPressChannelMembers?.(channel.id); + onPressChannelMeta?.(channel.id); sheetRef.current.setOpen(false); }, }, + ], + } as ActionGroup, + ] + : []), + ...(channel.type === 'groupDm' + ? [ + { + accent: 'neutral', + actions: [ { - title: 'Edit group info', + title: 'Members', endIcon: 'ChevronRight', action: () => { if (!channel) { return; } - onPressChannelMeta?.(channel.id); + onPressChannelMembers?.(channel.id); sheetRef.current.setOpen(false); }, }, From 3bd82864aef677bab990567b78c68c6046c4919d Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Wed, 18 Sep 2024 12:20:06 -0500 Subject: [PATCH 073/157] Check for invitees before attempting to insert in CreateGroupWidget --- .../ui/src/components/AddChats/CreateGroupWidget.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx index c48f7a83b1..05e632af17 100644 --- a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx +++ b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx @@ -37,10 +37,13 @@ export function CreateGroupWidget(props: { shortCode, }); - await store.inviteGroupMembers({ - groupId: group.id, - contactIds: props.invitees, - }); + if (props.invitees.length > 0) { + await store.inviteGroupMembers({ + groupId: group.id, + contactIds: props.invitees, + }); + } + props.onCreatedGroup({ group, channel }); triggerHaptic('success'); } catch (e) { From c048ab12c114f16c773be8db6648f9b5a1f8b7ff Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Wed, 18 Sep 2024 12:44:14 -0500 Subject: [PATCH 074/157] Shove actions into ContactBook so we can render appropriately on the root screen, reset current screen on dismiss --- packages/ui/src/components/AddGroupSheet.tsx | 43 +++++++++++--------- packages/ui/src/components/ContactBook.tsx | 5 +++ 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/packages/ui/src/components/AddGroupSheet.tsx b/packages/ui/src/components/AddGroupSheet.tsx index 04843d36d3..7ec6e6ce9f 100644 --- a/packages/ui/src/components/AddGroupSheet.tsx +++ b/packages/ui/src/components/AddGroupSheet.tsx @@ -121,7 +121,10 @@ export function AddGroupSheet({ onOpenChange(false); // used for resetting components nested within screens after // reopening - setTimeout(() => setScreenKey((key) => key + 1), 300); + setTimeout(() => { + setCurrentScreen('Root'); + setScreenKey((key) => key + 1); + }, 300); }, [onOpenChange]); useEffect(() => { @@ -240,24 +243,6 @@ function RootScreen({ return ( - - goToScreen('InviteUsers'), - }} - /> - goToFindGroups(), - }} - /> - New Message @@ -268,6 +253,26 @@ function RootScreen({ onSelect={onSelect} onScrollChange={onScrollChange} key={screenKey} + quickActions={ + + goToScreen('InviteUsers'), + }} + /> + goToFindGroups(), + }} + /> + + } /> diff --git a/packages/ui/src/components/ContactBook.tsx b/packages/ui/src/components/ContactBook.tsx index 43dd4cc423..a2d90ebf00 100644 --- a/packages/ui/src/components/ContactBook.tsx +++ b/packages/ui/src/components/ContactBook.tsx @@ -28,6 +28,7 @@ export function ContactBook({ showCancelButton = false, onPressCancel, explanationComponent, + quickActions, }: { searchPlaceholder?: string; searchable?: boolean; @@ -38,6 +39,7 @@ export function ContactBook({ showCancelButton?: boolean; onPressCancel?: () => void; explanationComponent?: React.ReactElement; + quickActions?: React.ReactElement; }) { const contacts = useContacts(); const contactsIndex = useContactIndex(); @@ -119,6 +121,8 @@ export function ContactBook({ [onScrollChange] ); + const QuickActions = () => quickActions ?? null; + return ( {searchable && ( @@ -145,6 +149,7 @@ export function ContactBook({ ) : ( + {quickActions && !showSearchResults && } Date: Wed, 18 Sep 2024 14:26:15 -0400 Subject: [PATCH 075/157] remove metadata scry from lure fetch --- packages/shared/src/store/lure.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index 4128b4486d..27cb429ecd 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -130,7 +130,7 @@ export const useLureState = create((set, get) => ({ const { name } = getFlagParts(flag); const prevLure = get().lures[flag]; lureLogger.log('fetching', flag, 'prevLure', prevLure); - const [enabled, url, metadata] = await Promise.all([ + const [enabled, url] = await Promise.all([ // enabled asyncWithDefault(async () => { lureLogger.log(performance.now(), 'fetching enabled', flag); @@ -157,18 +157,9 @@ export const useLureState = create((set, get) => ({ return u; }); }, prevLure?.url), - // metadata - asyncWithDefault( - async () => - scry({ - app: 'reel', - path: `/v1/metadata/${flag}`, - }), - prevLure?.metadata - ), ]); - lureLogger.log('fetched', { flag, enabled, url, metadata }); + lureLogger.log('fetched', { flag, enabled, url }); let deepLinkUrl: string | undefined; lureLogger.log('enabled', enabled); @@ -205,7 +196,6 @@ export const useLureState = create((set, get) => ({ enabled, url, deepLinkUrl, - metadata, }; }) ); From d3f9bc84772dd277ca0ce17db36229273c683351 Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 18 Sep 2024 21:16:51 +0200 Subject: [PATCH 076/157] expose: more complete widget rendering Should contain approximately the same info as their listings in a channel view do. --- desk/app/expose.hoon | 208 ++++++++++++++++++++++++++----------------- 1 file changed, 126 insertions(+), 82 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 88788f5f55..e136bbbf17 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -22,22 +22,90 @@ :: +$ card card:agent:gall :: -++ e +++ r :: generic-ish rendering utils + |% + ++ author-node + |= [[our=@p now=@da] author=ship] + ^- manx + =/ aco=(unit contact:co) + (get-contact [our now] author) + ;div.author + ;div.avatar + ;+ + ?: &(?=(^ aco) ?=(^ avatar.u.aco) !=('' u.avatar.u.aco)) + ;img@"{(trip u.avatar.u.aco)}"(alt "Author's avatar"); + =/ val=@ux ?~(aco 0x0 color.u.aco) + =/ col=tape ((x-co:^co 6) val) + %. author + %_ sigil + size 25 + icon & + bg '#'^col + fg ?:((gth (div (roll (rip 3 val) add) 3) 180) "black" "white") ::REVIEW + == + == + :: + ;+ + =* nom ;span:"{(scow %p author)}" + ?~ aco nom + ?: =('' nickname.u.aco) nom + ;span(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" + == + :: + ++ render-datetime ::TODO date-only mode + |= =time + ^- manx + =, chrono:userlib + =; utc=tape + ::NOTE timestamp-utc class and ms attr used by +time-script, + :: which replaces this rendering with the local time + ;time.timestamp-utc(ms (a-co:^co (unm time))) + ; {utc} + == + =/ =date (yore time) + |^ "{(snag (dec m.date) mon:yu)} ". + "{(num d.t.date)}{(ith d.t.date)}, ". + "{(num y.date)}, ". + "{(dum h.t.date)}:{(dum m.t.date)} (UTC)" + ++ num a-co:^co + ++ dum (d-co:^co 2) + ++ ith + |= n=@ud + ?- n + %1 "st" + %2 "nd" + %3 "rd" + ?(%11 %12 %13) "th" + :: + @ + ?: (lth n 10) "th" + $(n (mod n 10)) + == + -- + :: + ++ time-script-node + ;script(type "text/javascript"):"{(trip time-script)}" + ++ time-script + ''' + const a = document.getElementsByClassName('timestamp-utc'); + for (const e of a) { + const t = new Date(Number(e.attributes['ms'].value)); + e.innerText = t.toLocaleString('en-US', {month: 'long'}) + ' ' + + (a=>a+=[,"st","nd","rd"][a.match`1?.$`]||"th")(''+t.getDate()) + ', ' + + t.getFullYear() + ', ' + + t.toLocaleString('en-US', {hour: '2-digit', minute: '2-digit', hour12: false}); + }; + ''' + -- +:: +++ e :: expose rendering |% ++ render-post |= [our=@p now=@da] |= [=nest:g:c msg=post:d] ^- (unit manx) =/ aco=(unit contact:co) - =/ base=path /(scot %p our)/contacts/(scot %da now) - ?. .^(? %gu (weld base /$)) - ~ - =+ .^(rol=rolodex:co %gx (weld base /all/contact-rolodex)) - ?~ for=(~(get by rol) author.msg) - ~ - ?. ?=([[@ ^] *] u.for) - ~ - `con.for.u.for + (get-contact [our now] author.msg) :: ::TODO if we render replies then we can "unroll" whole chat threads too (: ::TODO just key off the kind-data, no? @@ -98,7 +166,7 @@ == ;+ footer == - ;script(type "text/javascript"):"{(trip time-script)}" + ;+ time-script-node:r == :: ++ time-script @@ -1879,9 +1947,9 @@ ^- manx ;div.prelude ;div.published - ;+ (render-datetime sent.msg) + ;+ (render-datetime:r sent.msg) == - ;+ author-node + ;+ (author-node:r [our now] author.msg) == :: ++ diary-prelude @@ -1890,9 +1958,9 @@ :~ ;h1:"{title}" ;div.prelude ;div.published - ;+ (render-datetime sent.msg) + ;+ (render-datetime:r sent.msg) == - ;+ author-node + ;+ (author-node:r [our now] author.msg) == == :: @@ -1904,67 +1972,10 @@ [-]~ ;div.prelude ;div.published - ;+ (render-datetime sent.msg) - == - ;+ author-node - == - :: - ++ author-node - ^- manx - =* author author.msg - ;div.author - ;div.avatar - ;+ - ?: &(?=(^ aco) ?=(^ avatar.u.aco) !=('' u.avatar.u.aco)) - ;img@"{(trip u.avatar.u.aco)}"(alt "Author's avatar"); - =/ val=@ux ?~(aco 0x0 color.u.aco) - =/ col=tape ((x-co:^co 6) val) - %. author - %_ sigil - size 25 - icon & - bg '#'^col - fg ?:((gth (div (roll (rip 3 val) add) 3) 180) "black" "white") ::REVIEW - == + ;+ (render-datetime:r sent.msg) == - :: - ;+ - =* nom ;span:"{(scow %p author)}" - ?~ aco nom - ?: =('' nickname.u.aco) nom - ;span(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" + ;+ (author-node:r [our now] author.msg) == - :: - ++ render-datetime - |= =time - ^- manx - =, chrono:userlib - =; utc=tape - ::NOTE timestamp-utc class and ms attr used by +time-script, - :: which replaces this rendering with the local time - ;time.timestamp-utc(ms (a-co:^co (unm time))) - ; {utc} - == - =/ =date (yore time) - |^ "{(snag (dec m.date) mon:yu)} ". - "{(num d.t.date)}{(ith d.t.date)}, ". - "{(num y.date)}, ". - "{(dum h.t.date)}:{(dum m.t.date)} (UTC)" - ++ num a-co:^co - ++ dum (d-co:^co 2) - ++ ith - |= n=@ud - ?- n - %1 "st" - %2 "nd" - %3 "rd" - ?(%11 %12 %13) "th" - :: - @ - ?: (lth n 10) "th" - $(n (mod n 10)) - == - -- -- :: ++ post-from-cite @@ -2029,6 +2040,7 @@ } ''' :- ;style:"{(trip style)}" + =- (snoc - time-script-node:r) %+ murn cis |= ref=cite:c ^- (unit manx) @@ -2039,28 +2051,58 @@ =/ link=tape (spud (print:c ref)) =, post.u.pon - =/ desc=tape - ::TODO also want to know if there's.. more content, so we can - :: conditionally render the ellipses? - (trip (rap 3 (turn (first-inline:u content) flatten-inline:u))) ?- -.kind-data.post.u.pon %chat ;a.exposed.chat/"/expose{link}" - ;span:"{desc}" + ;div.meta + ;+ (author-node:r [our now] author) + ;+ (render-datetime:r sent) + == + ;div.content + ;* (story:en-manx:u content) + == == :: %diary ;a.exposed.diary/"/expose{link}" - ::TODO image background? + ;* ?: =('' image.kind-data) ~ + :_ ~ + ;img@"{(trip image.kind-data)}"; ;h3:"{(trip title.kind-data)}" - ;span:"{desc}" + ;+ (render-datetime:r sent) + ;+ (author-node:r [our now] author) == :: %heap - ;a.exposed.heap/"/expose{link}":"TODO gallery item?" + ::TODO for the kinds of children the div.content gets for heap posts, + :: having an %a (grand)parent breaks the html rendering, + :: putting the inner divs outside/after the a.exposed + ;a.exposed.heap/"/expose{link}" + ;div.content + ;* (story:en-manx:u content) + == + ;div.meta + ;+ (render-datetime:r sent) + ;+ (author-node:r [our now] author) + == + == == -- :: +++ get-contact + |= [[our=@p now=@da] who=@p] + => [+< co=co ..zuse] :: memoization aid + ^- (unit contact:co) ~+ + =/ base=path /(scot %p our)/contacts/(scot %da now) + ?. ~+ .^(? %gu (weld base /$)) + ~ + =+ ~+ .^(rol=rolodex:co %gx (weld base /all/contact-rolodex)) + ?~ for=(~(get by rol) who) + ~ + ?. ?=([[@ ^] *] u.for) + ~ + `con.for.u.for +:: ++ hutils :: http request utils ::NOTE most of the below are also available in /lib/server, but we :: reimplement them here for independence's sake @@ -2125,6 +2167,8 @@ |= ole=vase ^- (quip card _this) =. state !<(state-0 ole) + ::TODO defer, because it scries + ::TODO should update cache entries, styling/content might've changed [(update-widget:e [our now]:bowl open) this] :: ++ on-poke From c519e04b1a63ea56d92f56b34d3a4358f6de1250 Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 18 Sep 2024 21:19:24 +0200 Subject: [PATCH 077/157] expose: split css out into separate files And add a shared.css that gets loaded into both the individual pages and the widget. --- desk/app/expose.hoon | 1755 +----------------------------- desk/app/expose/style/page.css | 1709 +++++++++++++++++++++++++++++ desk/app/expose/style/shared.css | 1 + desk/app/expose/style/widget.css | 15 + 4 files changed, 1735 insertions(+), 1745 deletions(-) create mode 100644 desk/app/expose/style/page.css create mode 100644 desk/app/expose/style/shared.css create mode 100644 desk/app/expose/style/widget.css diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index e136bbbf17..be78e7023a 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -9,6 +9,10 @@ /+ sigil, u=channel-utils, dbug, verb :: +/* style-shared %css /app/expose/style/shared/css +/* style-widget %css /app/expose/style/widget/css +/* style-page %css /app/expose/style/page/css +:: |% +$ state-0 $: %0 @@ -169,1736 +173,12 @@ ;+ time-script-node:r == :: - ++ time-script - ''' - const a = document.getElementsByClassName('timestamp-utc'); - for (const e of a) { - const t = new Date(Number(e.attributes['ms'].value)); - e.innerText = t.toLocaleString('en-US', {month: 'long'}) + ' ' - + (a=>a+=[,"st","nd","rd"][a.match`1?.$`]||"th")(''+t.getDate()) + ', ' - + t.getFullYear() + ', ' - + t.toLocaleString('en-US', {hour: '2-digit', minute: '2-digit', hour12: false}); - }; - ''' - :: - ++ style - ''' - :root { - --background-body: #ffffff; - --background: #ffffff; - --background-alt: #f5f5f5; - --selection: rgba(24, 24, 24, 0.08); - --text-main: #1a1818; - --text-bright: #1a1818; - --text-muted: #666666; - --links: #008eff; - --focus: #008eff; - --border: #e5e5e5; - --code: #1a1818; - --animation-duration: 0.1s; - --button-base: #f5f5f5; - --button-hover: #e5e5e5; - --scrollbar-thumb: rgb(244, 244, 244); - --scrollbar-thumb-hover: var(--button-hover); - --form-placeholder: #999999; - --form-text: #1a1818; - --variable: #2ad546; - --highlight: #fade7a; - --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); - } - - @media (prefers-color-scheme: dark) { - :root { - --background-body: #1a1818; - --background: #1a1818; - --background-alt: #322e2e; - --selection: rgba(255, 255, 255, 0.08); - --text-main: #ffffff; - --text-bright: #fff; - --text-muted: #b3b3b3; - --links: #008eff; - --focus: #008eff; - --border: #333333; - --code: #ffffff; - --animation-duration: 0.1s; - --button-base: #322e2e; - --button-hover: #4c4c4c; - --scrollbar-thumb: var(--button-hover); - --scrollbar-thumb-hover: rgb(0, 0, 0); - --form-placeholder: #808080; - --form-text: #fff; - --variable: #2ad546; - --highlight: #fade7a; - --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); - } - } - - html { - scrollbar-color: rgb(244, 244, 244) #ffffff; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - scrollbar-width: thin; - } - - @media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } - } - - @media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } - } - - @media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } - } - - @media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } - } - - @media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } - } - - @media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } - } - - body { - font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", - "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", - "Helvetica Neue", "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", - sans-serif; - font-size: 16px; - font-weight: 400; - line-height: 1.6; - margin: 1em 1em; - padding: 0; - word-wrap: break-word; - color: #1a1818; - color: var(--text-main); - background: #ffffff; - background: var(--background-body); - text-rendering: optimizeLegibility; - } - - @media (prefers-color-scheme: dark) { - body { - background: #1a1818; - background: var(--background-body); - } - } - - @media (prefers-color-scheme: dark) { - body { - color: #ffffff; - color: var(--text-main); - } - } - - body.chat { - margin: 0; - } - - body.chat article { - margin: 0 1em; - } - - body.chat header > h1 { - padding: 1rem 1rem 0; - } - - @media screen and (min-width: 40rem) { - body { - font-size: 20px; - line-height: 1.8; - } - body:not(.chat) { - max-width: 40rem; - margin: 1em auto; - padding: 0 1em; - } - } - - @media screen and (min-width: 960px) { - body:not(.chat) { - margin: 2em auto 2em 2em; - padding: 0; - } - body.chat { - max-width: 40rem; - margin: 0 auto; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - overflow: auto; - } - } - - button { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } - - @media (prefers-color-scheme: dark) { - button { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } - } - - input { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } - - @media (prefers-color-scheme: dark) { - input { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } - } - - textarea { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } - - @media (prefers-color-scheme: dark) { - textarea { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } - } - - h1 { - margin-top: 0; - font-size: 2em; - font-weight: 400; - letter-spacing: -0.025em; - line-height: 1; - margin-top: 0; - margin-bottom: 0.6em; - } - - h2, - h3, - h4, - h5, - h6 { - margin-bottom: 0.6em; - margin-top: 1.25em; - font-size: 1em; - } - - h1 { - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - h1 { - color: #fff; - color: var(--text-bright); - } - } - - h2 { - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - h2 { - color: #fff; - color: var(--text-bright); - } - } - - h3 { - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - h3 { - color: #fff; - color: var(--text-bright); - } - } - - h4 { - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - h4 { - color: #fff; - color: var(--text-bright); - } - } - - h5 { - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - h5 { - color: #fff; - color: var(--text-bright); - } - } - - h6 { - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - h6 { - color: #fff; - color: var(--text-bright); - } - } - - strong { - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - strong { - color: #fff; - color: var(--text-bright); - } - } - - h2, - h3, - h4, - h5, - h6, - b, - strong, - th { - font-weight: 600; - } - - q::before { - content: none; - } - - q::after { - content: none; - } - - blockquote { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - margin: 1.5em 0; - padding: 0.5em 1em; - font-style: italic; - } - - @media (prefers-color-scheme: dark) { - blockquote { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - } - } - - q { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - margin: 1.5em 0; - padding: 0.5em 1em; - font-style: italic; - } - - @media (prefers-color-scheme: dark) { - q { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - } - } - - blockquote > footer { - font-style: normal; - border: 0; - } - - blockquote cite { - font-style: normal; - } - - address { - font-style: normal; - } - - a[href^="mailto\:"]::before { - content: "📧 "; - } - - a[href^="tel\:"]::before { - content: "📞 "; - } - - a[href^="sms\:"]::before { - content: "💬 "; - } - - mark { - background-color: #fade7a; - background-color: var(--highlight); - border-radius: 0.125em; - padding: 0 0.125em 0 0.125em; - color: #000; - } - - @media (prefers-color-scheme: dark) { - mark { - background-color: #fade7a; - background-color: var(--highlight); - } - } - - a > code, - a > strong { - color: inherit; - } - - button, - select, - input[type="submit"], - input[type="reset"], - input[type="button"], - input[type="checkbox"], - input[type="range"], - input[type="radio"] { - cursor: pointer; - } - - input, - select { - display: block; - } - - [type="checkbox"], - [type="radio"] { - display: initial; - } - - input { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; - } - - @media (prefers-color-scheme: dark) { - input { - background-color: #1a1818; - background-color: var(--background); - } - } - - @media (prefers-color-scheme: dark) { - input { - color: #fff; - color: var(--form-text); - } - } - - button { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; - } - - @media (prefers-color-scheme: dark) { - button { - background-color: #1a1818; - background-color: var(--background); - } - } - - @media (prefers-color-scheme: dark) { - button { - color: #fff; - color: var(--form-text); - } - } - - textarea { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; - } - - @media (prefers-color-scheme: dark) { - textarea { - background-color: #1a1818; - background-color: var(--background); - } - } - - @media (prefers-color-scheme: dark) { - textarea { - color: #fff; - color: var(--form-text); - } - } - - select { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; - } - - @media (prefers-color-scheme: dark) { - select { - background-color: #1a1818; - background-color: var(--background); - } - } - - @media (prefers-color-scheme: dark) { - select { - color: #fff; - color: var(--form-text); - } - } - - button { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; - } - - @media (prefers-color-scheme: dark) { - button { - background-color: #322e2e; - background-color: var(--button-base); - } - } - - input[type="submit"] { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; - } - - @media (prefers-color-scheme: dark) { - input[type="submit"] { - background-color: #322e2e; - background-color: var(--button-base); - } - } - - input[type="reset"] { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; - } - - @media (prefers-color-scheme: dark) { - input[type="reset"] { - background-color: #322e2e; - background-color: var(--button-base); - } - } - - input[type="button"] { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; - } - - @media (prefers-color-scheme: dark) { - input[type="button"] { - background-color: #322e2e; - background-color: var(--button-base); - } - } - - button:hover { - background: #e5e5e5; - background: var(--button-hover); - } - - @media (prefers-color-scheme: dark) { - button:hover { - background: #4c4c4c; - background: var(--button-hover); - } - } - - input[type="submit"]:hover { - background: #e5e5e5; - background: var(--button-hover); - } - - @media (prefers-color-scheme: dark) { - input[type="submit"]:hover { - background: #4c4c4c; - background: var(--button-hover); - } - } - - input[type="reset"]:hover { - background: #e5e5e5; - background: var(--button-hover); - } - - @media (prefers-color-scheme: dark) { - input[type="reset"]:hover { - background: #4c4c4c; - background: var(--button-hover); - } - } - - input[type="button"]:hover { - background: #e5e5e5; - background: var(--button-hover); - } - - @media (prefers-color-scheme: dark) { - input[type="button"]:hover { - background: #4c4c4c; - background: var(--button-hover); - } - } - - input[type="color"] { - min-height: 2rem; - padding: 0.4em; - cursor: pointer; - } - - input[type="checkbox"], - input[type="radio"] { - height: 1em; - width: 1em; - } - - input[type="radio"] { - border-radius: 100%; - } - - input { - vertical-align: top; - } - - label { - vertical-align: middle; - margin-bottom: 0.25em; - display: inline-block; - } - - input:not([type="checkbox"]):not([type="radio"]), - input[type="range"], - select, - button, - textarea { - -webkit-appearance: none; - } - - textarea { - display: block; - margin-right: 0; - box-sizing: border-box; - resize: vertical; - } - - textarea:not([cols]) { - width: 100%; - } - - textarea:not([rows]) { - min-height: 2em; - height: 12em; - } - - select { - background: #ffffff - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / 10.125em - no-repeat; - padding-right: 1.75em; - } - - @media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } - } - - @media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } - } - - @media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } - } - - @media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } - } - - select::-ms-expand { - display: none; - } - - select[multiple] { - padding-right: 0.5em; - background-image: none; - overflow-y: auto; - } - - input:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - - @media (prefers-color-scheme: dark) { - input:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - } - - select:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - - @media (prefers-color-scheme: dark) { - select:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - } - - button:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - - @media (prefers-color-scheme: dark) { - button:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - } - - textarea:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - - @media (prefers-color-scheme: dark) { - textarea:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } - } - - input[type="checkbox"]:active, - input[type="radio"]:active, - input[type="submit"]:active, - input[type="reset"]:active, - input[type="button"]:active, - input[type="range"]:active, - button:active { - transform: translateY(0.125em); - } - - input:disabled, - select:disabled, - button:disabled, - textarea:disabled { - cursor: not-allowed; - opacity: 0.5; - } - - ::-moz-placeholder { - color: #999999; - color: var(--form-placeholder); - } - - :-ms-input-placeholder { - color: #999999; - color: var(--form-placeholder); - } - - ::-ms-input-placeholder { - color: #999999; - color: var(--form-placeholder); - } - - ::placeholder { - color: #999999; - color: var(--form-placeholder); - } - - @media (prefers-color-scheme: dark) { - ::-moz-placeholder { - color: #808080; - color: var(--form-placeholder); - } - - :-ms-input-placeholder { - color: #808080; - color: var(--form-placeholder); - } - - ::-ms-input-placeholder { - color: #808080; - color: var(--form-placeholder); - } - - ::placeholder { - color: #808080; - color: var(--form-placeholder); - } - } - - fieldset { - border: 0.125em #008eff solid; - border: 0.125em var(--focus) solid; - border-radius: 0.25em; - margin: 0; - margin-bottom: 10.125em; - padding: 0.5em; - } - - @media (prefers-color-scheme: dark) { - fieldset { - border: 0.125em #008eff solid; - border: 0.125em var(--focus) solid; - } - } - - legend { - font-size: 0.9em; - font-weight: 600; - } - - input[type="range"] { - margin: 0.5em 0; - padding: 0.5em 0; - background: transparent; - } - - input[type="range"]:focus { - outline: none; - } - - input[type="range"]::-webkit-slider-runnable-track { - width: 100%; - height: 0.5em; - -webkit-transition: 0.2s; - transition: 0.2s; - background: #ffffff; - background: var(--background); - border-radius: 0.25em; - } - - @media (prefers-color-scheme: dark) { - input[type="range"]::-webkit-slider-runnable-track { - background: #1a1818; - background: var(--background); - } - } - - input[type="range"]::-webkit-slider-thumb { - box-shadow: 0 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - height: 1em; - width: 1em; - border-radius: 50%; - background: #e5e5e5; - background: var(--border); - -webkit-appearance: none; - margin-top: -0.3em; - } - - @media (prefers-color-scheme: dark) { - input[type="range"]::-webkit-slider-thumb { - background: #333333; - background: var(--border); - } - } - - input[type="range"]:focus::-webkit-slider-runnable-track { - background: #ffffff; - background: var(--background); - } - - @media (prefers-color-scheme: dark) { - input[type="range"]:focus::-webkit-slider-runnable-track { - background: #1a1818; - background: var(--background); - } - } - - input[type="range"]::-moz-range-track { - width: 100%; - height: 1em; - -moz-transition: 0.2s; - transition: 0.2s; - background: #ffffff; - background: var(--background); - border-radius: 0.25em; - } - - @media (prefers-color-scheme: dark) { - input[type="range"]::-moz-range-track { - background: #1a1818; - background: var(--background); - } - } - - input[type="range"]::-moz-range-thumb { - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - height: 1em; - width: 1em; - border-radius: 50%; - background: #e5e5e5; - background: var(--border); - } - - @media (prefers-color-scheme: dark) { - input[type="range"]::-moz-range-thumb { - background: #333333; - background: var(--border); - } - } - - input[type="range"]::-ms-track { - width: 100%; - height: 0.5em; - background: transparent; - border-color: transparent; - border-width: 10.25em 0; - color: transparent; - } - - input[type="range"]::-ms-fill-lower { - background: #ffffff; - background: var(--background); - border: 0.0.125em solid #010101; - border-radius: 0.25em; - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - } - - @media (prefers-color-scheme: dark) { - input[type="range"]::-ms-fill-lower { - background: #1a1818; - background: var(--background); - } - } - - input[type="range"]::-ms-fill-upper { - background: #ffffff; - background: var(--background); - border: 0.0.125em solid #010101; - border-radius: 0.25em; - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - } - - @media (prefers-color-scheme: dark) { - input[type="range"]::-ms-fill-upper { - background: #1a1818; - background: var(--background); - } - } - - input[type="range"]::-ms-thumb { - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - border: 0.125em solid #000; - height: 1em; - width: 1em; - border-radius: 50%; - background: #e5e5e5; - background: var(--border); - } - - @media (prefers-color-scheme: dark) { - input[type="range"]::-ms-thumb { - background: #333333; - background: var(--border); - } - } - - input[type="range"]:focus::-ms-fill-lower { - background: #ffffff; - background: var(--background); - } - - @media (prefers-color-scheme: dark) { - input[type="range"]:focus::-ms-fill-lower { - background: #1a1818; - background: var(--background); - } - } - - input[type="range"]:focus::-ms-fill-upper { - background: #ffffff; - background: var(--background); - } - - @media (prefers-color-scheme: dark) { - input[type="range"]:focus::-ms-fill-upper { - background: #1a1818; - background: var(--background); - } - } - - a { - text-decoration: underline; - color: #008eff; - color: var(--links); - } - - @media (prefers-color-scheme: dark) { - a { - color: #008eff; - color: var(--links); - } - } - - a:hover { - text-decoration: underline; - } - - code { - background: #ffffff; - background: var(--background); - color: #1a1818; - color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; - font-size: 1em; - } - - @media (prefers-color-scheme: dark) { - code { - color: #ffffff; - color: var(--code); - } - } - - @media (prefers-color-scheme: dark) { - code { - background: #1a1818; - background: var(--background); - } - } - - samp { - background: #ffffff; - background: var(--background); - color: #1a1818; - color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; - font-size: 1em; - } - - @media (prefers-color-scheme: dark) { - samp { - color: #ffffff; - color: var(--code); - } - } - - @media (prefers-color-scheme: dark) { - samp { - background: #1a1818; - background: var(--background); - } - } - - time { - background: #ffffff; - background: var(--background); - color: #1a1818; - color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; - font-size: 1em; - } - - @media (prefers-color-scheme: dark) { - time { - color: #ffffff; - color: var(--code); - } - } - - @media (prefers-color-scheme: dark) { - time { - background: #1a1818; - background: var(--background); - } - } - - pre > code { - padding: 0.5em; - display: block; - overflow-x: auto; - } - - var { - color: #2ad546; - color: var(--variable); - font-style: normal; - font-family: monospace; - } - - @media (prefers-color-scheme: dark) { - var { - color: #2ad546; - color: var(--variable); - } - } - - kbd { - background: #ffffff; - background: var(--background); - border: 0.125em solid #e5e5e5; - border: 0.125em solid var(--border); - border-radius: 0.125em; - color: #1a1818; - color: var(--text-main); - padding: 0.125em 0.25em 0.125em 0.25em; - } - - @media (prefers-color-scheme: dark) { - kbd { - color: #ffffff; - color: var(--text-main); - } - } - - @media (prefers-color-scheme: dark) { - kbd { - border: 0.125em solid #333333; - border: 0.125em solid var(--border); - } - } - - @media (prefers-color-scheme: dark) { - kbd { - background: #1a1818; - background: var(--background); - } - } - - img, - video { - max-width: 100%; - height: auto; - } - - hr { - border: none; - border-top: 0.125em solid #e5e5e5; - border-top: 0.125em solid var(--border); - } - - @media (prefers-color-scheme: dark) { - hr { - border-top: 0.125em solid #333333; - border-top: 0.125em solid var(--border); - } - } - - table { - border-collapse: collapse; - margin-bottom: 0.5em; - width: 100%; - table-layout: fixed; - } - - table caption { - text-align: left; - } - - td, - th { - padding: 0.25em; - text-align: left; - vertical-align: top; - word-wrap: break-word; - } - - thead { - border-bottom: 0.125em solid #e5e5e5; - border-bottom: 0.125em solid var(--border); - } - - @media (prefers-color-scheme: dark) { - thead { - border-bottom: 0.125em solid #333333; - border-bottom: 0.125em solid var(--border); - } - } - - tfoot { - border-top: 0.125em solid #e5e5e5; - border-top: 0.125em solid var(--border); - } - - @media (prefers-color-scheme: dark) { - tfoot { - border-top: 0.125em solid #333333; - border-top: 0.125em solid var(--border); - } - } - - tbody tr:nth-child(even) { - background-color: #ffffff; - background-color: var(--background); - } - - @media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) { - background-color: #1a1818; - background-color: var(--background); - } - } - - tbody tr:nth-child(even) button { - background-color: #f5f5f5; - background-color: var(--background-alt); - } - - @media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) button { - background-color: #322e2e; - background-color: var(--background-alt); - } - } - - tbody tr:nth-child(even) button:hover { - background-color: #ffffff; - background-color: var(--background-body); - } - - @media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) button:hover { - background-color: #1a1818; - background-color: var(--background-body); - } - } - - ::-webkit-scrollbar { - height: 0.5em; - width: 0.5em; - } - - ::-webkit-scrollbar-track { - background: #ffffff; - background: var(--background); - border-radius: 0.25em; - } - - @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-track { - background: #1a1818; - background: var(--background); - } - } - - ::-webkit-scrollbar-thumb { - background: rgb(244, 244, 244); - background: var(--scrollbar-thumb); - border-radius: 0.25em; - } - - @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb { - background: #4c4c4c; - background: var(--scrollbar-thumb); - } - } - - @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb { - background: #4c4c4c; - background: var(--scrollbar-thumb); - } - } - - ::-webkit-scrollbar-thumb:hover { - background: #e5e5e5; - background: var(--scrollbar-thumb-hover); - } - - @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); - background: var(--scrollbar-thumb-hover); - } - } - - @media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); - background: var(--scrollbar-thumb-hover); - } - } - - ::-moz-selection { - background-color: rgba(24, 24, 24, 0.08); - background-color: var(--selection); - color: #1a1818; - color: var(--text-bright); - } - - ::selection { - background-color: rgba(24, 24, 24, 0.08); - background-color: var(--selection); - color: #1a1818; - color: var(--text-bright); - } - - @media (prefers-color-scheme: dark) { - ::-moz-selection { - color: #fff; - color: var(--text-bright); - } - - ::selection { - color: #fff; - color: var(--text-bright); - } - } - - @media (prefers-color-scheme: dark) { - ::-moz-selection { - background-color: rgba(255, 255, 255, 0.08); - background-color: var(--selection); - } - - ::selection { - background-color: rgba(255, 255, 255, 0.08); - background-color: var(--selection); - } - } - - details { - display: flex; - flex-direction: column; - align-items: flex-start; - background-color: #f5f5f5; - background-color: var(--background-alt); - padding: 0.5em 0.5em 0; - margin: 1em 0; - border-radius: 0.25em; - overflow: hidden; - } - - @media (prefers-color-scheme: dark) { - details { - background-color: #322e2e; - background-color: var(--background-alt); - } - } - - details[open] { - padding: 0.5em; - } - - details > :last-child { - margin-bottom: 0; - } - - details[open] summary { - margin-bottom: 0.5em; - } - - summary { - display: list-item; - background-color: #ffffff; - background-color: var(--background); - padding: 0.5em; - margin: -0.5em -0.5em 0; - cursor: pointer; - outline: none; - } - - @media (prefers-color-scheme: dark) { - summary { - background-color: #1a1818; - background-color: var(--background); - } - } - - summary:hover, - summary:focus { - text-decoration: underline; - } - - details > :not(summary) { - margin-top: 0; - } - - summary::-webkit-details-marker { - color: #1a1818; - color: var(--text-main); - } - - @media (prefers-color-scheme: dark) { - summary::-webkit-details-marker { - color: #ffffff; - color: var(--text-main); - } - } - - dialog { - background-color: #f5f5f5; - background-color: var(--background-alt); - color: #1a1818; - color: var(--text-main); - border: none; - border-radius: 0.25em; - border-color: #e5e5e5; - border-color: var(--border); - padding: 0.5em 1.5em; - } - - @media (prefers-color-scheme: dark) { - dialog { - border-color: #333333; - border-color: var(--border); - } - } - - @media (prefers-color-scheme: dark) { - dialog { - color: #ffffff; - color: var(--text-main); - } - } - - @media (prefers-color-scheme: dark) { - dialog { - background-color: #322e2e; - background-color: var(--background-alt); - } - } - - dialog > header:first-child { - background-color: #ffffff; - background-color: var(--background); - border-radius: 0.25em 0.25em 0 0; - margin: -0.5em -1.5em 0.5em; - padding: 0.5em; - text-align: center; - } - - @media (prefers-color-scheme: dark) { - dialog > header:first-child { - background-color: #1a1818; - background-color: var(--background); - } - } - - dialog::-webkit-backdrop { - background: #0000009c; - -webkit-backdrop-filter: blur(0.25em); - backdrop-filter: blur(0.25em); - } - - dialog::backdrop { - background: #0000009c; - -webkit-backdrop-filter: blur(0.25em); - backdrop-filter: blur(0.25em); - } - - footer { - position: fixed; - bottom: 1em; - right: 1em; - background: var(--background); - font-size: 0.75em; - padding: 0.5em 0.75em; - border-radius: 0.5em; - border: 0.125em solid var(--border); - box-shadow: 0 0.125em 0.25em var(--selection), 0 0 1em var(--selection); - } - - @media screen and (min-width: 960px) { - footer { - bottom: 2em; - right: 2em; - } - } - - footer a { - display: flex; - gap: 0.5em; - align-items: center; - text-decoration: none; - color: var(--text-main); - } - - @media (prefers-color-scheme: dark) { - footer { - border-top: 0.125em solid #333333; - border-top: 0.125em solid var(--border); - } - } - - body > footer { - margin-top: 2em; - } - - @media print { - body, - pre, - code, - summary, - details, - button, - input, - textarea { - background-color: #fff; - } - - button, - input, - textarea { - border: 0.125em solid #000; - } - - body, - h1, - h2, - h3, - h4, - h5, - h6, - pre, - code, - button, - input, - textarea, - footer, - summary, - strong { - color: #000; - } - - summary::marker { - color: #000; - } - - summary::-webkit-details-marker { - color: #000; - } - - tbody tr:nth-child(even) { - background-color: #f2f2f2; - } - - a { - color: #00f; - text-decoration: underline; - } - } - - .cover { - border-radius: 1em; - margin-bottom: 1em; - } - - .prelude { - font-size: 0.75em; - display: flex; - flex-direction: column; - gap: 0.4em; - border-bottom: 0.125em solid var(--border); - padding-bottom: 1em; - margin-bottom: 1em; - } - - body.chat .prelude { - padding: 1em; - flex-direction: row-reverse; - justify-content: space-between; - align-items: center; - } - - @media screen and (min-width: 960px) { - body.chat .prelude { - margin: 0; - position: fixed; - bottom: 2em; - left: 2em; - border: 0; - background: var(--background); - font-size: 0.75em; - padding: 0.5em 0.75em; - border-radius: 0.5em; - gap: 1em; - } - } - - .prelude .author { - display: flex; - align-items: center; - gap: 0.5em; - font-weight: 500; - color: var(--text-muted); - } - - .prelude .author .avatar { - border-radius: 0.25em; - overflow: hidden; - } - - .prelude .author .avatar img { - width: 1.5625em; - height: 1.5625em; - object-fit: cover; - } - - .prelude time { - color: var(--text-muted); - padding: 0; - } - - article p:empty { - display: none; - } - - article img { - max-width: 100%; - border-radius: 0.5em; - } - ''' - :: ++ heads |= [title=tape img=(unit @t)] ;head ;title:"{title}" - ;style:"{(trip style)}" + ;link(rel "stylesheet", href "/expose/style/shared.css"); + ;style:"{(trip style-page)}" ;link(rel "preconnect", href "https://rsms.me"); ;link(rel "stylesheet", href "https://rsms.me/inter/inter.css"); :: @@ -2021,25 +301,7 @@ ?~ aa=(rush i.t.wer.a dum:ag) | ?~ bb=(rush i.t.wer.b dum:ag) & (gth u.aa u.bb) - =/ style=@t - ''' - /* TODO - prefer scoping rules to just elements under #groups--expose-all, - otherwise style may affect other widgets on the profile - (alternatively do inline style attributes) - */ - #groups--expose-all { - border: 1px solid red; - } - #groups--expose-all a { - display: block; - margin: 1em; - } - #groups--expose-all .exposed { - border: 1px solid black; - } - ''' - :- ;style:"{(trip style)}" + :- ;style:"{(trip style-shared)} {(trip style-widget)}" =- (snoc - time-script-node:r) %+ murn cis |= ref=cite:c @@ -2227,6 +489,9 @@ :: if we handled a request here, make sure it's cached for next time :: [%pass /eyre/cache %arvo %e %set-response url.request `[| %payload payload]] + ?: =('/expose/style/shared.css' url.request) + :- [200 ['content-type' 'text/css']~] + `(as-octs:mimes:html style-shared) =/ ref=(unit cite:c) (rush url.request (sear purse:c ;~(pfix (jest '/expose') stap))) ?~ ref diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css new file mode 100644 index 0000000000..d4889d7b80 --- /dev/null +++ b/desk/app/expose/style/page.css @@ -0,0 +1,1709 @@ +:root { + --background-body: #ffffff; + --background: #ffffff; + --background-alt: #f5f5f5; + --selection: rgba(24, 24, 24, 0.08); + --text-main: #1a1818; + --text-bright: #1a1818; + --text-muted: #666666; + --links: #008eff; + --focus: #008eff; + --border: #e5e5e5; + --code: #1a1818; + --animation-duration: 0.1s; + --button-base: #f5f5f5; + --button-hover: #e5e5e5; + --scrollbar-thumb: rgb(244, 244, 244); + --scrollbar-thumb-hover: var(--button-hover); + --form-placeholder: #999999; + --form-text: #1a1818; + --variable: #2ad546; + --highlight: #fade7a; + --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); +} + +@media (prefers-color-scheme: dark) { + :root { + --background-body: #1a1818; + --background: #1a1818; + --background-alt: #322e2e; + --selection: rgba(255, 255, 255, 0.08); + --text-main: #ffffff; + --text-bright: #fff; + --text-muted: #b3b3b3; + --links: #008eff; + --focus: #008eff; + --border: #333333; + --code: #ffffff; + --animation-duration: 0.1s; + --button-base: #322e2e; + --button-hover: #4c4c4c; + --scrollbar-thumb: var(--button-hover); + --scrollbar-thumb-hover: rgb(0, 0, 0); + --form-placeholder: #808080; + --form-text: #fff; + --variable: #2ad546; + --highlight: #fade7a; + --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); + } +} + +html { + scrollbar-color: rgb(244, 244, 244) #ffffff; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-width: thin; +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +body { + font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", + "Helvetica Neue", "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", + sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 1.6; + margin: 1em 1em; + padding: 0; + word-wrap: break-word; + color: #1a1818; + color: var(--text-main); + background: #ffffff; + background: var(--background-body); + text-rendering: optimizeLegibility; +} + +@media (prefers-color-scheme: dark) { + body { + background: #1a1818; + background: var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + body { + color: #ffffff; + color: var(--text-main); + } +} + +body.chat { + margin: 0; +} + +body.chat article { + margin: 0 1em; +} + +body.chat header > h1 { + padding: 1rem 1rem 0; +} + +@media screen and (min-width: 40rem) { + body { + font-size: 20px; + line-height: 1.8; + } + body:not(.chat) { + max-width: 40rem; + margin: 1em auto; + padding: 0 1em; + } +} + +@media screen and (min-width: 960px) { + body:not(.chat) { + margin: 2em auto 2em 2em; + padding: 0; + } + body.chat { + max-width: 40rem; + margin: 0 auto; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + overflow: auto; + } +} + +button { + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; +} + +@media (prefers-color-scheme: dark) { + button { + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } +} + +input { + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; +} + +@media (prefers-color-scheme: dark) { + input { + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } +} + +textarea { + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; +} + +@media (prefers-color-scheme: dark) { + textarea { + transition: background-color 0.1s linear, border-color 0.1s linear, + color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; + transition: background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } +} + +h1 { + margin-top: 0; + font-size: 2em; + font-weight: 400; + letter-spacing: -0.025em; + line-height: 1; + margin-top: 0; + margin-bottom: 0.6em; +} + +h2, +h3, +h4, +h5, +h6 { + margin-bottom: 0.6em; + margin-top: 1.25em; + font-size: 1em; +} + +h1 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h1 { + color: #fff; + color: var(--text-bright); + } +} + +h2 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h2 { + color: #fff; + color: var(--text-bright); + } +} + +h3 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h3 { + color: #fff; + color: var(--text-bright); + } +} + +h4 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h4 { + color: #fff; + color: var(--text-bright); + } +} + +h5 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h5 { + color: #fff; + color: var(--text-bright); + } +} + +h6 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h6 { + color: #fff; + color: var(--text-bright); + } +} + +strong { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + strong { + color: #fff; + color: var(--text-bright); + } +} + +h2, +h3, +h4, +h5, +h6, +b, +strong, +th { + font-weight: 600; +} + +q::before { + content: none; +} + +q::after { + content: none; +} + +blockquote { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + margin: 1.5em 0; + padding: 0.5em 1em; + font-style: italic; +} + +@media (prefers-color-scheme: dark) { + blockquote { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + } +} + +q { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + margin: 1.5em 0; + padding: 0.5em 1em; + font-style: italic; +} + +@media (prefers-color-scheme: dark) { + q { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + } +} + +blockquote > footer { + font-style: normal; + border: 0; +} + +blockquote cite { + font-style: normal; +} + +address { + font-style: normal; +} + +a[href^="mailto\:"]::before { + content: "📧 "; +} + +a[href^="tel\:"]::before { + content: "📞 "; +} + +a[href^="sms\:"]::before { + content: "💬 "; +} + +mark { + background-color: #fade7a; + background-color: var(--highlight); + border-radius: 0.125em; + padding: 0 0.125em 0 0.125em; + color: #000; +} + +@media (prefers-color-scheme: dark) { + mark { + background-color: #fade7a; + background-color: var(--highlight); + } +} + +a > code, +a > strong { + color: inherit; +} + +button, +select, +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="checkbox"], +input[type="range"], +input[type="radio"] { + cursor: pointer; +} + +input, +select { + display: block; +} + +[type="checkbox"], +[type="radio"] { + display: initial; +} + +input { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + input { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + input { + color: #fff; + color: var(--form-text); + } +} + +button { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + button { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + button { + color: #fff; + color: var(--form-text); + } +} + +textarea { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + textarea { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + textarea { + color: #fff; + color: var(--form-text); + } +} + +select { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + select { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + select { + color: #fff; + color: var(--form-text); + } +} + +button { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + button { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +input[type="submit"] { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + input[type="submit"] { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +input[type="reset"] { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + input[type="reset"] { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +input[type="button"] { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + input[type="button"] { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +button:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + button:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type="submit"]:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + input[type="submit"]:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type="reset"]:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + input[type="reset"]:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type="button"]:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + input[type="button"]:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type="color"] { + min-height: 2rem; + padding: 0.4em; + cursor: pointer; +} + +input[type="checkbox"], +input[type="radio"] { + height: 1em; + width: 1em; +} + +input[type="radio"] { + border-radius: 100%; +} + +input { + vertical-align: top; +} + +label { + vertical-align: middle; + margin-bottom: 0.25em; + display: inline-block; +} + +input:not([type="checkbox"]):not([type="radio"]), +input[type="range"], +select, +button, +textarea { + -webkit-appearance: none; +} + +textarea { + display: block; + margin-right: 0; + box-sizing: border-box; + resize: vertical; +} + +textarea:not([cols]) { + width: 100%; +} + +textarea:not([rows]) { + min-height: 2em; + height: 12em; +} + +select { + background: #ffffff + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / 10.125em + no-repeat; + padding-right: 1.75em; +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +select::-ms-expand { + display: none; +} + +select[multiple] { + padding-right: 0.5em; + background-image: none; + overflow-y: auto; +} + +input:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + input:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +select:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + select:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +button:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + button:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +textarea:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + textarea:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +input[type="checkbox"]:active, +input[type="radio"]:active, +input[type="submit"]:active, +input[type="reset"]:active, +input[type="button"]:active, +input[type="range"]:active, +button:active { + transform: translateY(0.125em); +} + +input:disabled, +select:disabled, +button:disabled, +textarea:disabled { + cursor: not-allowed; + opacity: 0.5; +} + +::-moz-placeholder { + color: #999999; + color: var(--form-placeholder); +} + +:-ms-input-placeholder { + color: #999999; + color: var(--form-placeholder); +} + +::-ms-input-placeholder { + color: #999999; + color: var(--form-placeholder); +} + +::placeholder { + color: #999999; + color: var(--form-placeholder); +} + +@media (prefers-color-scheme: dark) { + ::-moz-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + :-ms-input-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + ::-ms-input-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + ::placeholder { + color: #808080; + color: var(--form-placeholder); + } +} + +fieldset { + border: 0.125em #008eff solid; + border: 0.125em var(--focus) solid; + border-radius: 0.25em; + margin: 0; + margin-bottom: 10.125em; + padding: 0.5em; +} + +@media (prefers-color-scheme: dark) { + fieldset { + border: 0.125em #008eff solid; + border: 0.125em var(--focus) solid; + } +} + +legend { + font-size: 0.9em; + font-weight: 600; +} + +input[type="range"] { + margin: 0.5em 0; + padding: 0.5em 0; + background: transparent; +} + +input[type="range"]:focus { + outline: none; +} + +input[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 0.5em; + -webkit-transition: 0.2s; + transition: 0.2s; + background: #ffffff; + background: var(--background); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + input[type="range"]::-webkit-slider-runnable-track { + background: #1a1818; + background: var(--background); + } +} + +input[type="range"]::-webkit-slider-thumb { + box-shadow: 0 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; + height: 1em; + width: 1em; + border-radius: 50%; + background: #e5e5e5; + background: var(--border); + -webkit-appearance: none; + margin-top: -0.3em; +} + +@media (prefers-color-scheme: dark) { + input[type="range"]::-webkit-slider-thumb { + background: #333333; + background: var(--border); + } +} + +input[type="range"]:focus::-webkit-slider-runnable-track { + background: #ffffff; + background: var(--background); +} + +@media (prefers-color-scheme: dark) { + input[type="range"]:focus::-webkit-slider-runnable-track { + background: #1a1818; + background: var(--background); + } +} + +input[type="range"]::-moz-range-track { + width: 100%; + height: 1em; + -moz-transition: 0.2s; + transition: 0.2s; + background: #ffffff; + background: var(--background); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + input[type="range"]::-moz-range-track { + background: #1a1818; + background: var(--background); + } +} + +input[type="range"]::-moz-range-thumb { + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; + height: 1em; + width: 1em; + border-radius: 50%; + background: #e5e5e5; + background: var(--border); +} + +@media (prefers-color-scheme: dark) { + input[type="range"]::-moz-range-thumb { + background: #333333; + background: var(--border); + } +} + +input[type="range"]::-ms-track { + width: 100%; + height: 0.5em; + background: transparent; + border-color: transparent; + border-width: 10.25em 0; + color: transparent; +} + +input[type="range"]::-ms-fill-lower { + background: #ffffff; + background: var(--background); + border: 0.0.125em solid #010101; + border-radius: 0.25em; + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; +} + +@media (prefers-color-scheme: dark) { + input[type="range"]::-ms-fill-lower { + background: #1a1818; + background: var(--background); + } +} + +input[type="range"]::-ms-fill-upper { + background: #ffffff; + background: var(--background); + border: 0.0.125em solid #010101; + border-radius: 0.25em; + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; +} + +@media (prefers-color-scheme: dark) { + input[type="range"]::-ms-fill-upper { + background: #1a1818; + background: var(--background); + } +} + +input[type="range"]::-ms-thumb { + box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; + border: 0.125em solid #000; + height: 1em; + width: 1em; + border-radius: 50%; + background: #e5e5e5; + background: var(--border); +} + +@media (prefers-color-scheme: dark) { + input[type="range"]::-ms-thumb { + background: #333333; + background: var(--border); + } +} + +input[type="range"]:focus::-ms-fill-lower { + background: #ffffff; + background: var(--background); +} + +@media (prefers-color-scheme: dark) { + input[type="range"]:focus::-ms-fill-lower { + background: #1a1818; + background: var(--background); + } +} + +input[type="range"]:focus::-ms-fill-upper { + background: #ffffff; + background: var(--background); +} + +@media (prefers-color-scheme: dark) { + input[type="range"]:focus::-ms-fill-upper { + background: #1a1818; + background: var(--background); + } +} + +a { + text-decoration: underline; + color: #008eff; + color: var(--links); +} + +@media (prefers-color-scheme: dark) { + a { + color: #008eff; + color: var(--links); + } +} + +a:hover { + text-decoration: underline; +} + +code { + background: #ffffff; + background: var(--background); + color: #1a1818; + color: var(--code); + padding: 0.125em 0.25em; + border-radius: 0.25em; + font-size: 1em; +} + +@media (prefers-color-scheme: dark) { + code { + color: #ffffff; + color: var(--code); + } +} + +@media (prefers-color-scheme: dark) { + code { + background: #1a1818; + background: var(--background); + } +} + +samp { + background: #ffffff; + background: var(--background); + color: #1a1818; + color: var(--code); + padding: 0.125em 0.25em; + border-radius: 0.25em; + font-size: 1em; +} + +@media (prefers-color-scheme: dark) { + samp { + color: #ffffff; + color: var(--code); + } +} + +@media (prefers-color-scheme: dark) { + samp { + background: #1a1818; + background: var(--background); + } +} + +time { + background: #ffffff; + background: var(--background); + color: #1a1818; + color: var(--code); + padding: 0.125em 0.25em; + border-radius: 0.25em; + font-size: 1em; +} + +@media (prefers-color-scheme: dark) { + time { + color: #ffffff; + color: var(--code); + } +} + +@media (prefers-color-scheme: dark) { + time { + background: #1a1818; + background: var(--background); + } +} + +pre > code { + padding: 0.5em; + display: block; + overflow-x: auto; +} + +var { + color: #2ad546; + color: var(--variable); + font-style: normal; + font-family: monospace; +} + +@media (prefers-color-scheme: dark) { + var { + color: #2ad546; + color: var(--variable); + } +} + +kbd { + background: #ffffff; + background: var(--background); + border: 0.125em solid #e5e5e5; + border: 0.125em solid var(--border); + border-radius: 0.125em; + color: #1a1818; + color: var(--text-main); + padding: 0.125em 0.25em 0.125em 0.25em; +} + +@media (prefers-color-scheme: dark) { + kbd { + color: #ffffff; + color: var(--text-main); + } +} + +@media (prefers-color-scheme: dark) { + kbd { + border: 0.125em solid #333333; + border: 0.125em solid var(--border); + } +} + +@media (prefers-color-scheme: dark) { + kbd { + background: #1a1818; + background: var(--background); + } +} + +img, +video { + max-width: 100%; + height: auto; +} + +hr { + border: none; + border-top: 0.125em solid #e5e5e5; + border-top: 0.125em solid var(--border); +} + +@media (prefers-color-scheme: dark) { + hr { + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); + } +} + +table { + border-collapse: collapse; + margin-bottom: 0.5em; + width: 100%; + table-layout: fixed; +} + +table caption { + text-align: left; +} + +td, +th { + padding: 0.25em; + text-align: left; + vertical-align: top; + word-wrap: break-word; +} + +thead { + border-bottom: 0.125em solid #e5e5e5; + border-bottom: 0.125em solid var(--border); +} + +@media (prefers-color-scheme: dark) { + thead { + border-bottom: 0.125em solid #333333; + border-bottom: 0.125em solid var(--border); + } +} + +tfoot { + border-top: 0.125em solid #e5e5e5; + border-top: 0.125em solid var(--border); +} + +@media (prefers-color-scheme: dark) { + tfoot { + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); + } +} + +tbody tr:nth-child(even) { + background-color: #ffffff; + background-color: var(--background); +} + +@media (prefers-color-scheme: dark) { + tbody tr:nth-child(even) { + background-color: #1a1818; + background-color: var(--background); + } +} + +tbody tr:nth-child(even) button { + background-color: #f5f5f5; + background-color: var(--background-alt); +} + +@media (prefers-color-scheme: dark) { + tbody tr:nth-child(even) button { + background-color: #322e2e; + background-color: var(--background-alt); + } +} + +tbody tr:nth-child(even) button:hover { + background-color: #ffffff; + background-color: var(--background-body); +} + +@media (prefers-color-scheme: dark) { + tbody tr:nth-child(even) button:hover { + background-color: #1a1818; + background-color: var(--background-body); + } +} + +::-webkit-scrollbar { + height: 0.5em; + width: 0.5em; +} + +::-webkit-scrollbar-track { + background: #ffffff; + background: var(--background); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-track { + background: #1a1818; + background: var(--background); + } +} + +::-webkit-scrollbar-thumb { + background: rgb(244, 244, 244); + background: var(--scrollbar-thumb); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb { + background: #4c4c4c; + background: var(--scrollbar-thumb); + } +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb { + background: #4c4c4c; + background: var(--scrollbar-thumb); + } +} + +::-webkit-scrollbar-thumb:hover { + background: #e5e5e5; + background: var(--scrollbar-thumb-hover); +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb:hover { + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); + } +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb:hover { + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); + } +} + +::-moz-selection { + background-color: rgba(24, 24, 24, 0.08); + background-color: var(--selection); + color: #1a1818; + color: var(--text-bright); +} + +::selection { + background-color: rgba(24, 24, 24, 0.08); + background-color: var(--selection); + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + ::-moz-selection { + color: #fff; + color: var(--text-bright); + } + + ::selection { + color: #fff; + color: var(--text-bright); + } +} + +@media (prefers-color-scheme: dark) { + ::-moz-selection { + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); + } + + ::selection { + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); + } +} + +details { + display: flex; + flex-direction: column; + align-items: flex-start; + background-color: #f5f5f5; + background-color: var(--background-alt); + padding: 0.5em 0.5em 0; + margin: 1em 0; + border-radius: 0.25em; + overflow: hidden; +} + +@media (prefers-color-scheme: dark) { + details { + background-color: #322e2e; + background-color: var(--background-alt); + } +} + +details[open] { + padding: 0.5em; +} + +details > :last-child { + margin-bottom: 0; +} + +details[open] summary { + margin-bottom: 0.5em; +} + +summary { + display: list-item; + background-color: #ffffff; + background-color: var(--background); + padding: 0.5em; + margin: -0.5em -0.5em 0; + cursor: pointer; + outline: none; +} + +@media (prefers-color-scheme: dark) { + summary { + background-color: #1a1818; + background-color: var(--background); + } +} + +summary:hover, +summary:focus { + text-decoration: underline; +} + +details > :not(summary) { + margin-top: 0; +} + +summary::-webkit-details-marker { + color: #1a1818; + color: var(--text-main); +} + +@media (prefers-color-scheme: dark) { + summary::-webkit-details-marker { + color: #ffffff; + color: var(--text-main); + } +} + +dialog { + background-color: #f5f5f5; + background-color: var(--background-alt); + color: #1a1818; + color: var(--text-main); + border: none; + border-radius: 0.25em; + border-color: #e5e5e5; + border-color: var(--border); + padding: 0.5em 1.5em; +} + +@media (prefers-color-scheme: dark) { + dialog { + border-color: #333333; + border-color: var(--border); + } +} + +@media (prefers-color-scheme: dark) { + dialog { + color: #ffffff; + color: var(--text-main); + } +} + +@media (prefers-color-scheme: dark) { + dialog { + background-color: #322e2e; + background-color: var(--background-alt); + } +} + +dialog > header:first-child { + background-color: #ffffff; + background-color: var(--background); + border-radius: 0.25em 0.25em 0 0; + margin: -0.5em -1.5em 0.5em; + padding: 0.5em; + text-align: center; +} + +@media (prefers-color-scheme: dark) { + dialog > header:first-child { + background-color: #1a1818; + background-color: var(--background); + } +} + +dialog::-webkit-backdrop { + background: #0000009c; + -webkit-backdrop-filter: blur(0.25em); + backdrop-filter: blur(0.25em); +} + +dialog::backdrop { + background: #0000009c; + -webkit-backdrop-filter: blur(0.25em); + backdrop-filter: blur(0.25em); +} + +footer { + position: fixed; + bottom: 1em; + right: 1em; + background: var(--background); + font-size: 0.75em; + padding: 0.5em 0.75em; + border-radius: 0.5em; + border: 0.125em solid var(--border); + box-shadow: 0 0.125em 0.25em var(--selection), 0 0 1em var(--selection); +} + +@media screen and (min-width: 960px) { + footer { + bottom: 2em; + right: 2em; + } +} + +footer a { + display: flex; + gap: 0.5em; + align-items: center; + text-decoration: none; + color: var(--text-main); +} + +@media (prefers-color-scheme: dark) { + footer { + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); + } +} + +body > footer { + margin-top: 2em; +} + +@media print { + body, + pre, + code, + summary, + details, + button, + input, + textarea { + background-color: #fff; + } + + button, + input, + textarea { + border: 0.125em solid #000; + } + + body, + h1, + h2, + h3, + h4, + h5, + h6, + pre, + code, + button, + input, + textarea, + footer, + summary, + strong { + color: #000; + } + + summary::marker { + color: #000; + } + + summary::-webkit-details-marker { + color: #000; + } + + tbody tr:nth-child(even) { + background-color: #f2f2f2; + } + + a { + color: #00f; + text-decoration: underline; + } +} + +.cover { + border-radius: 1em; + margin-bottom: 1em; +} + +.prelude { + font-size: 0.75em; + display: flex; + flex-direction: column; + gap: 0.4em; + border-bottom: 0.125em solid var(--border); + padding-bottom: 1em; + margin-bottom: 1em; +} + +body.chat .prelude { + padding: 1em; + flex-direction: row-reverse; + justify-content: space-between; + align-items: center; +} + +@media screen and (min-width: 960px) { + body.chat .prelude { + margin: 0; + position: fixed; + bottom: 2em; + left: 2em; + border: 0; + background: var(--background); + font-size: 0.75em; + padding: 0.5em 0.75em; + border-radius: 0.5em; + gap: 1em; + } +} + +.prelude .author { + display: flex; + align-items: center; + gap: 0.5em; + font-weight: 500; + color: var(--text-muted); +} + +.prelude .author .avatar { + border-radius: 0.25em; + overflow: hidden; +} + +.prelude .author .avatar img { + width: 1.5625em; + height: 1.5625em; + object-fit: cover; +} + +.prelude time { + color: var(--text-muted); + padding: 0; +} + +article p:empty { + display: none; +} + +article img { + max-width: 100%; + border-radius: 0.5em; +} diff --git a/desk/app/expose/style/shared.css b/desk/app/expose/style/shared.css new file mode 100644 index 0000000000..ff05f53a73 --- /dev/null +++ b/desk/app/expose/style/shared.css @@ -0,0 +1 @@ +/* TODO use me */ diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css new file mode 100644 index 0000000000..9fa2b522c3 --- /dev/null +++ b/desk/app/expose/style/widget.css @@ -0,0 +1,15 @@ +/* TODO + prefer scoping rules to just elements under #groups--expose-all, + otherwise style may affect other widgets on the profile + (alternatively do inline style attributes) +*/ +#groups--expose-all { + border: 1px solid red; +} +#groups--expose-all a { + display: block; + margin: 1em; +} +#groups--expose-all .exposed { + border: 1px solid black; +} From d351ebdb28c4d88425c868438d64326758bb237c Mon Sep 17 00:00:00 2001 From: fang Date: Wed, 18 Sep 2024 21:36:11 +0200 Subject: [PATCH 078/157] expose: refresh entire cache on-load And defer this to a separate event, because rendering scries, which is unsafe during load. We make sure to update the cache entries for all posts, because style files might've changed that impact their pages. ...This may actually be somewhat sluggish in cases where you have a lot of content exposed. We should consider just purging the cache for individual pages instead, and only re-rendering and caching those on-demand. --- desk/app/expose.hoon | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index be78e7023a..a7442f4387 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -429,9 +429,10 @@ |= ole=vase ^- (quip card _this) =. state !<(state-0 ole) - ::TODO defer, because it scries - ::TODO should update cache entries, styling/content might've changed - [(update-widget:e [our now]:bowl open) this] + :_ this + :: we must defer refreshing the cache because rendering scries + :: + [%pass /refresh %arvo %b %wait now.bowl]~ :: ++ on-poke |= [=mark =vase] @@ -521,6 +522,31 @@ ?+ wire !! [%eyre %connect ~] [~ this] ::TODO print if not successful + :: + [%refresh ~] + :_ this + :- %+ store:hutils '/expose/style/shared.css' + =/ bod=(unit octs) + `(as-octs:mimes:html style-shared) + `[| %payload [200 ['content-type' 'text/css'] ~] bod] + %+ weld + (update-widget:e [our now]:bowl open) + %+ murn ~(tap in open) + |= ref=cite:c + ^- (unit card) ::TODO or should this remove from cache also? + ::TODO maybe find a way to dedupe with logic in %show and %handle-http-req + ::TODO reconsider. if we just remove the cache entry, we'll re-render + :: on-demand instead of all-at-once, which may be slow. + =/ msg=(unit [=nest:g:c =post:d]) + (post-from-cite:e our.bowl now.bowl ref) + ?~ msg ~ + =/ pag=(unit manx) + ((render-post:e [our now]:bowl) u.msg) + ?~ pag ~ + %- some + %+ store:hutils + (cat 3 '/expose' (spat (print:c ref))) + `[| %payload (paint:hutils %page u.pag)] == :: ++ on-peek From f9f7ceee8369db4bef7a098d6ba2dcd3f43b35c7 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Wed, 18 Sep 2024 14:44:44 -0500 Subject: [PATCH 079/157] Make whole list scrollable in root, remove unnecessary wrapper, remove extra padding --- .../components/AddChats/CreateGroupWidget.tsx | 22 ++++---- packages/ui/src/components/AddGroupSheet.tsx | 56 +++++++++---------- packages/ui/src/components/ContactBook.tsx | 2 +- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx index 05e632af17..a3a669d539 100644 --- a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx +++ b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx @@ -60,18 +60,16 @@ export function CreateGroupWidget(props: { New Group - - - - - + + + New Message - - - goToScreen('InviteUsers'), - }} - /> - goToFindGroups(), - }} - /> - - } - /> - + + goToScreen('InviteUsers'), + }} + /> + goToFindGroups(), + }} + /> + + } + /> ); diff --git a/packages/ui/src/components/ContactBook.tsx b/packages/ui/src/components/ContactBook.tsx index a2d90ebf00..129ad3ddc1 100644 --- a/packages/ui/src/components/ContactBook.tsx +++ b/packages/ui/src/components/ContactBook.tsx @@ -149,8 +149,8 @@ export function ContactBook({ ) : ( - {quickActions && !showSearchResults && } Date: Wed, 18 Sep 2024 14:53:38 -0500 Subject: [PATCH 080/157] combine useGroupActions into single hook, break out navigation into two new hooks --- packages/app/hooks/useGroupActions.native.tsx | 33 ------------------- packages/app/hooks/useGroupActions.tsx | 11 ++++--- .../app/hooks/useGroupNavigation.native.ts | 26 +++++++++++++++ packages/app/hooks/useGroupNavigation.ts | 23 +++++++++++++ 4 files changed, 55 insertions(+), 38 deletions(-) delete mode 100644 packages/app/hooks/useGroupActions.native.tsx create mode 100644 packages/app/hooks/useGroupNavigation.native.ts create mode 100644 packages/app/hooks/useGroupNavigation.ts diff --git a/packages/app/hooks/useGroupActions.native.tsx b/packages/app/hooks/useGroupActions.native.tsx deleted file mode 100644 index 43b6a2ba56..0000000000 --- a/packages/app/hooks/useGroupActions.native.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useNavigation } from '@react-navigation/native'; -import * as db from '@tloncorp/shared/dist/db'; -import { GroupPreviewAction } from '@tloncorp/ui'; -import { useCallback } from 'react'; - -export const useGroupActions = () => { - const navigation = useNavigation< - // @ts-expect-error - TODO: pass navigation handlers into context - NativeStackNavigationProp - >(); - - const performGroupAction = useCallback( - async (action: GroupPreviewAction, updatedGroup: db.Group) => { - if (action === 'goTo' && updatedGroup.lastPost?.channelId) { - const channel = await db.getChannel({ - id: updatedGroup.lastPost.channelId, - }); - if (channel) { - navigation.navigate('Channel', { channel }); - } - } - - if (action === 'joined') { - navigation.navigate('ChatList'); - } - }, - [navigation] - ); - - return { - performGroupAction, - }; -}; diff --git a/packages/app/hooks/useGroupActions.tsx b/packages/app/hooks/useGroupActions.tsx index d2194a49fe..9a5b2ffdbd 100644 --- a/packages/app/hooks/useGroupActions.tsx +++ b/packages/app/hooks/useGroupActions.tsx @@ -1,10 +1,11 @@ import * as db from '@tloncorp/shared/dist/db'; import { GroupPreviewAction } from '@tloncorp/ui'; import { useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; + +import { useGroupNavigation } from './useGroupNavigation'; export const useGroupActions = () => { - const navigate = useNavigate(); + const { goToChannel, goToHome } = useGroupNavigation(); const performGroupAction = useCallback( async (action: GroupPreviewAction, updatedGroup: db.Group) => { @@ -13,15 +14,15 @@ export const useGroupActions = () => { id: updatedGroup.lastPost.channelId, }); if (channel) { - navigate('/group/' + channel.groupId + '/channel/' + channel.id); + goToChannel(channel); } } if (action === 'joined') { - navigate('/'); + goToHome(); } }, - [navigate] + [goToChannel, goToHome] ); return { diff --git a/packages/app/hooks/useGroupNavigation.native.ts b/packages/app/hooks/useGroupNavigation.native.ts new file mode 100644 index 0000000000..f44cef9cc0 --- /dev/null +++ b/packages/app/hooks/useGroupNavigation.native.ts @@ -0,0 +1,26 @@ +import { useNavigation } from '@react-navigation/native'; +import * as db from '@tloncorp/shared/dist/db'; +import { useCallback } from 'react'; + +export const useGroupNavigation = () => { + const navigation = useNavigation< + // @ts-expect-error - TODO: pass navigation handlers into context + NativeStackNavigationProp + >(); + + const goToChannel = useCallback( + async (channel: db.Channel) => { + navigation.navigate('Channel', { channel }); + }, + [navigation] + ); + + const goToHome = useCallback(() => { + navigation.navigate('ChatList'); + }, [navigation]); + + return { + goToChannel, + goToHome, + }; +}; diff --git a/packages/app/hooks/useGroupNavigation.ts b/packages/app/hooks/useGroupNavigation.ts new file mode 100644 index 0000000000..ebea002e16 --- /dev/null +++ b/packages/app/hooks/useGroupNavigation.ts @@ -0,0 +1,23 @@ +import * as db from '@tloncorp/shared/dist/db'; +import { useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; + +export const useGroupNavigation = () => { + const navigate = useNavigate(); + + const goToChannel = useCallback( + async (channel: db.Channel) => { + navigate(`/group/${channel.groupId}/channel/${channel.id}`); + }, + [navigate] + ); + + const goToHome = useCallback(() => { + navigate('/'); + }, [navigate]); + + return { + goToChannel, + goToHome, + }; +}; From c0ca969f9ced61eca7967b836e6889924daf81d2 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 18 Sep 2024 15:26:35 -0500 Subject: [PATCH 081/157] lure: if we encounter old link, regenerate --- packages/shared/src/store/lure.ts | 27 ++++++++++++++----- .../ui/src/components/InviteUsersWidget.tsx | 19 ++++++++----- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index 4128b4486d..0a7c81c4ff 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; import produce from 'immer'; -import { useCallback, useMemo, useRef } from 'react'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; import create from 'zustand'; import { getCurrentUserId, poke, scry, subscribeOnce } from '../api/urbit'; @@ -315,11 +315,12 @@ export function useLureLinkStatus({ branchDomain: string; branchKey: string; }) { - const { supported, fetched, enabled, url, deepLinkUrl, toggle } = useLure({ - flag, - branchDomain, - branchKey, - }); + const { supported, fetched, enabled, url, deepLinkUrl, toggle, describe } = + useLure({ + flag, + branchDomain, + branchKey, + }); const { good, checked } = useLureLinkChecked(url, !!enabled); lureLogger.log('useLureLinkStatus', { @@ -342,6 +343,10 @@ export function useLureLinkStatus({ return 'disabled'; } + if (url && checkOldLureToken(url)) { + return 'stale'; + } + if (!url || !checkLureToken(url) || !fetched || !checked) { lureLogger.log('loading', fetched, checked, url); return 'loading'; @@ -356,7 +361,7 @@ export function useLureLinkStatus({ lureLogger.log('url', url, 'deepLinkUrl', deepLinkUrl, 'status', status); - return { status, shareUrl: deepLinkUrl ?? url, toggle }; + return { status, shareUrl: deepLinkUrl ?? url, toggle, describe }; } // hack: we get an intermediate state while generating lure links where @@ -367,3 +372,11 @@ function checkLureToken(url: string | undefined) { const token = url.split('/').pop(); return token && token.startsWith('0v'); } + +function checkOldLureToken(url: string | undefined) { + if (!url) return false; + const parts = url.split('/'); + const token = parts.pop(); + const ship = parts.pop(); + return ship && token && ship.startsWith('~'); +} diff --git a/packages/ui/src/components/InviteUsersWidget.tsx b/packages/ui/src/components/InviteUsersWidget.tsx index fca07ff642..893ee34164 100644 --- a/packages/ui/src/components/InviteUsersWidget.tsx +++ b/packages/ui/src/components/InviteUsersWidget.tsx @@ -21,7 +21,7 @@ const InviteUsersWidgetComponent = ({ const currentUser = useCurrentUserId(); const branchDomain = useBranchDomain(); const branchKey = useBranchKey(); - const { status, shareUrl, toggle } = store.useLureLinkStatus({ + const { status, shareUrl, toggle, describe } = store.useLureLinkStatus({ flag: group.id, branchDomain: branchDomain, branchKey: branchKey, @@ -77,17 +77,22 @@ const InviteUsersWidgetComponent = ({ ]); useEffect(() => { + const meta = { + title: group.title ?? '', + description: group.description ?? '', + cover: group.coverImage ?? '', + image: group.iconImage ?? '', + }; + const toggleLink = async () => { - await toggle({ - title: group.title ?? '', - description: group.description ?? '', - cover: group.coverImage ?? '', - image: group.iconImage ?? '', - }); + await toggle(meta); }; if (status === 'disabled' && currentUserIsAdmin) { toggleLink(); } + if (status === 'stale') { + describe(meta); + } }, [group, branchDomain, branchKey, toggle, status, currentUserIsAdmin]); const handleSkipButtonPress = useCallback(() => { From 3bbd92822b900d45ec4e289714d44e5e7eb96b7a Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 18 Sep 2024 15:31:00 -0500 Subject: [PATCH 082/157] lure: do the same for web --- apps/tlon-web/src/state/lure/lure.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/tlon-web/src/state/lure/lure.ts b/apps/tlon-web/src/state/lure/lure.ts index 1b30f9c946..9c4e36bf32 100644 --- a/apps/tlon-web/src/state/lure/lure.ts +++ b/apps/tlon-web/src/state/lure/lure.ts @@ -236,7 +236,15 @@ export function useLure(flag: string, disableLoading = false) { ); useEffect(() => { - if (lure.enabled && !lure.url && group?.meta) { + if (!group?.meta) { + return; + } + + if (lure.enabled && !lure.url) { + describe(group.meta); + } + + if (lure.enabled && lure.url && checkOldLureToken(lure.url)) { describe(group.meta); } }, [group]); @@ -293,6 +301,10 @@ export function useLureLinkStatus(flag: string) { return 'disabled'; } + if (url && checkOldLureToken(url)) { + return 'stale'; + } + if (!url || !fetched || !checked) { return 'loading'; } @@ -306,3 +318,11 @@ export function useLureLinkStatus(flag: string) { return { status, shareUrl: deepLinkUrl ?? url, toggle }; } + +function checkOldLureToken(url: string | undefined) { + if (!url) return false; + const parts = url.split('/'); + const token = parts.pop(); + const ship = parts.pop(); + return ship && token && ship.startsWith('~'); +} From 53bc8662c5651b6df7668fff3d798129714a5dc5 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Wed, 18 Sep 2024 17:10:23 -0400 Subject: [PATCH 083/157] expose: better selectors, division of shared/page concerns --- desk/app/expose.hoon | 34 +- desk/app/expose/style/page.css | 1707 +----------------------------- desk/app/expose/style/shared.css | 1616 +++++++++++++++++++++++++++- 3 files changed, 1678 insertions(+), 1679 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index a7442f4387..324e85d667 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -162,13 +162,13 @@ ;html ;+ hes ;body(class tag) - ;header - ;* pre - == - ;article + ;article.expose-content + ;header + ;* pre + == ;* bod == - ;+ footer + ;+ badge == ;+ time-script-node:r == @@ -179,8 +179,6 @@ ;title:"{title}" ;link(rel "stylesheet", href "/expose/style/shared.css"); ;style:"{(trip style-page)}" - ;link(rel "preconnect", href "https://rsms.me"); - ;link(rel "stylesheet", href "https://rsms.me/inter/inter.css"); :: ;meta(charset "utf-8"); ;meta(name "viewport", content "width=device-width, initial-scale=1"); @@ -213,8 +211,8 @@ ;meta(property "og:article:author:first_name", content (trip nickname.u.aco)); == :: - ++ footer - ;footer + ++ badge + ;div.tlon-badge ;a(href "https://tlon.io") ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); ;span @@ -225,22 +223,18 @@ :: ++ chat-prelude ^- manx - ;div.prelude - ;div.published - ;+ (render-datetime:r sent.msg) - == + ;div.author-row ;+ (author-node:r [our now] author.msg) + ;+ (render-datetime:r sent.msg) == :: ++ diary-prelude |= title=tape ^- marl :~ ;h1:"{title}" - ;div.prelude - ;div.published - ;+ (render-datetime:r sent.msg) - == + ;div.author-row ;+ (author-node:r [our now] author.msg) + ;+ (render-datetime:r sent.msg) == == :: @@ -250,11 +244,9 @@ =- ?: =("" title) [-]~ :- ;h1:"{title}" [-]~ - ;div.prelude - ;div.published - ;+ (render-datetime:r sent.msg) - == + ;div.author-row ;+ (author-node:r [our now] author.msg) + ;+ (render-datetime:r sent.msg) == -- :: diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index d4889d7b80..64acbb67b5 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -1,1552 +1,78 @@ -:root { - --background-body: #ffffff; - --background: #ffffff; - --background-alt: #f5f5f5; - --selection: rgba(24, 24, 24, 0.08); - --text-main: #1a1818; - --text-bright: #1a1818; - --text-muted: #666666; - --links: #008eff; - --focus: #008eff; - --border: #e5e5e5; - --code: #1a1818; - --animation-duration: 0.1s; - --button-base: #f5f5f5; - --button-hover: #e5e5e5; - --scrollbar-thumb: rgb(244, 244, 244); - --scrollbar-thumb-hover: var(--button-hover); - --form-placeholder: #999999; - --form-text: #1a1818; - --variable: #2ad546; - --highlight: #fade7a; - --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); -} - -@media (prefers-color-scheme: dark) { - :root { - --background-body: #1a1818; - --background: #1a1818; - --background-alt: #322e2e; - --selection: rgba(255, 255, 255, 0.08); - --text-main: #ffffff; - --text-bright: #fff; - --text-muted: #b3b3b3; - --links: #008eff; - --focus: #008eff; - --border: #333333; - --code: #ffffff; - --animation-duration: 0.1s; - --button-base: #322e2e; - --button-hover: #4c4c4c; - --scrollbar-thumb: var(--button-hover); - --scrollbar-thumb-hover: rgb(0, 0, 0); - --form-placeholder: #808080; - --form-text: #fff; - --variable: #2ad546; - --highlight: #fade7a; - --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); - } -} - -html { - scrollbar-color: rgb(244, 244, 244) #ffffff; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - scrollbar-width: thin; -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -body { - font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", - "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", - "Helvetica Neue", "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", - sans-serif; - font-size: 16px; - font-weight: 400; - line-height: 1.6; - margin: 1em 1em; - padding: 0; - word-wrap: break-word; - color: #1a1818; - color: var(--text-main); - background: #ffffff; - background: var(--background-body); - text-rendering: optimizeLegibility; -} - -@media (prefers-color-scheme: dark) { - body { - background: #1a1818; - background: var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - body { - color: #ffffff; - color: var(--text-main); - } -} - -body.chat { - margin: 0; -} - -body.chat article { - margin: 0 1em; -} - -body.chat header > h1 { - padding: 1rem 1rem 0; -} - -@media screen and (min-width: 40rem) { - body { - font-size: 20px; - line-height: 1.8; - } - body:not(.chat) { - max-width: 40rem; - margin: 1em auto; - padding: 0 1em; - } -} - -@media screen and (min-width: 960px) { - body:not(.chat) { - margin: 2em auto 2em 2em; - padding: 0; - } - body.chat { - max-width: 40rem; - margin: 0 auto; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - overflow: auto; - } -} - -button { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; -} - -@media (prefers-color-scheme: dark) { - button { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } -} - -input { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; -} - -@media (prefers-color-scheme: dark) { - input { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } -} - -textarea { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; -} - -@media (prefers-color-scheme: dark) { - textarea { - transition: background-color 0.1s linear, border-color 0.1s linear, - color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; - transition: background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } -} - -h1 { - margin-top: 0; - font-size: 2em; - font-weight: 400; - letter-spacing: -0.025em; - line-height: 1; - margin-top: 0; - margin-bottom: 0.6em; -} - -h2, -h3, -h4, -h5, -h6 { - margin-bottom: 0.6em; - margin-top: 1.25em; - font-size: 1em; -} - -h1 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h1 { - color: #fff; - color: var(--text-bright); - } -} - -h2 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h2 { - color: #fff; - color: var(--text-bright); - } -} - -h3 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h3 { - color: #fff; - color: var(--text-bright); - } -} - -h4 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h4 { - color: #fff; - color: var(--text-bright); - } -} - -h5 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h5 { - color: #fff; - color: var(--text-bright); - } -} - -h6 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h6 { - color: #fff; - color: var(--text-bright); - } -} - -strong { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - strong { - color: #fff; - color: var(--text-bright); - } -} - -h2, -h3, -h4, -h5, -h6, -b, -strong, -th { - font-weight: 600; -} - -q::before { - content: none; -} - -q::after { - content: none; -} - -blockquote { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - margin: 1.5em 0; - padding: 0.5em 1em; - font-style: italic; -} - -@media (prefers-color-scheme: dark) { - blockquote { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - } -} - -q { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - margin: 1.5em 0; - padding: 0.5em 1em; - font-style: italic; -} - -@media (prefers-color-scheme: dark) { - q { - border-left: 0.25em solid #008eff; - border-left: 0.25em solid var(--focus); - } -} - -blockquote > footer { - font-style: normal; - border: 0; -} - -blockquote cite { - font-style: normal; -} - -address { - font-style: normal; -} - -a[href^="mailto\:"]::before { - content: "📧 "; -} - -a[href^="tel\:"]::before { - content: "📞 "; -} - -a[href^="sms\:"]::before { - content: "💬 "; -} - -mark { - background-color: #fade7a; - background-color: var(--highlight); - border-radius: 0.125em; - padding: 0 0.125em 0 0.125em; - color: #000; -} - -@media (prefers-color-scheme: dark) { - mark { - background-color: #fade7a; - background-color: var(--highlight); - } -} - -a > code, -a > strong { - color: inherit; -} - -button, -select, -input[type="submit"], -input[type="reset"], -input[type="button"], -input[type="checkbox"], -input[type="range"], -input[type="radio"] { - cursor: pointer; -} - -input, -select { - display: block; -} - -[type="checkbox"], -[type="radio"] { - display: initial; -} - -input { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; -} - -@media (prefers-color-scheme: dark) { - input { - background-color: #1a1818; - background-color: var(--background); - } -} - -@media (prefers-color-scheme: dark) { - input { - color: #fff; - color: var(--form-text); - } -} - -button { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; -} - -@media (prefers-color-scheme: dark) { - button { - background-color: #1a1818; - background-color: var(--background); - } -} - -@media (prefers-color-scheme: dark) { - button { - color: #fff; - color: var(--form-text); - } -} - -textarea { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; -} - -@media (prefers-color-scheme: dark) { - textarea { - background-color: #1a1818; - background-color: var(--background); - } -} - -@media (prefers-color-scheme: dark) { - textarea { - color: #fff; - color: var(--form-text); - } -} - -select { - color: #1a1818; - color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); - font-family: inherit; - font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; - outline: none; -} - -@media (prefers-color-scheme: dark) { - select { - background-color: #1a1818; - background-color: var(--background); - } -} - -@media (prefers-color-scheme: dark) { - select { - color: #fff; - color: var(--form-text); - } -} - -button { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; -} - -@media (prefers-color-scheme: dark) { - button { - background-color: #322e2e; - background-color: var(--button-base); - } -} - -input[type="submit"] { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; -} - -@media (prefers-color-scheme: dark) { - input[type="submit"] { - background-color: #322e2e; - background-color: var(--button-base); - } -} - -input[type="reset"] { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; -} - -@media (prefers-color-scheme: dark) { - input[type="reset"] { - background-color: #322e2e; - background-color: var(--button-base); - } -} - -input[type="button"] { - background-color: #f5f5f5; - background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; -} - -@media (prefers-color-scheme: dark) { - input[type="button"] { - background-color: #322e2e; - background-color: var(--button-base); - } -} - -button:hover { - background: #e5e5e5; - background: var(--button-hover); -} - -@media (prefers-color-scheme: dark) { - button:hover { - background: #4c4c4c; - background: var(--button-hover); - } -} - -input[type="submit"]:hover { - background: #e5e5e5; - background: var(--button-hover); -} - -@media (prefers-color-scheme: dark) { - input[type="submit"]:hover { - background: #4c4c4c; - background: var(--button-hover); - } -} - -input[type="reset"]:hover { - background: #e5e5e5; - background: var(--button-hover); -} - -@media (prefers-color-scheme: dark) { - input[type="reset"]:hover { - background: #4c4c4c; - background: var(--button-hover); - } -} - -input[type="button"]:hover { - background: #e5e5e5; - background: var(--button-hover); -} - -@media (prefers-color-scheme: dark) { - input[type="button"]:hover { - background: #4c4c4c; - background: var(--button-hover); - } -} - -input[type="color"] { - min-height: 2rem; - padding: 0.4em; - cursor: pointer; -} - -input[type="checkbox"], -input[type="radio"] { - height: 1em; - width: 1em; -} - -input[type="radio"] { - border-radius: 100%; -} - -input { - vertical-align: top; -} - -label { - vertical-align: middle; - margin-bottom: 0.25em; - display: inline-block; -} - -input:not([type="checkbox"]):not([type="radio"]), -input[type="range"], -select, -button, -textarea { - -webkit-appearance: none; -} - -textarea { - display: block; - margin-right: 0; - box-sizing: border-box; - resize: vertical; -} - -textarea:not([cols]) { - width: 100%; -} - -textarea:not([rows]) { - min-height: 2em; - height: 12em; -} - -select { - background: #ffffff - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / 10.125em - no-repeat; - padding-right: 1.75em; -} - -@media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } -} - -@media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } -} - -@media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } -} - -@media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } -} - -select::-ms-expand { - display: none; -} - -select[multiple] { - padding-right: 0.5em; - background-image: none; - overflow-y: auto; -} - -input:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); -} - -@media (prefers-color-scheme: dark) { - input:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } -} - -select:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); -} - -@media (prefers-color-scheme: dark) { - select:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } -} - -button:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); -} - -@media (prefers-color-scheme: dark) { - button:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } -} - -textarea:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); -} - -@media (prefers-color-scheme: dark) { - textarea:focus { - box-shadow: 0 0 0 0.125em #008eff; - box-shadow: 0 0 0 0.125em var(--focus); - } -} - -input[type="checkbox"]:active, -input[type="radio"]:active, -input[type="submit"]:active, -input[type="reset"]:active, -input[type="button"]:active, -input[type="range"]:active, -button:active { - transform: translateY(0.125em); -} - -input:disabled, -select:disabled, -button:disabled, -textarea:disabled { - cursor: not-allowed; - opacity: 0.5; -} - -::-moz-placeholder { - color: #999999; - color: var(--form-placeholder); -} - -:-ms-input-placeholder { - color: #999999; - color: var(--form-placeholder); -} - -::-ms-input-placeholder { - color: #999999; - color: var(--form-placeholder); -} - -::placeholder { - color: #999999; - color: var(--form-placeholder); -} - -@media (prefers-color-scheme: dark) { - ::-moz-placeholder { - color: #808080; - color: var(--form-placeholder); - } - - :-ms-input-placeholder { - color: #808080; - color: var(--form-placeholder); - } - - ::-ms-input-placeholder { - color: #808080; - color: var(--form-placeholder); - } - - ::placeholder { - color: #808080; - color: var(--form-placeholder); - } -} - -fieldset { - border: 0.125em #008eff solid; - border: 0.125em var(--focus) solid; - border-radius: 0.25em; - margin: 0; - margin-bottom: 10.125em; - padding: 0.5em; -} - -@media (prefers-color-scheme: dark) { - fieldset { - border: 0.125em #008eff solid; - border: 0.125em var(--focus) solid; - } -} - -legend { - font-size: 0.9em; - font-weight: 600; -} - -input[type="range"] { - margin: 0.5em 0; - padding: 0.5em 0; - background: transparent; -} - -input[type="range"]:focus { - outline: none; -} - -input[type="range"]::-webkit-slider-runnable-track { - width: 100%; - height: 0.5em; - -webkit-transition: 0.2s; - transition: 0.2s; - background: #ffffff; - background: var(--background); - border-radius: 0.25em; -} - -@media (prefers-color-scheme: dark) { - input[type="range"]::-webkit-slider-runnable-track { - background: #1a1818; - background: var(--background); - } -} - -input[type="range"]::-webkit-slider-thumb { - box-shadow: 0 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - height: 1em; - width: 1em; - border-radius: 50%; - background: #e5e5e5; - background: var(--border); - -webkit-appearance: none; - margin-top: -0.3em; -} - -@media (prefers-color-scheme: dark) { - input[type="range"]::-webkit-slider-thumb { - background: #333333; - background: var(--border); - } -} - -input[type="range"]:focus::-webkit-slider-runnable-track { - background: #ffffff; - background: var(--background); -} - -@media (prefers-color-scheme: dark) { - input[type="range"]:focus::-webkit-slider-runnable-track { - background: #1a1818; - background: var(--background); - } -} - -input[type="range"]::-moz-range-track { - width: 100%; - height: 1em; - -moz-transition: 0.2s; - transition: 0.2s; - background: #ffffff; - background: var(--background); - border-radius: 0.25em; -} - -@media (prefers-color-scheme: dark) { - input[type="range"]::-moz-range-track { - background: #1a1818; - background: var(--background); - } -} - -input[type="range"]::-moz-range-thumb { - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - height: 1em; - width: 1em; - border-radius: 50%; - background: #e5e5e5; - background: var(--border); -} - -@media (prefers-color-scheme: dark) { - input[type="range"]::-moz-range-thumb { - background: #333333; - background: var(--border); - } -} - -input[type="range"]::-ms-track { - width: 100%; - height: 0.5em; - background: transparent; - border-color: transparent; - border-width: 10.25em 0; - color: transparent; -} - -input[type="range"]::-ms-fill-lower { - background: #ffffff; - background: var(--background); - border: 0.0.125em solid #010101; - border-radius: 0.25em; - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; -} - -@media (prefers-color-scheme: dark) { - input[type="range"]::-ms-fill-lower { - background: #1a1818; - background: var(--background); - } -} - -input[type="range"]::-ms-fill-upper { - background: #ffffff; - background: var(--background); - border: 0.0.125em solid #010101; - border-radius: 0.25em; - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; -} - -@media (prefers-color-scheme: dark) { - input[type="range"]::-ms-fill-upper { - background: #1a1818; - background: var(--background); - } -} - -input[type="range"]::-ms-thumb { - box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; - border: 0.125em solid #000; - height: 1em; - width: 1em; - border-radius: 50%; - background: #e5e5e5; - background: var(--border); -} - -@media (prefers-color-scheme: dark) { - input[type="range"]::-ms-thumb { - background: #333333; - background: var(--border); - } -} - -input[type="range"]:focus::-ms-fill-lower { - background: #ffffff; - background: var(--background); -} - -@media (prefers-color-scheme: dark) { - input[type="range"]:focus::-ms-fill-lower { - background: #1a1818; - background: var(--background); - } -} - -input[type="range"]:focus::-ms-fill-upper { - background: #ffffff; - background: var(--background); -} - -@media (prefers-color-scheme: dark) { - input[type="range"]:focus::-ms-fill-upper { - background: #1a1818; - background: var(--background); - } -} - -a { - text-decoration: underline; - color: #008eff; - color: var(--links); -} - -@media (prefers-color-scheme: dark) { - a { - color: #008eff; - color: var(--links); - } -} - -a:hover { - text-decoration: underline; -} - -code { - background: #ffffff; - background: var(--background); - color: #1a1818; - color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; - font-size: 1em; -} - -@media (prefers-color-scheme: dark) { - code { - color: #ffffff; - color: var(--code); - } -} - -@media (prefers-color-scheme: dark) { - code { - background: #1a1818; - background: var(--background); - } -} - -samp { - background: #ffffff; - background: var(--background); - color: #1a1818; - color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; - font-size: 1em; -} - -@media (prefers-color-scheme: dark) { - samp { - color: #ffffff; - color: var(--code); - } -} - -@media (prefers-color-scheme: dark) { - samp { - background: #1a1818; - background: var(--background); - } -} - -time { - background: #ffffff; - background: var(--background); - color: #1a1818; - color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; - font-size: 1em; -} - -@media (prefers-color-scheme: dark) { - time { - color: #ffffff; - color: var(--code); - } -} - -@media (prefers-color-scheme: dark) { - time { - background: #1a1818; - background: var(--background); - } -} - -pre > code { - padding: 0.5em; - display: block; - overflow-x: auto; -} - -var { - color: #2ad546; - color: var(--variable); - font-style: normal; - font-family: monospace; -} - -@media (prefers-color-scheme: dark) { - var { - color: #2ad546; - color: var(--variable); +@media screen and (min-width: 960px) { + body.chat { + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + overflow: auto; } } -kbd { - background: #ffffff; +.expose-content { + margin: 0 auto; + max-width: 40em; + padding: 2em; + border-radius: 1.5em; background: var(--background); - border: 0.125em solid #e5e5e5; - border: 0.125em solid var(--border); - border-radius: 0.125em; - color: #1a1818; - color: var(--text-main); - padding: 0.125em 0.25em 0.125em 0.25em; -} - -@media (prefers-color-scheme: dark) { - kbd { - color: #ffffff; - color: var(--text-main); - } -} - -@media (prefers-color-scheme: dark) { - kbd { - border: 0.125em solid #333333; - border: 0.125em solid var(--border); - } } -@media (prefers-color-scheme: dark) { - kbd { - background: #1a1818; - background: var(--background); - } +.expose-content p:empty { + display: none; } -img, -video { +.expose-content img { max-width: 100%; - height: auto; -} - -hr { - border: none; - border-top: 0.125em solid #e5e5e5; - border-top: 0.125em solid var(--border); -} - -@media (prefers-color-scheme: dark) { - hr { - border-top: 0.125em solid #333333; - border-top: 0.125em solid var(--border); - } + border-radius: 0.5em; } -table { - border-collapse: collapse; - margin-bottom: 0.5em; - width: 100%; - table-layout: fixed; +.expose-content > p:last-child { + margin-bottom: 0; } -table caption { - text-align: left; +.cover { + border-radius: 1em; } -td, -th { - padding: 0.25em; - text-align: left; - vertical-align: top; - word-wrap: break-word; +.cover + h1 { + margin-top: 0.6em; } -thead { - border-bottom: 0.125em solid #e5e5e5; +.author-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 2em; + padding: 0 0 0.75em; border-bottom: 0.125em solid var(--border); } -@media (prefers-color-scheme: dark) { - thead { - border-bottom: 0.125em solid #333333; - border-bottom: 0.125em solid var(--border); - } -} - -tfoot { - border-top: 0.125em solid #e5e5e5; - border-top: 0.125em solid var(--border); -} - -@media (prefers-color-scheme: dark) { - tfoot { - border-top: 0.125em solid #333333; - border-top: 0.125em solid var(--border); - } -} - -tbody tr:nth-child(even) { - background-color: #ffffff; - background-color: var(--background); -} - -@media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) { - background-color: #1a1818; - background-color: var(--background); - } -} - -tbody tr:nth-child(even) button { - background-color: #f5f5f5; - background-color: var(--background-alt); -} - -@media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) button { - background-color: #322e2e; - background-color: var(--background-alt); - } -} - -tbody tr:nth-child(even) button:hover { - background-color: #ffffff; - background-color: var(--background-body); -} - -@media (prefers-color-scheme: dark) { - tbody tr:nth-child(even) button:hover { - background-color: #1a1818; - background-color: var(--background-body); - } -} - -::-webkit-scrollbar { - height: 0.5em; - width: 0.5em; -} - -::-webkit-scrollbar-track { - background: #ffffff; - background: var(--background); - border-radius: 0.25em; -} - -@media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-track { - background: #1a1818; - background: var(--background); - } -} - -::-webkit-scrollbar-thumb { - background: rgb(244, 244, 244); - background: var(--scrollbar-thumb); - border-radius: 0.25em; -} - -@media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb { - background: #4c4c4c; - background: var(--scrollbar-thumb); - } -} - -@media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb { - background: #4c4c4c; - background: var(--scrollbar-thumb); - } -} - -::-webkit-scrollbar-thumb:hover { - background: #e5e5e5; - background: var(--scrollbar-thumb-hover); -} - -@media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); - background: var(--scrollbar-thumb-hover); - } -} - -@media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); - background: var(--scrollbar-thumb-hover); - } -} - -::-moz-selection { - background-color: rgba(24, 24, 24, 0.08); - background-color: var(--selection); - color: #1a1818; - color: var(--text-bright); -} - -::selection { - background-color: rgba(24, 24, 24, 0.08); - background-color: var(--selection); - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - ::-moz-selection { - color: #fff; - color: var(--text-bright); - } - - ::selection { - color: #fff; - color: var(--text-bright); - } -} - -@media (prefers-color-scheme: dark) { - ::-moz-selection { - background-color: rgba(255, 255, 255, 0.08); - background-color: var(--selection); - } - - ::selection { - background-color: rgba(255, 255, 255, 0.08); - background-color: var(--selection); - } +.author-row time { + color: var(--text-muted); + background: transparent; + padding: 0; } -details { +.author { display: flex; - flex-direction: column; - align-items: flex-start; - background-color: #f5f5f5; - background-color: var(--background-alt); - padding: 0.5em 0.5em 0; - margin: 1em 0; - border-radius: 0.25em; - overflow: hidden; -} - -@media (prefers-color-scheme: dark) { - details { - background-color: #322e2e; - background-color: var(--background-alt); - } -} - -details[open] { - padding: 0.5em; -} - -details > :last-child { - margin-bottom: 0; -} - -details[open] summary { - margin-bottom: 0.5em; -} - -summary { - display: list-item; - background-color: #ffffff; - background-color: var(--background); - padding: 0.5em; - margin: -0.5em -0.5em 0; - cursor: pointer; - outline: none; -} - -@media (prefers-color-scheme: dark) { - summary { - background-color: #1a1818; - background-color: var(--background); - } -} - -summary:hover, -summary:focus { - text-decoration: underline; -} - -details > :not(summary) { - margin-top: 0; -} - -summary::-webkit-details-marker { - color: #1a1818; - color: var(--text-main); -} - -@media (prefers-color-scheme: dark) { - summary::-webkit-details-marker { - color: #ffffff; - color: var(--text-main); - } + align-items: center; + gap: 0.5em; + font-weight: 500; + color: var(--text-muted); } -dialog { - background-color: #f5f5f5; - background-color: var(--background-alt); - color: #1a1818; - color: var(--text-main); - border: none; +.author .avatar { border-radius: 0.25em; - border-color: #e5e5e5; - border-color: var(--border); - padding: 0.5em 1.5em; -} - -@media (prefers-color-scheme: dark) { - dialog { - border-color: #333333; - border-color: var(--border); - } -} - -@media (prefers-color-scheme: dark) { - dialog { - color: #ffffff; - color: var(--text-main); - } -} - -@media (prefers-color-scheme: dark) { - dialog { - background-color: #322e2e; - background-color: var(--background-alt); - } -} - -dialog > header:first-child { - background-color: #ffffff; - background-color: var(--background); - border-radius: 0.25em 0.25em 0 0; - margin: -0.5em -1.5em 0.5em; - padding: 0.5em; - text-align: center; -} - -@media (prefers-color-scheme: dark) { - dialog > header:first-child { - background-color: #1a1818; - background-color: var(--background); - } -} - -dialog::-webkit-backdrop { - background: #0000009c; - -webkit-backdrop-filter: blur(0.25em); - backdrop-filter: blur(0.25em); + overflow: hidden; } -dialog::backdrop { - background: #0000009c; - -webkit-backdrop-filter: blur(0.25em); - backdrop-filter: blur(0.25em); +.author .avatar img { + width: 1.5625em; + height: 1.5625em; + object-fit: cover; } -footer { +.tlon-badge { position: fixed; bottom: 1em; right: 1em; @@ -1555,155 +81,22 @@ footer { padding: 0.5em 0.75em; border-radius: 0.5em; border: 0.125em solid var(--border); - box-shadow: 0 0.125em 0.25em var(--selection), 0 0 1em var(--selection); + box-shadow: + 0 0.125em 0.25em var(--selection), + 0 0 1em var(--selection); } @media screen and (min-width: 960px) { - footer { + .tlon-badge { bottom: 2em; right: 2em; } } -footer a { +.tlon-badge a { display: flex; gap: 0.5em; align-items: center; text-decoration: none; color: var(--text-main); } - -@media (prefers-color-scheme: dark) { - footer { - border-top: 0.125em solid #333333; - border-top: 0.125em solid var(--border); - } -} - -body > footer { - margin-top: 2em; -} - -@media print { - body, - pre, - code, - summary, - details, - button, - input, - textarea { - background-color: #fff; - } - - button, - input, - textarea { - border: 0.125em solid #000; - } - - body, - h1, - h2, - h3, - h4, - h5, - h6, - pre, - code, - button, - input, - textarea, - footer, - summary, - strong { - color: #000; - } - - summary::marker { - color: #000; - } - - summary::-webkit-details-marker { - color: #000; - } - - tbody tr:nth-child(even) { - background-color: #f2f2f2; - } - - a { - color: #00f; - text-decoration: underline; - } -} - -.cover { - border-radius: 1em; - margin-bottom: 1em; -} - -.prelude { - font-size: 0.75em; - display: flex; - flex-direction: column; - gap: 0.4em; - border-bottom: 0.125em solid var(--border); - padding-bottom: 1em; - margin-bottom: 1em; -} - -body.chat .prelude { - padding: 1em; - flex-direction: row-reverse; - justify-content: space-between; - align-items: center; -} - -@media screen and (min-width: 960px) { - body.chat .prelude { - margin: 0; - position: fixed; - bottom: 2em; - left: 2em; - border: 0; - background: var(--background); - font-size: 0.75em; - padding: 0.5em 0.75em; - border-radius: 0.5em; - gap: 1em; - } -} - -.prelude .author { - display: flex; - align-items: center; - gap: 0.5em; - font-weight: 500; - color: var(--text-muted); -} - -.prelude .author .avatar { - border-radius: 0.25em; - overflow: hidden; -} - -.prelude .author .avatar img { - width: 1.5625em; - height: 1.5625em; - object-fit: cover; -} - -.prelude time { - color: var(--text-muted); - padding: 0; -} - -article p:empty { - display: none; -} - -article img { - max-width: 100%; - border-radius: 0.5em; -} diff --git a/desk/app/expose/style/shared.css b/desk/app/expose/style/shared.css index ff05f53a73..9b6960d23f 100644 --- a/desk/app/expose/style/shared.css +++ b/desk/app/expose/style/shared.css @@ -1 +1,1615 @@ -/* TODO use me */ +:root { + --background-body: #ffffff; + --background: #ffffff; + --background-alt: #f5f5f5; + --selection: rgba(24, 24, 24, 0.08); + --text-main: #1a1818; + --text-bright: #1a1818; + --text-muted: #666666; + --links: #008eff; + --focus: #008eff; + --border: #e5e5e5; + --code: #1a1818; + --animation-duration: 0.1s; + --button-base: #f5f5f5; + --button-hover: #e5e5e5; + --scrollbar-thumb: rgb(244, 244, 244); + --scrollbar-thumb-hover: var(--button-hover); + --form-placeholder: #999999; + --form-text: #1a1818; + --variable: #2ad546; + --highlight: #fade7a; + --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); +} + +@media (prefers-color-scheme: dark) { + :root { + --background-body: #1a1818; + --background: #1a1818; + --background-alt: #322e2e; + --selection: rgba(255, 255, 255, 0.08); + --text-main: #ffffff; + --text-bright: #fff; + --text-muted: #b3b3b3; + --links: #008eff; + --focus: #008eff; + --border: #333333; + --code: #ffffff; + --animation-duration: 0.1s; + --button-base: #322e2e; + --button-hover: #4c4c4c; + --scrollbar-thumb: var(--button-hover); + --scrollbar-thumb-hover: rgb(0, 0, 0); + --form-placeholder: #808080; + --form-text: #fff; + --variable: #2ad546; + --highlight: #fade7a; + --select-arrow: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E"); + } +} + +html { + scrollbar-color: rgb(244, 244, 244) #ffffff; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + scrollbar-width: thin; +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +@media (prefers-color-scheme: dark) { + html { + scrollbar-color: #4c4c4c #1a1818; + scrollbar-color: var(--scrollbar-thumb) var(--background-body); + } +} + +body { + font-family: + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + 'Roboto', + 'Oxygen', + 'Ubuntu', + 'Cantarell', + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + 'Segoe UI Emoji', + 'Apple Color Emoji', + 'Noto Color Emoji', + sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 1.6; + margin: 1em 1em; + padding: 0; + word-wrap: break-word; + color: #1a1818; + color: var(--text-main); + background: #f5f5f5; + background: var(--background-alt); + text-rendering: optimizeLegibility; +} + +@media screen and (min-width: 40rem) { + body { + font-size: 20px; + line-height: 1.8; + } +} + +@media (prefers-color-scheme: dark) { + body { + background: #322e2e; + background: var(--background-alt); + } +} + +@media (prefers-color-scheme: dark) { + body { + color: #ffffff; + color: var(--text-main); + } +} + +button { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; +} + +@media (prefers-color-scheme: dark) { + button { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } +} + +input { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; +} + +@media (prefers-color-scheme: dark) { + input { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } +} + +textarea { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; +} + +@media (prefers-color-scheme: dark) { + textarea { + transition: + background-color 0.1s linear, + border-color 0.1s linear, + color 0.1s linear, + box-shadow 0.1s linear, + transform 0.1s ease; + transition: + background-color var(--animation-duration) linear, + border-color var(--animation-duration) linear, + color var(--animation-duration) linear, + box-shadow var(--animation-duration) linear, + transform var(--animation-duration) ease; + } +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-bottom: 0.6em; + margin-top: 1.25em; + font-size: 1em; +} + +h1 { + font-size: 2em; + margin-top: 0; + color: #1a1818; + color: var(--text-bright); + font-weight: 400; + letter-spacing: -0.025em; + line-height: 1; +} + +@media (prefers-color-scheme: dark) { + h1 { + color: #fff; + color: var(--text-bright); + } +} + +h2 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h2 { + color: #fff; + color: var(--text-bright); + } +} + +h3 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h3 { + color: #fff; + color: var(--text-bright); + } +} + +h4 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h4 { + color: #fff; + color: var(--text-bright); + } +} + +h5 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h5 { + color: #fff; + color: var(--text-bright); + } +} + +h6 { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + h6 { + color: #fff; + color: var(--text-bright); + } +} + +strong { + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + strong { + color: #fff; + color: var(--text-bright); + } +} + +h2, +h3, +h4, +h5, +h6, +b, +strong, +th { + font-weight: 600; +} + +q::before { + content: none; +} + +q::after { + content: none; +} + +blockquote { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + margin: 1.5em 0; + padding: 0.5em 1em; + font-style: italic; +} + +@media (prefers-color-scheme: dark) { + blockquote { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + } +} + +q { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + margin: 1.5em 0; + padding: 0.5em 1em; + font-style: italic; +} + +@media (prefers-color-scheme: dark) { + q { + border-left: 0.25em solid #008eff; + border-left: 0.25em solid var(--focus); + } +} + +blockquote > footer { + font-style: normal; + border: 0; +} + +blockquote cite { + font-style: normal; +} + +address { + font-style: normal; +} + +a[href^='mailto\:']::before { + content: '📧 '; +} + +a[href^='tel\:']::before { + content: '📞 '; +} + +a[href^='sms\:']::before { + content: '💬 '; +} + +mark { + background-color: #fade7a; + background-color: var(--highlight); + border-radius: 0.125em; + padding: 0 0.125em 0 0.125em; + color: #000; +} + +@media (prefers-color-scheme: dark) { + mark { + background-color: #fade7a; + background-color: var(--highlight); + } +} + +a > code, +a > strong { + color: inherit; +} + +button, +select, +input[type='submit'], +input[type='reset'], +input[type='button'], +input[type='checkbox'], +input[type='range'], +input[type='radio'] { + cursor: pointer; +} + +input, +select { + display: block; +} + +[type='checkbox'], +[type='radio'] { + display: initial; +} + +input { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + input { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + input { + color: #fff; + color: var(--form-text); + } +} + +button { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + button { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + button { + color: #fff; + color: var(--form-text); + } +} + +textarea { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + textarea { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + textarea { + color: #fff; + color: var(--form-text); + } +} + +select { + color: #1a1818; + color: var(--form-text); + background-color: #ffffff; + background-color: var(--background); + font-family: inherit; + font-size: inherit; + margin-right: 0.25em; + margin-bottom: 0.25em; + padding: 0.5em; + border: none; + border-radius: 0.25em; + outline: none; +} + +@media (prefers-color-scheme: dark) { + select { + background-color: #1a1818; + background-color: var(--background); + } +} + +@media (prefers-color-scheme: dark) { + select { + color: #fff; + color: var(--form-text); + } +} + +button { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + button { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +input[type='submit'] { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + input[type='submit'] { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +input[type='reset'] { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + input[type='reset'] { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +input[type='button'] { + background-color: #f5f5f5; + background-color: var(--button-base); + padding-right: 1.5em; + padding-left: 1.5em; +} + +@media (prefers-color-scheme: dark) { + input[type='button'] { + background-color: #322e2e; + background-color: var(--button-base); + } +} + +button:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + button:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type='submit']:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + input[type='submit']:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type='reset']:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + input[type='reset']:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type='button']:hover { + background: #e5e5e5; + background: var(--button-hover); +} + +@media (prefers-color-scheme: dark) { + input[type='button']:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + +input[type='color'] { + min-height: 2rem; + padding: 0.4em; + cursor: pointer; +} + +input[type='checkbox'], +input[type='radio'] { + height: 1em; + width: 1em; +} + +input[type='radio'] { + border-radius: 100%; +} + +input { + vertical-align: top; +} + +label { + vertical-align: middle; + margin-bottom: 0.25em; + display: inline-block; +} + +input:not([type='checkbox']):not([type='radio']), +input[type='range'], +select, +button, +textarea { + -webkit-appearance: none; +} + +textarea { + display: block; + margin-right: 0; + box-sizing: border-box; + resize: vertical; +} + +textarea:not([cols]) { + width: 100%; +} + +textarea:not([rows]) { + min-height: 2em; + height: 12em; +} + +select { + background: #ffffff + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + padding-right: 1.75em; +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +@media (prefers-color-scheme: dark) { + select { + background: #1a1818 + url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") + calc(100% - 10.125em) 50% / 10.125em no-repeat; + background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / + 10.125em no-repeat; + } +} + +select::-ms-expand { + display: none; +} + +select[multiple] { + padding-right: 0.5em; + background-image: none; + overflow-y: auto; +} + +input:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + input:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +select:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + select:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +button:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + button:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +textarea:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); +} + +@media (prefers-color-scheme: dark) { + textarea:focus { + box-shadow: 0 0 0 0.125em #008eff; + box-shadow: 0 0 0 0.125em var(--focus); + } +} + +input[type='checkbox']:active, +input[type='radio']:active, +input[type='submit']:active, +input[type='reset']:active, +input[type='button']:active, +input[type='range']:active, +button:active { + transform: translateY(0.125em); +} + +input:disabled, +select:disabled, +button:disabled, +textarea:disabled { + cursor: not-allowed; + opacity: 0.5; +} + +::-moz-placeholder { + color: #999999; + color: var(--form-placeholder); +} + +:-ms-input-placeholder { + color: #999999; + color: var(--form-placeholder); +} + +::-ms-input-placeholder { + color: #999999; + color: var(--form-placeholder); +} + +::placeholder { + color: #999999; + color: var(--form-placeholder); +} + +@media (prefers-color-scheme: dark) { + ::-moz-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + :-ms-input-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + ::-ms-input-placeholder { + color: #808080; + color: var(--form-placeholder); + } + + ::placeholder { + color: #808080; + color: var(--form-placeholder); + } +} + +fieldset { + border: 0.125em #008eff solid; + border: 0.125em var(--focus) solid; + border-radius: 0.25em; + margin: 0; + margin-bottom: 10.125em; + padding: 0.5em; +} + +@media (prefers-color-scheme: dark) { + fieldset { + border: 0.125em #008eff solid; + border: 0.125em var(--focus) solid; + } +} + +legend { + font-size: 0.9em; + font-weight: 600; +} + +input[type='range'] { + margin: 0.5em 0; + padding: 0.5em 0; + background: transparent; +} + +input[type='range']:focus { + outline: none; +} + +input[type='range']::-webkit-slider-runnable-track { + width: 100%; + height: 0.5em; + -webkit-transition: 0.2s; + transition: 0.2s; + background: #ffffff; + background: var(--background); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + input[type='range']::-webkit-slider-runnable-track { + background: #1a1818; + background: var(--background); + } +} + +input[type='range']::-webkit-slider-thumb { + box-shadow: + 0 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; + height: 1em; + width: 1em; + border-radius: 50%; + background: #e5e5e5; + background: var(--border); + -webkit-appearance: none; + margin-top: -0.3em; +} + +@media (prefers-color-scheme: dark) { + input[type='range']::-webkit-slider-thumb { + background: #333333; + background: var(--border); + } +} + +input[type='range']:focus::-webkit-slider-runnable-track { + background: #ffffff; + background: var(--background); +} + +@media (prefers-color-scheme: dark) { + input[type='range']:focus::-webkit-slider-runnable-track { + background: #1a1818; + background: var(--background); + } +} + +input[type='range']::-moz-range-track { + width: 100%; + height: 1em; + -moz-transition: 0.2s; + transition: 0.2s; + background: #ffffff; + background: var(--background); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + input[type='range']::-moz-range-track { + background: #1a1818; + background: var(--background); + } +} + +input[type='range']::-moz-range-thumb { + box-shadow: + 0.125em 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; + height: 1em; + width: 1em; + border-radius: 50%; + background: #e5e5e5; + background: var(--border); +} + +@media (prefers-color-scheme: dark) { + input[type='range']::-moz-range-thumb { + background: #333333; + background: var(--border); + } +} + +input[type='range']::-ms-track { + width: 100%; + height: 0.5em; + background: transparent; + border-color: transparent; + border-width: 10.25em 0; + color: transparent; +} + +input[type='range']::-ms-fill-lower { + background: #ffffff; + background: var(--background); + border: 0.125em solid #010101; + border-radius: 0.25em; + box-shadow: + 0.125em 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; +} + +@media (prefers-color-scheme: dark) { + input[type='range']::-ms-fill-lower { + background: #1a1818; + background: var(--background); + } +} + +input[type='range']::-ms-fill-upper { + background: #ffffff; + background: var(--background); + border: 0.125em solid #010101; + border-radius: 0.25em; + box-shadow: + 0.125em 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; +} + +@media (prefers-color-scheme: dark) { + input[type='range']::-ms-fill-upper { + background: #1a1818; + background: var(--background); + } +} + +input[type='range']::-ms-thumb { + box-shadow: + 0.125em 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; + border: 0.125em solid #000; + height: 1em; + width: 1em; + border-radius: 50%; + background: #e5e5e5; + background: var(--border); +} + +@media (prefers-color-scheme: dark) { + input[type='range']::-ms-thumb { + background: #333333; + background: var(--border); + } +} + +input[type='range']:focus::-ms-fill-lower { + background: #ffffff; + background: var(--background); +} + +@media (prefers-color-scheme: dark) { + input[type='range']:focus::-ms-fill-lower { + background: #1a1818; + background: var(--background); + } +} + +input[type='range']:focus::-ms-fill-upper { + background: #ffffff; + background: var(--background); +} + +@media (prefers-color-scheme: dark) { + input[type='range']:focus::-ms-fill-upper { + background: #1a1818; + background: var(--background); + } +} + +a { + text-decoration: underline; + color: #008eff; + color: var(--links); +} + +@media (prefers-color-scheme: dark) { + a { + color: #008eff; + color: var(--links); + } +} + +a:hover { + text-decoration: underline; +} + +code { + background: #ffffff; + background: var(--background); + color: #1a1818; + color: var(--code); + padding: 0.125em 0.25em; + border-radius: 0.25em; + font-size: 1em; +} + +@media (prefers-color-scheme: dark) { + code { + color: #ffffff; + color: var(--code); + } +} + +@media (prefers-color-scheme: dark) { + code { + background: #1a1818; + background: var(--background); + } +} + +samp { + background: #ffffff; + background: var(--background); + color: #1a1818; + color: var(--code); + padding: 0.125em 0.25em; + border-radius: 0.25em; + font-size: 1em; +} + +@media (prefers-color-scheme: dark) { + samp { + color: #ffffff; + color: var(--code); + } +} + +@media (prefers-color-scheme: dark) { + samp { + background: #1a1818; + background: var(--background); + } +} + +time { + background: #ffffff; + background: var(--background); + color: #1a1818; + color: var(--code); + padding: 0.125em 0.25em; + border-radius: 0.25em; + font-size: 1em; +} + +@media (prefers-color-scheme: dark) { + time { + color: #ffffff; + color: var(--code); + } +} + +@media (prefers-color-scheme: dark) { + time { + background: #1a1818; + background: var(--background); + } +} + +pre > code { + padding: 0.5em; + display: block; + overflow-x: auto; +} + +var { + color: #2ad546; + color: var(--variable); + font-style: normal; + font-family: monospace; +} + +@media (prefers-color-scheme: dark) { + var { + color: #2ad546; + color: var(--variable); + } +} + +kbd { + background: #ffffff; + background: var(--background); + border: 0.125em solid #e5e5e5; + border: 0.125em solid var(--border); + border-radius: 0.125em; + color: #1a1818; + color: var(--text-main); + padding: 0.125em 0.25em 0.125em 0.25em; +} + +@media (prefers-color-scheme: dark) { + kbd { + color: #ffffff; + color: var(--text-main); + } +} + +@media (prefers-color-scheme: dark) { + kbd { + border: 0.125em solid #333333; + border: 0.125em solid var(--border); + } +} + +@media (prefers-color-scheme: dark) { + kbd { + background: #1a1818; + background: var(--background); + } +} + +img, +video { + max-width: 100%; + height: auto; +} + +hr { + border: none; + border-top: 0.125em solid #e5e5e5; + border-top: 0.125em solid var(--border); +} + +@media (prefers-color-scheme: dark) { + hr { + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); + } +} + +table { + border-collapse: collapse; + margin-bottom: 0.5em; + width: 100%; + table-layout: fixed; +} + +table caption { + text-align: left; +} + +td, +th { + padding: 0.25em; + text-align: left; + vertical-align: top; + word-wrap: break-word; +} + +thead { + border-bottom: 0.125em solid #e5e5e5; + border-bottom: 0.125em solid var(--border); +} + +@media (prefers-color-scheme: dark) { + thead { + border-bottom: 0.125em solid #333333; + border-bottom: 0.125em solid var(--border); + } +} + +tfoot { + border-top: 0.125em solid #e5e5e5; + border-top: 0.125em solid var(--border); +} + +@media (prefers-color-scheme: dark) { + tfoot { + border-top: 0.125em solid #333333; + border-top: 0.125em solid var(--border); + } +} + +tbody tr:nth-child(even) { + background-color: #ffffff; + background-color: var(--background); +} + +@media (prefers-color-scheme: dark) { + tbody tr:nth-child(even) { + background-color: #1a1818; + background-color: var(--background); + } +} + +tbody tr:nth-child(even) button { + background-color: #f5f5f5; + background-color: var(--background-alt); +} + +@media (prefers-color-scheme: dark) { + tbody tr:nth-child(even) button { + background-color: #322e2e; + background-color: var(--background-alt); + } +} + +tbody tr:nth-child(even) button:hover { + background-color: #ffffff; + background-color: var(--background-body); +} + +@media (prefers-color-scheme: dark) { + tbody tr:nth-child(even) button:hover { + background-color: #1a1818; + background-color: var(--background-body); + } +} + +::-webkit-scrollbar { + height: 0.5em; + width: 0.5em; +} + +::-webkit-scrollbar-track { + background: #ffffff; + background: var(--background); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-track { + background: #1a1818; + background: var(--background); + } +} + +::-webkit-scrollbar-thumb { + background: rgb(244, 244, 244); + background: var(--scrollbar-thumb); + border-radius: 0.25em; +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb { + background: #4c4c4c; + background: var(--scrollbar-thumb); + } +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb { + background: #4c4c4c; + background: var(--scrollbar-thumb); + } +} + +::-webkit-scrollbar-thumb:hover { + background: #e5e5e5; + background: var(--scrollbar-thumb-hover); +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb:hover { + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); + } +} + +@media (prefers-color-scheme: dark) { + ::-webkit-scrollbar-thumb:hover { + background: rgb(0, 0, 0); + background: var(--scrollbar-thumb-hover); + } +} + +::-moz-selection { + background-color: rgba(24, 24, 24, 0.08); + background-color: var(--selection); + color: #1a1818; + color: var(--text-bright); +} + +::selection { + background-color: rgba(24, 24, 24, 0.08); + background-color: var(--selection); + color: #1a1818; + color: var(--text-bright); +} + +@media (prefers-color-scheme: dark) { + ::-moz-selection { + color: #fff; + color: var(--text-bright); + } + + ::selection { + color: #fff; + color: var(--text-bright); + } +} + +@media (prefers-color-scheme: dark) { + ::-moz-selection { + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); + } + + ::selection { + background-color: rgba(255, 255, 255, 0.08); + background-color: var(--selection); + } +} + +details { + display: flex; + flex-direction: column; + align-items: flex-start; + background-color: #f5f5f5; + background-color: var(--background-alt); + padding: 0.5em 0.5em 0; + margin: 1em 0; + border-radius: 0.25em; + overflow: hidden; +} + +@media (prefers-color-scheme: dark) { + details { + background-color: #322e2e; + background-color: var(--background-alt); + } +} + +details[open] { + padding: 0.5em; +} + +details > :last-child { + margin-bottom: 0; +} + +details[open] summary { + margin-bottom: 0.5em; +} + +summary { + display: list-item; + background-color: #ffffff; + background-color: var(--background); + padding: 0.5em; + margin: -0.5em -0.5em 0; + cursor: pointer; + outline: none; +} + +@media (prefers-color-scheme: dark) { + summary { + background-color: #1a1818; + background-color: var(--background); + } +} + +summary:hover, +summary:focus { + text-decoration: underline; +} + +details > :not(summary) { + margin-top: 0; +} + +summary::-webkit-details-marker { + color: #1a1818; + color: var(--text-main); +} + +@media (prefers-color-scheme: dark) { + summary::-webkit-details-marker { + color: #ffffff; + color: var(--text-main); + } +} + +dialog { + background-color: #f5f5f5; + background-color: var(--background-alt); + color: #1a1818; + color: var(--text-main); + border: none; + border-radius: 0.25em; + border-color: #e5e5e5; + border-color: var(--border); + padding: 0.5em 1.5em; +} + +@media (prefers-color-scheme: dark) { + dialog { + border-color: #333333; + border-color: var(--border); + } +} + +@media (prefers-color-scheme: dark) { + dialog { + color: #ffffff; + color: var(--text-main); + } +} + +@media (prefers-color-scheme: dark) { + dialog { + background-color: #322e2e; + background-color: var(--background-alt); + } +} + +dialog > header:first-child { + background-color: #ffffff; + background-color: var(--background); + border-radius: 0.25em 0.25em 0 0; + margin: -0.5em -1.5em 0.5em; + padding: 0.5em; + text-align: center; +} + +@media (prefers-color-scheme: dark) { + dialog > header:first-child { + background-color: #1a1818; + background-color: var(--background); + } +} + +dialog::-webkit-backdrop { + background: #0000009c; + -webkit-backdrop-filter: blur(0.25em); + backdrop-filter: blur(0.25em); +} + +dialog::backdrop { + background: #0000009c; + -webkit-backdrop-filter: blur(0.25em); + backdrop-filter: blur(0.25em); +} + +@media print { + body, + pre, + code, + summary, + details, + button, + input, + textarea { + background-color: #fff; + } + + button, + input, + textarea { + border: 0.125em solid #000; + } + + body, + h1, + h2, + h3, + h4, + h5, + h6, + pre, + code, + button, + input, + textarea, + footer, + summary, + strong { + color: #000; + } + + summary::marker { + color: #000; + } + + summary::-webkit-details-marker { + color: #000; + } + + tbody tr:nth-child(even) { + background-color: #f2f2f2; + } + + a { + color: #00f; + text-decoration: underline; + } +} From f4de93282d4265e0aabc0fa38ba48c6280d1f88d Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 18 Sep 2024 22:03:49 +0000 Subject: [PATCH 084/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 5e1cccc936..cd3a465f0c 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v6.6f6v6.n4k3o.l7rol.pea3l.m9o8u.glob' 0v6.6f6v6.n4k3o.l7rol.pea3l.m9o8u] + glob-http+['https://bootstrap.urbit.org/glob-0v1.fk3sv.qf8if.jhsgs.q0lpu.lh16m.glob' 0v1.fk3sv.qf8if.jhsgs.q0lpu.lh16m] base+'groups' version+[6 3 0] website+'https://tlon.io' From a21196bf75e184168db6122423eef5642479b11c Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 18 Sep 2024 18:15:46 -0400 Subject: [PATCH 085/157] use action sheet for invite --- .../ui/src/components/InviteUsersSheet.tsx | 19 ++--- .../ui/src/components/InviteUsersWidget.tsx | 70 +++++++++++-------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/packages/ui/src/components/InviteUsersSheet.tsx b/packages/ui/src/components/InviteUsersSheet.tsx index 7fd182c0f3..204c083c49 100644 --- a/packages/ui/src/components/InviteUsersSheet.tsx +++ b/packages/ui/src/components/InviteUsersSheet.tsx @@ -2,6 +2,7 @@ import * as db from '@tloncorp/shared/dist/db'; import React from 'react'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { ActionSheet } from './ActionSheet'; import { InviteUsersWidget } from './InviteUsersWidget'; import { Sheet } from './Sheet'; @@ -23,24 +24,16 @@ const InviteUsersSheetComponent = ({ } return ( - - - - + - - + + ); }; diff --git a/packages/ui/src/components/InviteUsersWidget.tsx b/packages/ui/src/components/InviteUsersWidget.tsx index fca07ff642..2ba938b6e2 100644 --- a/packages/ui/src/components/InviteUsersWidget.tsx +++ b/packages/ui/src/components/InviteUsersWidget.tsx @@ -2,13 +2,13 @@ import * as db from '@tloncorp/shared/dist/db'; import * as store from '@tloncorp/shared/dist/store'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Share } from 'react-native'; -import { YStack, isWeb } from 'tamagui'; +import { isWeb } from 'tamagui'; import { useBranchDomain, useBranchKey, useCurrentUserId } from '../contexts'; import { useCopy } from '../hooks/useCopy'; +import { ActionSheet } from './ActionSheet'; import { Button } from './Button'; import { ContactBook } from './ContactBook'; -import { LoadingSpinner } from './LoadingSpinner'; const InviteUsersWidgetComponent = ({ group, @@ -94,36 +94,44 @@ const InviteUsersWidgetComponent = ({ onInviteComplete(); }, [onInviteComplete]); + const buttonText = useMemo(() => { + if (invitees.length === 0 && status === 'ready') { + return `Invite friends that aren't on Tlon`; + } + + if (invitees.length === 0) { + return `Invite`; + } + + return `Invite ${invitees.length} and continue`; + }, [invitees, status]); + return ( - - - - - + <> + + + + + + {/* */} + + ); }; From a35d033945351b976bfc7140503e692491238f87 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Thu, 19 Sep 2024 06:41:18 -0500 Subject: [PATCH 086/157] Add invite users to tlon button to empty channel notice, fixes TLON-2528 --- .../components/Channel/EmptyChannelNotice.tsx | 27 +++--- .../components/InviteFriendsToTlonButton.tsx | 89 +++++++++++++++++++ .../ui/src/components/InviteUsersWidget.tsx | 10 ++- 3 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 packages/ui/src/components/InviteFriendsToTlonButton.tsx diff --git a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx index 1c23164fc2..48bfb93cae 100644 --- a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx +++ b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx @@ -1,11 +1,10 @@ import * as db from '@tloncorp/shared/dist/db'; import { useMemo, useState } from 'react'; -import { SizableText, YStack } from 'tamagui'; +import { SizableText, View, YStack } from 'tamagui'; import { useGroup } from '../../contexts'; import { useIsAdmin } from '../../utils'; -import { Button } from '../Button'; -import { InviteUsersSheet } from '../InviteUsersSheet'; +import { InviteFriendsToTlonButton } from '../InviteFriendsToTlonButton'; export function EmptyChannelNotice({ channel, @@ -17,7 +16,6 @@ export function EmptyChannelNotice({ const isGroupAdmin = useIsAdmin(channel.groupId ?? '', userId); const group = useGroup(channel.groupId ?? ''); const [isFirstVisit] = useState(() => channel.lastViewedAt == null); - const [showInviteUsersSheet, setShowInviteUsersSheet] = useState(false); const isWelcomeChannel = !!channel.isDefaultWelcomeChannel; const noticeText = useMemo(() => { if (isGroupAdmin && isFirstVisit && isWelcomeChannel) { @@ -28,21 +26,18 @@ export function EmptyChannelNotice({ }, [isGroupAdmin, isFirstVisit, isWelcomeChannel]); return ( - <> - + + {noticeText} - - setShowInviteUsersSheet(open)} - group={group} - onInviteComplete={() => setShowInviteUsersSheet(false)} - /> - + + ); } diff --git a/packages/ui/src/components/InviteFriendsToTlonButton.tsx b/packages/ui/src/components/InviteFriendsToTlonButton.tsx new file mode 100644 index 0000000000..61a726444a --- /dev/null +++ b/packages/ui/src/components/InviteFriendsToTlonButton.tsx @@ -0,0 +1,89 @@ +import * as db from '@tloncorp/shared/dist/db'; +import * as store from '@tloncorp/shared/dist/store'; +import { useCallback, useEffect } from 'react'; +import { Share } from 'react-native'; +import { Text, View, XStack, isWeb } from 'tamagui'; + +import { useBranchDomain, useBranchKey, useCurrentUserId } from '../contexts'; +import { useCopy } from '../hooks/useCopy'; +import { useIsAdmin } from '../utils'; +import { Button } from './Button'; +import { Icon } from './Icon'; + +export function InviteFriendsToTlonButton({ group }: { group?: db.Group }) { + const userId = useCurrentUserId(); + const isGroupAdmin = useIsAdmin(group?.id ?? '', userId); + const branchDomain = useBranchDomain(); + const branchKey = useBranchKey(); + const { status, shareUrl, toggle, describe } = store.useLureLinkStatus({ + flag: group?.id ?? '', + branchDomain: branchDomain, + branchKey: branchKey, + }); + const { doCopy } = useCopy(shareUrl || ''); + + const handleInviteButtonPress = useCallback(async () => { + if (shareUrl && status === 'ready' && group) { + if (isWeb) { + if (navigator.share !== undefined) { + await navigator.share({ + title: `Join ${group.title} on Tlon`, + url: shareUrl, + }); + return; + } + + doCopy(); + return; + } + + await Share.share({ + message: `Join ${group.title} on Tlon: ${shareUrl}`, + title: `Join ${group.title} on Tlon`, + }); + + return; + } + }, [group, shareUrl, doCopy, status]); + + useEffect(() => { + const meta = { + title: group?.title ?? '', + description: group?.description ?? '', + cover: group?.coverImage ?? '', + image: group?.iconImage ?? '', + }; + + const toggleLink = async () => { + if (!group) return; + await toggle(meta); + }; + if (status === 'disabled' && isGroupAdmin) { + toggleLink(); + } + if (status === 'stale') { + describe(meta); + } + }, [group, branchDomain, branchKey, toggle, status, isGroupAdmin, describe]); + + return ( + + ); +} diff --git a/packages/ui/src/components/InviteUsersWidget.tsx b/packages/ui/src/components/InviteUsersWidget.tsx index d39deb9553..8c8fd88e45 100644 --- a/packages/ui/src/components/InviteUsersWidget.tsx +++ b/packages/ui/src/components/InviteUsersWidget.tsx @@ -92,7 +92,15 @@ const InviteUsersWidgetComponent = ({ if (status === 'stale') { describe(meta); } - }, [group, branchDomain, branchKey, toggle, status, currentUserIsAdmin]); + }, [ + group, + branchDomain, + branchKey, + toggle, + status, + currentUserIsAdmin, + describe, + ]); const handleSkipButtonPress = useCallback(() => { onInviteComplete(); From 78f22fdd29830d499ca1b04e6dabe31f981aee0c Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Thu, 19 Sep 2024 06:45:18 -0500 Subject: [PATCH 087/157] Fix components with bad snapPoints --- packages/ui/src/components/AddGroupSheet.tsx | 3 ++- packages/ui/src/components/Channel/EmptyChannelNotice.tsx | 2 +- packages/ui/src/components/InviteUsersSheet.tsx | 7 ++++++- packages/ui/src/components/ProfileSheet.tsx | 7 ++++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/AddGroupSheet.tsx b/packages/ui/src/components/AddGroupSheet.tsx index 623ec789d4..e7934b64f9 100644 --- a/packages/ui/src/components/AddGroupSheet.tsx +++ b/packages/ui/src/components/AddGroupSheet.tsx @@ -181,7 +181,8 @@ export function AddGroupSheet({ moveOnKeyboardChange open={open} onOpenChange={dismiss} - snapPoints={['90%']} + snapPoints={[90]} + snapPointsMode="percent" > diff --git a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx index 48bfb93cae..2ca5846ca4 100644 --- a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx +++ b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx @@ -1,6 +1,6 @@ import * as db from '@tloncorp/shared/dist/db'; import { useMemo, useState } from 'react'; -import { SizableText, View, YStack } from 'tamagui'; +import { SizableText, YStack } from 'tamagui'; import { useGroup } from '../../contexts'; import { useIsAdmin } from '../../utils'; diff --git a/packages/ui/src/components/InviteUsersSheet.tsx b/packages/ui/src/components/InviteUsersSheet.tsx index c59b6e3341..345fbf8118 100644 --- a/packages/ui/src/components/InviteUsersSheet.tsx +++ b/packages/ui/src/components/InviteUsersSheet.tsx @@ -26,7 +26,12 @@ const InviteUsersSheetComponent = ({ if (!hasOpened.current || !group) return null; return ( - + + From dc1342c7a7e7b4563a763615417190a07bff810a Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 19 Sep 2024 09:09:27 -0400 Subject: [PATCH 088/157] expose: linted/deduped styles --- desk/app/expose/style/page.css | 32 +- desk/app/expose/style/shared.css | 661 +++++++++---------------------- 2 files changed, 211 insertions(+), 482 deletions(-) diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index 64acbb67b5..a054d83098 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -1,19 +1,19 @@ @media screen and (min-width: 960px) { body.chat { - min-height: 100vh; display: flex; + overflow: auto; + min-height: 100vh; flex-direction: column; align-items: center; justify-content: center; - overflow: auto; } } .expose-content { - margin: 0 auto; max-width: 40em; padding: 2em; border-radius: 1.5em; + margin: 0 auto; background: var(--background); } @@ -42,28 +42,28 @@ display: flex; align-items: center; justify-content: space-between; - gap: 2em; padding: 0 0 0.75em; border-bottom: 0.125em solid var(--border); + gap: 2em; } .author-row time { - color: var(--text-muted); - background: transparent; padding: 0; + background: transparent; + color: var(--text-muted); } .author { display: flex; align-items: center; - gap: 0.5em; - font-weight: 500; color: var(--text-muted); + font-weight: 500; + gap: 0.5em; } .author .avatar { - border-radius: 0.25em; overflow: hidden; + border-radius: 0.25em; } .author .avatar img { @@ -74,29 +74,29 @@ .tlon-badge { position: fixed; - bottom: 1em; right: 1em; - background: var(--background); - font-size: 0.75em; + bottom: 1em; padding: 0.5em 0.75em; - border-radius: 0.5em; border: 0.125em solid var(--border); + border-radius: 0.5em; + background: var(--background); box-shadow: 0 0.125em 0.25em var(--selection), 0 0 1em var(--selection); + font-size: 0.75em; } @media screen and (min-width: 960px) { .tlon-badge { - bottom: 2em; right: 2em; + bottom: 2em; } } .tlon-badge a { display: flex; - gap: 0.5em; align-items: center; - text-decoration: none; color: var(--text-main); + gap: 0.5em; + text-decoration: none; } diff --git a/desk/app/expose/style/shared.css b/desk/app/expose/style/shared.css index 9b6960d23f..ebea615deb 100644 --- a/desk/app/expose/style/shared.css +++ b/desk/app/expose/style/shared.css @@ -1,11 +1,11 @@ :root { - --background-body: #ffffff; - --background: #ffffff; + --background-body: #fff; + --background: #fff; --background-alt: #f5f5f5; - --selection: rgba(24, 24, 24, 0.08); + --selection: rgb(24 24 24 / 8%); --text-main: #1a1818; --text-bright: #1a1818; - --text-muted: #666666; + --text-muted: #666; --links: #008eff; --focus: #008eff; --border: #e5e5e5; @@ -13,9 +13,9 @@ --animation-duration: 0.1s; --button-base: #f5f5f5; --button-hover: #e5e5e5; - --scrollbar-thumb: rgb(244, 244, 244); + --scrollbar-thumb: rgb(244 244 244); --scrollbar-thumb-hover: var(--button-hover); - --form-placeholder: #999999; + --form-placeholder: #999; --form-text: #1a1818; --variable: #2ad546; --highlight: #fade7a; @@ -27,19 +27,19 @@ --background-body: #1a1818; --background: #1a1818; --background-alt: #322e2e; - --selection: rgba(255, 255, 255, 0.08); - --text-main: #ffffff; + --selection: rgb(255 255 255 / 8%); + --text-main: #fff; --text-bright: #fff; --text-muted: #b3b3b3; --links: #008eff; --focus: #008eff; - --border: #333333; - --code: #ffffff; + --border: #333; + --code: #fff; --animation-duration: 0.1s; --button-base: #322e2e; --button-hover: #4c4c4c; --scrollbar-thumb: var(--button-hover); - --scrollbar-thumb-hover: rgb(0, 0, 0); + --scrollbar-thumb-hover: rgb(0 0 0); --form-placeholder: #808080; --form-text: #fff; --variable: #2ad546; @@ -49,7 +49,7 @@ } html { - scrollbar-color: rgb(244, 244, 244) #ffffff; + scrollbar-color: rgb(244 244 244) #fff; scrollbar-color: var(--scrollbar-thumb) var(--background-body); scrollbar-width: thin; } @@ -61,51 +61,22 @@ html { } } -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - -@media (prefers-color-scheme: dark) { - html { - scrollbar-color: #4c4c4c #1a1818; - scrollbar-color: var(--scrollbar-thumb) var(--background-body); - } -} - body { + padding: 0; + margin: 1em; + background: #f5f5f5; + background: var(--background-alt); + color: #1a1818; + color: var(--text-main); font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', - 'Roboto', - 'Oxygen', - 'Ubuntu', - 'Cantarell', + Roboto, + Oxygen, + Ubuntu, + Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', @@ -116,17 +87,11 @@ body { font-size: 16px; font-weight: 400; line-height: 1.6; - margin: 1em 1em; - padding: 0; + text-rendering: optimizelegibility; word-wrap: break-word; - color: #1a1818; - color: var(--text-main); - background: #f5f5f5; - background: var(--background-alt); - text-rendering: optimizeLegibility; } -@media screen and (min-width: 40rem) { +@media screen and (width >= 40rem) { body { font-size: 20px; line-height: 1.8; @@ -137,194 +102,48 @@ body { body { background: #322e2e; background: var(--background-alt); - } -} - -@media (prefers-color-scheme: dark) { - body { - color: #ffffff; + color: #fff; color: var(--text-main); } } -button { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; -} - -@media (prefers-color-scheme: dark) { - button { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } -} - -input { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; -} - -@media (prefers-color-scheme: dark) { - input { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } -} - -textarea { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; -} - -@media (prefers-color-scheme: dark) { - textarea { - transition: - background-color 0.1s linear, - border-color 0.1s linear, - color 0.1s linear, - box-shadow 0.1s linear, - transform 0.1s ease; - transition: - background-color var(--animation-duration) linear, - border-color var(--animation-duration) linear, - color var(--animation-duration) linear, - box-shadow var(--animation-duration) linear, - transform var(--animation-duration) ease; - } -} - h1, h2, h3, h4, h5, h6 { - margin-bottom: 0.6em; margin-top: 1.25em; + margin-bottom: 0.6em; font-size: 1em; } h1 { - font-size: 2em; margin-top: 0; color: #1a1818; color: var(--text-bright); + font-size: 2em; font-weight: 400; letter-spacing: -0.025em; line-height: 1; } -@media (prefers-color-scheme: dark) { - h1 { - color: #fff; - color: var(--text-bright); - } -} - -h2 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h2 { - color: #fff; - color: var(--text-bright); - } -} - -h3 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h3 { - color: #fff; - color: var(--text-bright); - } -} - -h4 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h4 { - color: #fff; - color: var(--text-bright); - } -} - -h5 { - color: #1a1818; - color: var(--text-bright); -} - -@media (prefers-color-scheme: dark) { - h5 { - color: #fff; - color: var(--text-bright); - } -} - +h1, +h2, +h3, +h4, +h5, h6 { color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { + h1, + h2, + h3, + h4, + h5, h6 { color: #fff; color: var(--text-bright); @@ -363,10 +182,10 @@ q::after { } blockquote { + padding: 0.5em 1em; border-left: 0.25em solid #008eff; border-left: 0.25em solid var(--focus); margin: 1.5em 0; - padding: 0.5em 1em; font-style: italic; } @@ -378,10 +197,10 @@ blockquote { } q { + padding: 0.5em 1em; border-left: 0.25em solid #008eff; border-left: 0.25em solid var(--focus); margin: 1.5em 0; - padding: 0.5em 1em; font-style: italic; } @@ -393,8 +212,8 @@ q { } blockquote > footer { - font-style: normal; border: 0; + font-style: normal; } blockquote cite { @@ -418,10 +237,10 @@ a[href^='sms\:']::before { } mark { + padding: 0 0.125em; + border-radius: 0.125em; background-color: #fade7a; background-color: var(--highlight); - border-radius: 0.125em; - padding: 0 0.125em 0 0.125em; color: #000; } @@ -459,17 +278,17 @@ select { } input { + padding: 0.5em; + border: none; + border-radius: 0.25em; + margin-right: 0.25em; + margin-bottom: 0.25em; + background-color: #fff; + background-color: var(--background); color: #1a1818; color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; outline: none; } @@ -488,46 +307,41 @@ input { } button { + padding: 0.5em; + border: none; + border-radius: 0.25em; + margin-right: 0.25em; + margin-bottom: 0.25em; + background-color: var(--button-base); + background-color: #f5f5f5; color: #1a1818; color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; outline: none; } @media (prefers-color-scheme: dark) { button { - background-color: #1a1818; - background-color: var(--background); - } -} - -@media (prefers-color-scheme: dark) { - button { + background-color: #322e2e; + background-color: var(--button-base); color: #fff; color: var(--form-text); } } textarea { + padding: 0.5em; + border: none; + border-radius: 0.25em; + margin-right: 0.25em; + margin-bottom: 0.25em; + background-color: #fff; + background-color: var(--background); color: #1a1818; color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; outline: none; } @@ -546,17 +360,17 @@ textarea { } select { + padding: 0.5em; + border: none; + border-radius: 0.25em; + margin-right: 0.25em; + margin-bottom: 0.25em; + background-color: #fff; + background-color: var(--background); color: #1a1818; color: var(--form-text); - background-color: #ffffff; - background-color: var(--background); font-family: inherit; font-size: inherit; - margin-right: 0.25em; - margin-bottom: 0.25em; - padding: 0.5em; - border: none; - border-radius: 0.25em; outline: none; } @@ -574,25 +388,11 @@ select { } } -button { - background-color: #f5f5f5; - background-color: var(--button-base); +input[type='submit'] { padding-right: 1.5em; padding-left: 1.5em; -} - -@media (prefers-color-scheme: dark) { - button { - background-color: #322e2e; - background-color: var(--button-base); - } -} - -input[type='submit'] { background-color: #f5f5f5; background-color: var(--button-base); - padding-right: 1.5em; - padding-left: 1.5em; } @media (prefers-color-scheme: dark) { @@ -603,10 +403,10 @@ input[type='submit'] { } input[type='reset'] { - background-color: #f5f5f5; - background-color: var(--button-base); padding-right: 1.5em; padding-left: 1.5em; + background-color: #f5f5f5; + background-color: var(--button-base); } @media (prefers-color-scheme: dark) { @@ -617,10 +417,10 @@ input[type='reset'] { } input[type='button'] { - background-color: #f5f5f5; - background-color: var(--button-base); padding-right: 1.5em; padding-left: 1.5em; + background-color: #f5f5f5; + background-color: var(--button-base); } @media (prefers-color-scheme: dark) { @@ -686,8 +486,8 @@ input[type='color'] { input[type='checkbox'], input[type='radio'] { - height: 1em; width: 1em; + height: 1em; } input[type='radio'] { @@ -699,23 +499,23 @@ input { } label { - vertical-align: middle; - margin-bottom: 0.25em; display: inline-block; + margin-bottom: 0.25em; + vertical-align: middle; } -input:not([type='checkbox']):not([type='radio']), +input:not([type='checkbox'], [type='radio']), input[type='range'], select, button, textarea { - -webkit-appearance: none; + appearance: none; } textarea { display: block; - margin-right: 0; box-sizing: border-box; + margin-right: 0; resize: vertical; } @@ -724,17 +524,17 @@ textarea:not([cols]) { } textarea:not([rows]) { - min-height: 2em; height: 12em; + min-height: 2em; } select { - background: #ffffff + padding-right: 1.75em; + background: #fff url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23161f27'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") calc(100% - 10.125em) 50% / 10.125em no-repeat; background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / 10.125em no-repeat; - padding-right: 1.75em; } @media (prefers-color-scheme: dark) { @@ -853,38 +653,38 @@ textarea:disabled { opacity: 0.5; } -::-moz-placeholder { - color: #999999; +::placeholder { + color: #999; color: var(--form-placeholder); } -:-ms-input-placeholder { - color: #999999; +:input-placeholder { + color: #999; color: var(--form-placeholder); } -::-ms-input-placeholder { - color: #999999; +::input-placeholder { + color: #999; color: var(--form-placeholder); } ::placeholder { - color: #999999; + color: #999; color: var(--form-placeholder); } @media (prefers-color-scheme: dark) { - ::-moz-placeholder { + ::placeholder { color: #808080; color: var(--form-placeholder); } - :-ms-input-placeholder { + :input-placeholder { color: #808080; color: var(--form-placeholder); } - ::-ms-input-placeholder { + ::input-placeholder { color: #808080; color: var(--form-placeholder); } @@ -896,12 +696,12 @@ textarea:disabled { } fieldset { + padding: 0.5em; border: 0.125em #008eff solid; border: 0.125em var(--focus) solid; border-radius: 0.25em; margin: 0; margin-bottom: 10.125em; - padding: 0.5em; } @media (prefers-color-scheme: dark) { @@ -917,8 +717,8 @@ legend { } input[type='range'] { - margin: 0.5em 0; padding: 0.5em 0; + margin: 0.5em 0; background: transparent; } @@ -929,11 +729,10 @@ input[type='range']:focus { input[type='range']::-webkit-slider-runnable-track { width: 100%; height: 0.5em; - -webkit-transition: 0.2s; - transition: 0.2s; - background: #ffffff; - background: var(--background); border-radius: 0.25em; + background: #fff; + background: var(--background); + transition: 0.2s; } @media (prefers-color-scheme: dark) { @@ -944,27 +743,27 @@ input[type='range']::-webkit-slider-runnable-track { } input[type='range']::-webkit-slider-thumb { - box-shadow: - 0 0.125em 0.125em #000, - 0 0 0.125em #0d0d0d; - height: 1em; width: 1em; + height: 1em; border-radius: 50%; + margin-top: -0.3em; + appearance: none; background: #e5e5e5; background: var(--border); - -webkit-appearance: none; - margin-top: -0.3em; + box-shadow: + 0 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; } @media (prefers-color-scheme: dark) { input[type='range']::-webkit-slider-thumb { - background: #333333; + background: #333; background: var(--border); } } input[type='range']:focus::-webkit-slider-runnable-track { - background: #ffffff; + background: #fff; background: var(--background); } @@ -978,11 +777,11 @@ input[type='range']:focus::-webkit-slider-runnable-track { input[type='range']::-moz-range-track { width: 100%; height: 1em; - -moz-transition: 0.2s; - transition: 0.2s; - background: #ffffff; - background: var(--background); border-radius: 0.25em; + background: #fff; + background: var(--background); + transition: 0.2s; + transition: 0.2s; } @media (prefers-color-scheme: dark) { @@ -993,19 +792,19 @@ input[type='range']::-moz-range-track { } input[type='range']::-moz-range-thumb { - box-shadow: - 0.125em 0.125em 0.125em #000, - 0 0 0.125em #0d0d0d; - height: 1em; width: 1em; + height: 1em; border-radius: 50%; background: #e5e5e5; background: var(--border); + box-shadow: + 0.125em 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; } @media (prefers-color-scheme: dark) { input[type='range']::-moz-range-thumb { - background: #333333; + background: #333; background: var(--border); } } @@ -1013,17 +812,17 @@ input[type='range']::-moz-range-thumb { input[type='range']::-ms-track { width: 100%; height: 0.5em; - background: transparent; - border-color: transparent; border-width: 10.25em 0; + border-color: transparent; + background: transparent; color: transparent; } input[type='range']::-ms-fill-lower { - background: #ffffff; - background: var(--background); border: 0.125em solid #010101; border-radius: 0.25em; + background: #fff; + background: var(--background); box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; @@ -1037,10 +836,10 @@ input[type='range']::-ms-fill-lower { } input[type='range']::-ms-fill-upper { - background: #ffffff; - background: var(--background); border: 0.125em solid #010101; border-radius: 0.25em; + background: #fff; + background: var(--background); box-shadow: 0.125em 0.125em 0.125em #000, 0 0 0.125em #0d0d0d; @@ -1054,26 +853,26 @@ input[type='range']::-ms-fill-upper { } input[type='range']::-ms-thumb { - box-shadow: - 0.125em 0.125em 0.125em #000, - 0 0 0.125em #0d0d0d; - border: 0.125em solid #000; - height: 1em; width: 1em; + height: 1em; + border: 0.125em solid #000; border-radius: 50%; background: #e5e5e5; background: var(--border); + box-shadow: + 0.125em 0.125em 0.125em #000, + 0 0 0.125em #0d0d0d; } @media (prefers-color-scheme: dark) { input[type='range']::-ms-thumb { - background: #333333; + background: #333; background: var(--border); } } input[type='range']:focus::-ms-fill-lower { - background: #ffffff; + background: #fff; background: var(--background); } @@ -1085,7 +884,7 @@ input[type='range']:focus::-ms-fill-lower { } input[type='range']:focus::-ms-fill-upper { - background: #ffffff; + background: #fff; background: var(--background); } @@ -1097,9 +896,9 @@ input[type='range']:focus::-ms-fill-upper { } a { - text-decoration: underline; color: #008eff; color: var(--links); + text-decoration: underline; } @media (prefers-color-scheme: dark) { @@ -1114,88 +913,73 @@ a:hover { } code { - background: #ffffff; + padding: 0.125em 0.25em; + border-radius: 0.25em; + background: #fff; background: var(--background); color: #1a1818; color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; font-size: 1em; } -@media (prefers-color-scheme: dark) { - code { - color: #ffffff; - color: var(--code); - } -} - @media (prefers-color-scheme: dark) { code { background: #1a1818; background: var(--background); + color: #fff; + color: var(--code); } } samp { - background: #ffffff; + padding: 0.125em 0.25em; + border-radius: 0.25em; + background: #fff; background: var(--background); color: #1a1818; color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; font-size: 1em; } -@media (prefers-color-scheme: dark) { - samp { - color: #ffffff; - color: var(--code); - } -} - @media (prefers-color-scheme: dark) { samp { background: #1a1818; background: var(--background); + color: #fff; + color: var(--code); } } time { - background: #ffffff; + padding: 0.125em 0.25em; + border-radius: 0.25em; + background: #fff; background: var(--background); color: #1a1818; color: var(--code); - padding: 0.125em 0.25em; - border-radius: 0.25em; font-size: 1em; } -@media (prefers-color-scheme: dark) { - time { - color: #ffffff; - color: var(--code); - } -} - @media (prefers-color-scheme: dark) { time { background: #1a1818; background: var(--background); + color: #fff; + color: var(--code); } } pre > code { - padding: 0.5em; display: block; + padding: 0.5em; overflow-x: auto; } var { color: #2ad546; color: var(--variable); - font-style: normal; font-family: monospace; + font-style: normal; } @media (prefers-color-scheme: dark) { @@ -1206,34 +990,24 @@ var { } kbd { - background: #ffffff; - background: var(--background); + padding: 0.125em 0.25em; border: 0.125em solid #e5e5e5; border: 0.125em solid var(--border); border-radius: 0.125em; + background: #fff; + background: var(--background); color: #1a1818; color: var(--text-main); - padding: 0.125em 0.25em 0.125em 0.25em; } @media (prefers-color-scheme: dark) { kbd { - color: #ffffff; - color: var(--text-main); - } -} - -@media (prefers-color-scheme: dark) { - kbd { - border: 0.125em solid #333333; + border: 0.125em solid #333; border: 0.125em solid var(--border); - } -} - -@media (prefers-color-scheme: dark) { - kbd { background: #1a1818; background: var(--background); + color: #fff; + color: var(--text-main); } } @@ -1251,15 +1025,15 @@ hr { @media (prefers-color-scheme: dark) { hr { - border-top: 0.125em solid #333333; + border-top: 0.125em solid #333; border-top: 0.125em solid var(--border); } } table { - border-collapse: collapse; - margin-bottom: 0.5em; width: 100%; + margin-bottom: 0.5em; + border-collapse: collapse; table-layout: fixed; } @@ -1282,7 +1056,7 @@ thead { @media (prefers-color-scheme: dark) { thead { - border-bottom: 0.125em solid #333333; + border-bottom: 0.125em solid #333; border-bottom: 0.125em solid var(--border); } } @@ -1294,13 +1068,13 @@ tfoot { @media (prefers-color-scheme: dark) { tfoot { - border-top: 0.125em solid #333333; + border-top: 0.125em solid #333; border-top: 0.125em solid var(--border); } } tbody tr:nth-child(even) { - background-color: #ffffff; + background-color: #fff; background-color: var(--background); } @@ -1324,7 +1098,7 @@ tbody tr:nth-child(even) button { } tbody tr:nth-child(even) button:hover { - background-color: #ffffff; + background-color: #fff; background-color: var(--background-body); } @@ -1336,14 +1110,14 @@ tbody tr:nth-child(even) button:hover { } ::-webkit-scrollbar { - height: 0.5em; width: 0.5em; + height: 0.5em; } ::-webkit-scrollbar-track { - background: #ffffff; - background: var(--background); border-radius: 0.25em; + background: #fff; + background: var(--background); } @media (prefers-color-scheme: dark) { @@ -1354,16 +1128,9 @@ tbody tr:nth-child(even) button:hover { } ::-webkit-scrollbar-thumb { - background: rgb(244, 244, 244); - background: var(--scrollbar-thumb); border-radius: 0.25em; -} - -@media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb { - background: #4c4c4c; - background: var(--scrollbar-thumb); - } + background: rgb(244 244 244); + background: var(--scrollbar-thumb); } @media (prefers-color-scheme: dark) { @@ -1380,66 +1147,44 @@ tbody tr:nth-child(even) button:hover { @media (prefers-color-scheme: dark) { ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); + background: rgb(0 0 0); background: var(--scrollbar-thumb-hover); } } -@media (prefers-color-scheme: dark) { - ::-webkit-scrollbar-thumb:hover { - background: rgb(0, 0, 0); - background: var(--scrollbar-thumb-hover); - } -} - -::-moz-selection { - background-color: rgba(24, 24, 24, 0.08); - background-color: var(--selection); - color: #1a1818; - color: var(--text-bright); -} - ::selection { - background-color: rgba(24, 24, 24, 0.08); + background-color: rgb(24 24 24 / 8%); background-color: var(--selection); color: #1a1818; color: var(--text-bright); } @media (prefers-color-scheme: dark) { - ::-moz-selection { - color: #fff; - color: var(--text-bright); - } - ::selection { + background-color: rgb(255 255 255 / 8%); + background-color: var(--selection); color: #fff; color: var(--text-bright); } -} - -@media (prefers-color-scheme: dark) { - ::-moz-selection { - background-color: rgba(255, 255, 255, 0.08); - background-color: var(--selection); - } ::selection { - background-color: rgba(255, 255, 255, 0.08); + background-color: rgb(255 255 255 / 8%); background-color: var(--selection); + color: #fff; + color: var(--text-bright); } } details { display: flex; + overflow: hidden; flex-direction: column; align-items: flex-start; - background-color: #f5f5f5; - background-color: var(--background-alt); padding: 0.5em 0.5em 0; - margin: 1em 0; border-radius: 0.25em; - overflow: hidden; + margin: 1em 0; + background-color: #f5f5f5; + background-color: var(--background-alt); } @media (prefers-color-scheme: dark) { @@ -1463,10 +1208,10 @@ details[open] summary { summary { display: list-item; - background-color: #ffffff; - background-color: var(--background); padding: 0.5em; margin: -0.5em -0.5em 0; + background-color: #fff; + background-color: var(--background); cursor: pointer; outline: none; } @@ -1494,50 +1239,40 @@ summary::-webkit-details-marker { @media (prefers-color-scheme: dark) { summary::-webkit-details-marker { - color: #ffffff; + color: #fff; color: var(--text-main); } } dialog { + padding: 0.5em 1.5em; + border: none; + border-color: #e5e5e5; + border-color: var(--border); + border-radius: 0.25em; background-color: #f5f5f5; background-color: var(--background-alt); color: #1a1818; color: var(--text-main); - border: none; - border-radius: 0.25em; - border-color: #e5e5e5; - border-color: var(--border); - padding: 0.5em 1.5em; } @media (prefers-color-scheme: dark) { dialog { - border-color: #333333; + border-color: #333; border-color: var(--border); - } -} - -@media (prefers-color-scheme: dark) { - dialog { - color: #ffffff; - color: var(--text-main); - } -} - -@media (prefers-color-scheme: dark) { - dialog { background-color: #322e2e; background-color: var(--background-alt); + color: #fff; + color: var(--text-main); } } dialog > header:first-child { - background-color: #ffffff; - background-color: var(--background); + padding: 0.5em; border-radius: 0.25em 0.25em 0 0; margin: -0.5em -1.5em 0.5em; - padding: 0.5em; + background-color: #fff; + background-color: var(--background); text-align: center; } @@ -1548,16 +1283,10 @@ dialog > header:first-child { } } -dialog::-webkit-backdrop { - background: #0000009c; - -webkit-backdrop-filter: blur(0.25em); - backdrop-filter: blur(0.25em); -} - dialog::backdrop { - background: #0000009c; - -webkit-backdrop-filter: blur(0.25em); backdrop-filter: blur(0.25em); + backdrop-filter: blur(0.25em); + background: #0000009c; } @media print { From 58b527b3575efd72166e1262848249a843880557 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Thu, 19 Sep 2024 09:06:17 -0500 Subject: [PATCH 089/157] Move create group out to its own screen --- apps/tlon-mobile/cosmos.imports.ts | 110 +++--- .../controllers/ChatListScreenController.tsx | 1 + .../CreateGroupScreenController.tsx | 15 + .../src/fixtures/AddGroupSheet.fixture.tsx | 2 +- .../src/fixtures/CreateGroup.fixture.tsx | 14 + apps/tlon-mobile/src/navigation/RootStack.tsx | 2 + apps/tlon-mobile/src/types.ts | 1 + apps/tlon-web-new/src/app.tsx | 2 + .../controllers/ChatListScreenController.tsx | 3 + .../CreateGroupScreenController.tsx | 8 + packages/app/features/top/ChatListScreen.tsx | 22 +- .../app/features/top/CreateGroupScreen.tsx | 8 + .../components/AddChats/CreateGroupWidget.tsx | 9 +- packages/ui/src/components/AddGroupSheet.tsx | 370 +++--------------- .../ui/src/components/CreateGroupView.tsx | 107 +++++ packages/ui/src/index.tsx | 1 + 16 files changed, 279 insertions(+), 396 deletions(-) create mode 100644 apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx create mode 100644 apps/tlon-mobile/src/fixtures/CreateGroup.fixture.tsx create mode 100644 apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx create mode 100644 packages/app/features/top/CreateGroupScreen.tsx create mode 100644 packages/ui/src/components/CreateGroupView.tsx diff --git a/apps/tlon-mobile/cosmos.imports.ts b/apps/tlon-mobile/cosmos.imports.ts index b6c8a90df1..04069af491 100644 --- a/apps/tlon-mobile/cosmos.imports.ts +++ b/apps/tlon-mobile/cosmos.imports.ts @@ -3,33 +3,34 @@ import { RendererConfig, UserModuleWrappers } from 'react-cosmos-core'; import * as fixture0 from './src/App.fixture'; -import * as fixture51 from './src/fixtures/ActionSheet/AddGalleryPostSheet.fixture'; -import * as fixture50 from './src/fixtures/ActionSheet/AttachmentSheet.fixture'; -import * as fixture49 from './src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture'; -import * as fixture48 from './src/fixtures/ActionSheet/CreateChannelSheet.fixture'; -import * as fixture47 from './src/fixtures/ActionSheet/DeleteSheet.fixture'; -import * as fixture46 from './src/fixtures/ActionSheet/EditSectionNameSheet.fixture'; -import * as fixture45 from './src/fixtures/ActionSheet/GenericActionSheet.fixture'; -import * as fixture44 from './src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture'; -import * as fixture43 from './src/fixtures/ActionSheet/GroupPreviewSheet.fixture'; -import * as fixture42 from './src/fixtures/ActionSheet/ProfileSheet.fixture'; -import * as fixture41 from './src/fixtures/ActionSheet/SendPostRetrySheet.fixture'; -import * as fixture37 from './src/fixtures/Activity.fixture'; -import * as fixture36 from './src/fixtures/AddGroupSheet.fixture'; -import * as fixture35 from './src/fixtures/AttachmentPreviewList.fixture'; -import * as fixture34 from './src/fixtures/AudioEmbed.fixture'; -import * as fixture33 from './src/fixtures/Avatar.fixture'; -import * as fixture32 from './src/fixtures/BlockSectionList.fixture'; -import * as fixture31 from './src/fixtures/Button.fixture'; -import * as fixture30 from './src/fixtures/Channel.fixture'; -import * as fixture29 from './src/fixtures/ChannelDivider.fixture'; -import * as fixture28 from './src/fixtures/ChannelHeader.fixture'; -import * as fixture27 from './src/fixtures/ChannelSwitcherSheet.fixture'; -import * as fixture26 from './src/fixtures/ChatMessage.fixture'; -import * as fixture25 from './src/fixtures/ContactList.fixture'; -import * as fixture40 from './src/fixtures/DetailView/ChatDetailView.fixture'; -import * as fixture39 from './src/fixtures/DetailView/GalleryDetailView.fixture'; -import * as fixture38 from './src/fixtures/DetailView/NotebookDetailView.fixture'; +import * as fixture52 from './src/fixtures/ActionSheet/AddGalleryPostSheet.fixture'; +import * as fixture51 from './src/fixtures/ActionSheet/AttachmentSheet.fixture'; +import * as fixture50 from './src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture'; +import * as fixture49 from './src/fixtures/ActionSheet/CreateChannelSheet.fixture'; +import * as fixture48 from './src/fixtures/ActionSheet/DeleteSheet.fixture'; +import * as fixture47 from './src/fixtures/ActionSheet/EditSectionNameSheet.fixture'; +import * as fixture46 from './src/fixtures/ActionSheet/GenericActionSheet.fixture'; +import * as fixture45 from './src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture'; +import * as fixture44 from './src/fixtures/ActionSheet/GroupPreviewSheet.fixture'; +import * as fixture43 from './src/fixtures/ActionSheet/ProfileSheet.fixture'; +import * as fixture42 from './src/fixtures/ActionSheet/SendPostRetrySheet.fixture'; +import * as fixture38 from './src/fixtures/Activity.fixture'; +import * as fixture37 from './src/fixtures/AddGroupSheet.fixture'; +import * as fixture36 from './src/fixtures/AttachmentPreviewList.fixture'; +import * as fixture35 from './src/fixtures/AudioEmbed.fixture'; +import * as fixture34 from './src/fixtures/Avatar.fixture'; +import * as fixture33 from './src/fixtures/BlockSectionList.fixture'; +import * as fixture32 from './src/fixtures/Button.fixture'; +import * as fixture31 from './src/fixtures/Channel.fixture'; +import * as fixture30 from './src/fixtures/ChannelDivider.fixture'; +import * as fixture29 from './src/fixtures/ChannelHeader.fixture'; +import * as fixture28 from './src/fixtures/ChannelSwitcherSheet.fixture'; +import * as fixture27 from './src/fixtures/ChatMessage.fixture'; +import * as fixture26 from './src/fixtures/ContactList.fixture'; +import * as fixture25 from './src/fixtures/CreateGroup.fixture'; +import * as fixture41 from './src/fixtures/DetailView/ChatDetailView.fixture'; +import * as fixture40 from './src/fixtures/DetailView/GalleryDetailView.fixture'; +import * as fixture39 from './src/fixtures/DetailView/NotebookDetailView.fixture'; import * as fixture24 from './src/fixtures/FindGroups.fixture'; import * as fixture23 from './src/fixtures/Form.fixture'; import * as fixture22 from './src/fixtures/GalleryPost.fixture'; @@ -87,52 +88,53 @@ const fixtures = { 'src/fixtures/GalleryPost.fixture.tsx': { module: fixture22 }, 'src/fixtures/Form.fixture.tsx': { module: fixture23 }, 'src/fixtures/FindGroups.fixture.tsx': { module: fixture24 }, - 'src/fixtures/ContactList.fixture.tsx': { module: fixture25 }, - 'src/fixtures/ChatMessage.fixture.tsx': { module: fixture26 }, - 'src/fixtures/ChannelSwitcherSheet.fixture.tsx': { module: fixture27 }, - 'src/fixtures/ChannelHeader.fixture.tsx': { module: fixture28 }, - 'src/fixtures/ChannelDivider.fixture.tsx': { module: fixture29 }, - 'src/fixtures/Channel.fixture.tsx': { module: fixture30 }, - 'src/fixtures/Button.fixture.tsx': { module: fixture31 }, - 'src/fixtures/BlockSectionList.fixture.tsx': { module: fixture32 }, - 'src/fixtures/Avatar.fixture.tsx': { module: fixture33 }, - 'src/fixtures/AudioEmbed.fixture.tsx': { module: fixture34 }, - 'src/fixtures/AttachmentPreviewList.fixture.tsx': { module: fixture35 }, - 'src/fixtures/AddGroupSheet.fixture.tsx': { module: fixture36 }, - 'src/fixtures/Activity.fixture.tsx': { module: fixture37 }, + 'src/fixtures/CreateGroup.fixture.tsx': { module: fixture25 }, + 'src/fixtures/ContactList.fixture.tsx': { module: fixture26 }, + 'src/fixtures/ChatMessage.fixture.tsx': { module: fixture27 }, + 'src/fixtures/ChannelSwitcherSheet.fixture.tsx': { module: fixture28 }, + 'src/fixtures/ChannelHeader.fixture.tsx': { module: fixture29 }, + 'src/fixtures/ChannelDivider.fixture.tsx': { module: fixture30 }, + 'src/fixtures/Channel.fixture.tsx': { module: fixture31 }, + 'src/fixtures/Button.fixture.tsx': { module: fixture32 }, + 'src/fixtures/BlockSectionList.fixture.tsx': { module: fixture33 }, + 'src/fixtures/Avatar.fixture.tsx': { module: fixture34 }, + 'src/fixtures/AudioEmbed.fixture.tsx': { module: fixture35 }, + 'src/fixtures/AttachmentPreviewList.fixture.tsx': { module: fixture36 }, + 'src/fixtures/AddGroupSheet.fixture.tsx': { module: fixture37 }, + 'src/fixtures/Activity.fixture.tsx': { module: fixture38 }, 'src/fixtures/DetailView/NotebookDetailView.fixture.tsx': { - module: fixture38, + module: fixture39, }, 'src/fixtures/DetailView/GalleryDetailView.fixture.tsx': { - module: fixture39, + module: fixture40, }, - 'src/fixtures/DetailView/ChatDetailView.fixture.tsx': { module: fixture40 }, + 'src/fixtures/DetailView/ChatDetailView.fixture.tsx': { module: fixture41 }, 'src/fixtures/ActionSheet/SendPostRetrySheet.fixture.tsx': { - module: fixture41, + module: fixture42, }, - 'src/fixtures/ActionSheet/ProfileSheet.fixture.tsx': { module: fixture42 }, + 'src/fixtures/ActionSheet/ProfileSheet.fixture.tsx': { module: fixture43 }, 'src/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx': { - module: fixture43, + module: fixture44, }, 'src/fixtures/ActionSheet/GroupJoinRequestSheet.fixture.tsx': { - module: fixture44, + module: fixture45, }, 'src/fixtures/ActionSheet/GenericActionSheet.fixture.tsx': { - module: fixture45, + module: fixture46, }, 'src/fixtures/ActionSheet/EditSectionNameSheet.fixture.tsx': { - module: fixture46, + module: fixture47, }, - 'src/fixtures/ActionSheet/DeleteSheet.fixture.tsx': { module: fixture47 }, + 'src/fixtures/ActionSheet/DeleteSheet.fixture.tsx': { module: fixture48 }, 'src/fixtures/ActionSheet/CreateChannelSheet.fixture.tsx': { - module: fixture48, + module: fixture49, }, 'src/fixtures/ActionSheet/ChannelSortActionsSheet.fixture.tsx': { - module: fixture49, + module: fixture50, }, - 'src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx': { module: fixture50 }, + 'src/fixtures/ActionSheet/AttachmentSheet.fixture.tsx': { module: fixture51 }, 'src/fixtures/ActionSheet/AddGalleryPostSheet.fixture.tsx': { - module: fixture51, + module: fixture52, }, }; diff --git a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx b/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx index e9e604c0b8..9acd7aa1d4 100644 --- a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx +++ b/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx @@ -34,6 +34,7 @@ export function ChatListScreenController({ navigateToFindGroups={() => { navigation.navigate('FindGroups'); }} + navigateToCreateGroup={() => navigation.navigate('CreateGroup')} /> ); } diff --git a/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx b/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx new file mode 100644 index 0000000000..5e1ac741ab --- /dev/null +++ b/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx @@ -0,0 +1,15 @@ +import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { CreateGroupScreen } from '@tloncorp/app/features/top/CreateGroupScreen'; + +import type { RootStackParamList } from '../types'; + +type ChatListControllerProps = NativeStackScreenProps< + RootStackParamList, + 'ChatList' +>; + +export function CreateGroupScreenController({ + navigation, +}: ChatListControllerProps) { + return navigation.goBack()} />; +} diff --git a/apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx b/apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx index dcf157917e..3c2d649e4f 100644 --- a/apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx +++ b/apps/tlon-mobile/src/fixtures/AddGroupSheet.fixture.tsx @@ -11,8 +11,8 @@ export default { open onOpenChange={() => {}} onGoToDm={() => {}} - onCreatedGroup={() => {}} navigateToFindGroups={() => {}} + navigateToCreateGroup={() => {}} /> diff --git a/apps/tlon-mobile/src/fixtures/CreateGroup.fixture.tsx b/apps/tlon-mobile/src/fixtures/CreateGroup.fixture.tsx new file mode 100644 index 0000000000..f79a637833 --- /dev/null +++ b/apps/tlon-mobile/src/fixtures/CreateGroup.fixture.tsx @@ -0,0 +1,14 @@ +import { AppDataContextProvider, CreateGroupView } from '@tloncorp/ui'; + +import { FixtureWrapper } from './FixtureWrapper'; +import { initialContacts } from './fakeData'; + +export default { + basic: ( + + + {}} navigateToChannel={() => {}} /> + + + ), +}; diff --git a/apps/tlon-mobile/src/navigation/RootStack.tsx b/apps/tlon-mobile/src/navigation/RootStack.tsx index 5e86be1116..ab899b18fa 100644 --- a/apps/tlon-mobile/src/navigation/RootStack.tsx +++ b/apps/tlon-mobile/src/navigation/RootStack.tsx @@ -12,6 +12,7 @@ import { ChannelMetaScreenController } from '../controllers/ChannelMetaScreenCon import { ChannelScreenController } from '../controllers/ChannelScreenController'; import { ChannelSearchScreenController } from '../controllers/ChannelSearchScreenController'; import { ChatListScreenController } from '../controllers/ChatListScreenController'; +import { CreateGroupScreenController } from '../controllers/CreateGroupScreenController'; import { EditProfileScreenController } from '../controllers/EditProfileScreenController'; import { FeatureFlagScreenController } from '../controllers/FeatureFlagScreenController'; import { FindGroupsScreenController } from '../controllers/FindGroupsScreenController'; @@ -69,6 +70,7 @@ export function RootStack() { {/* individual screens */} + } /> } /> } /> + } /> { navigate('/find-groups'); }} + navigateToCreateGroup={() => { + navigate('/create-group'); + }} /> ); diff --git a/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx b/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx new file mode 100644 index 0000000000..d8fdd58ee4 --- /dev/null +++ b/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx @@ -0,0 +1,8 @@ +import { CreateGroupScreen } from '@tloncorp/app/features/top/CreateGroupScreen'; +import { useNavigate } from 'react-router'; + +export function CreateGroupScreenController() { + const navigate = useNavigate(); + + return navigate(-1)} />; +} diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx index 7f4efd5c1d..9f4fce757a 100644 --- a/packages/app/features/top/ChatListScreen.tsx +++ b/packages/app/features/top/ChatListScreen.tsx @@ -44,6 +44,7 @@ export default function ChatListScreen({ navigateToNotifications, navigateToProfile, navigateToFindGroups, + navigateToCreateGroup, }: { navigateToChannel: (channel: db.Channel) => void; navigateToGroupChannels: (group: db.Group) => void; @@ -52,6 +53,7 @@ export default function ChatListScreen({ navigateToNotifications: () => void; navigateToProfile: () => void; navigateToFindGroups: () => void; + navigateToCreateGroup: () => void; }) { const [addGroupOpen, setAddGroupOpen] = useState(false); const [screenTitle, setScreenTitle] = useState('Home'); @@ -123,6 +125,11 @@ export default function ChatListScreen({ navigateToFindGroups(); }, [navigateToFindGroups]); + const handleNavigateToCreateGroup = useCallback(() => { + setAddGroupOpen(false); + navigateToCreateGroup(); + }, [navigateToCreateGroup]); + const goToDm = useCallback( async (userId: string) => { const dmChannel = await store.upsertDmChannel({ @@ -134,19 +141,6 @@ export default function ChatListScreen({ [navigateToChannel, setAddGroupOpen] ); - const goToChannel = useCallback( - ({ channel }: { channel: db.Channel }) => { - setAddGroupOpen(false); - setTimeout(() => navigateToChannel(channel), 150); - }, - [navigateToChannel] - ); - - const handleGroupCreated = useCallback( - ({ channel }: { channel: db.Channel }) => goToChannel({ channel }), - [goToChannel] - ); - const handleAddGroupOpenChange = useCallback((open: boolean) => { if (!open) { setAddGroupOpen(false); @@ -329,8 +323,8 @@ export default function ChatListScreen({ open={addGroupOpen} onGoToDm={goToDm} onOpenChange={handleAddGroupOpenChange} - onCreatedGroup={handleGroupCreated} navigateToFindGroups={handleNavigateToFindGroups} + navigateToCreateGroup={handleNavigateToCreateGroup} /> ); diff --git a/packages/app/features/top/CreateGroupScreen.tsx b/packages/app/features/top/CreateGroupScreen.tsx new file mode 100644 index 0000000000..e2d58e14a2 --- /dev/null +++ b/packages/app/features/top/CreateGroupScreen.tsx @@ -0,0 +1,8 @@ +import { CreateGroupView } from '@tloncorp/ui'; +import { useGroupNavigation } from 'packages/app/hooks/useGroupNavigation'; + +export function CreateGroupScreen({ goBack }: { goBack: () => void }) { + const { goToChannel } = useGroupNavigation(); + + return ; +} diff --git a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx index a3a669d539..d80e661c29 100644 --- a/packages/ui/src/components/AddChats/CreateGroupWidget.tsx +++ b/packages/ui/src/components/AddChats/CreateGroupWidget.tsx @@ -2,15 +2,13 @@ import { createShortCodeFromTitle } from '@tloncorp/shared/dist'; import * as db from '@tloncorp/shared/dist/db'; import * as store from '@tloncorp/shared/dist/store'; import { useCallback, useState } from 'react'; -import { SizableText, XStack, YStack } from 'tamagui'; +import { YStack } from 'tamagui'; import { triggerHaptic } from '../../utils'; import { PrimaryButton } from '../Buttons'; import { Field, TextInput } from '../Form'; -import { Icon } from '../Icon'; export function CreateGroupWidget(props: { - goBack: () => void; invitees: string[]; onCreatedGroup: ({ group, @@ -55,11 +53,6 @@ export function CreateGroupWidget(props: { return ( - - props.goBack()} /> - New Group - - void; - onCreatedGroup: ({ - group, - channel, - }: { - group: db.Group; - channel: db.Channel; - }) => void; - onScrollChange: (scrolling: boolean) => void; - screenKey?: number; - contacts?: db.Contact[] | null; -} -const ActionContext = createContext({} as AddGroupActions); - -type CurrentScreenKey = 'Root' | 'CreateGroup' | 'InviteUsers'; - -const SCREEN_WIDTH = Dimensions.get('window').width; - -interface TransitionWrapperProps { - children: React.ReactNode; - isActive: boolean; -} - -// This component is used to animate the transition between screens -// in a way that mimics React Navigation's stack navigator. -// It could probably use some tweaking. -// We could also probably extract it out into its own file. -const TransitionWrapper: React.FC = ({ - children, - isActive, -}) => { - const animatedStyles = useAnimatedStyle(() => { - return { - transform: [ - { - translateX: withTiming(isActive ? 0 : SCREEN_WIDTH, { - duration: 300, - easing: Easing.out(Easing.cubic), - }), - }, - ], - opacity: withTiming(isActive ? 1 : 0, { - duration: 300, - easing: Easing.out(Easing.cubic), - }), - }; - }, [isActive]); - - return ( - - {children} - - ); -}; - -const styles = StyleSheet.create({ - screen: { - ...StyleSheet.absoluteFillObject, - }, -}); export function AddGroupSheet({ open, onOpenChange, - onCreatedGroup, onGoToDm, navigateToFindGroups, + navigateToCreateGroup, }: { open: boolean; onOpenChange: (open: boolean) => void; - onCreatedGroup: ({ - group, - channel, - }: { - group: db.Group; - channel: db.Channel; - }) => void; onGoToDm: (userId: string) => void; navigateToFindGroups: () => void; + navigateToCreateGroup: () => void; }) { const [screenScrolling, setScreenScrolling] = useState(false); const [screenKey, setScreenKey] = useState(0); - const [currentScreen, setCurrentScreen] = useState('Root'); - const [groupInvitees, setGroupInvitees] = useState([]); const contacts = useContacts(); const dismiss = useCallback(() => { @@ -122,7 +29,6 @@ export function AddGroupSheet({ // used for resetting components nested within screens after // reopening setTimeout(() => { - setCurrentScreen('Root'); setScreenKey((key) => key + 1); }, 300); }, [onOpenChange]); @@ -133,46 +39,11 @@ export function AddGroupSheet({ } }, [open]); - const goToScreen = useCallback( - (screen: CurrentScreenKey) => { - setCurrentScreen(screen); + const onSelect = useCallback( + (contactId: string) => { + onGoToDm(contactId); }, - [setCurrentScreen] - ); - - const screens: Record = useMemo( - () => ({ - Root: ( - - - - ), - CreateGroup: ( - - - - ), - InviteUsers: ( - - - - ), - }), - [goToScreen, onGoToDm, groupInvitees, currentScreen, navigateToFindGroups] + [onGoToDm] ); return ( @@ -186,189 +57,50 @@ export function AddGroupSheet({ > - - - {Object.values(screens)} - - + + + + + New Message + + + navigateToCreateGroup(), + }} + /> + navigateToFindGroups(), + }} + /> + + } + /> + + + ); } - -function ScreenWrapper({ - children, - withoutSafe, -}: { - children: React.ReactNode; - withoutSafe?: boolean; -}) { - const insets = useSafeAreaInsets(); - return ( - - {children} - - ); -} - -function RootScreen({ - goToScreen, - goToDM, - goToFindGroups, -}: { - goToScreen: (screen: CurrentScreenKey) => void; - goToDM: (userId: string) => void; - goToFindGroups: () => void; -}) { - const { onScrollChange, screenKey } = useContext(ActionContext); - const onSelect = useCallback( - (contactId: string) => { - goToDM(contactId); - }, - [goToDM] - ); - - return ( - - - - New Message - - - goToScreen('InviteUsers'), - }} - /> - goToFindGroups(), - }} - /> - - } - /> - - - ); -} - -function CreateGroupScreen({ - invitees, - goToScreen, -}: { - invitees: string[]; - goToScreen: (screen: CurrentScreenKey) => void; -}) { - const { onCreatedGroup, dismiss } = useContext(ActionContext); - const handleCreate = useCallback( - (args: { group: db.Group; channel: db.Channel }) => { - onCreatedGroup(args); - - dismiss(); - }, - [dismiss, onCreatedGroup] - ); - - return ( - - goToScreen('InviteUsers')} - onCreatedGroup={handleCreate} - invitees={invitees} - /> - - ); -} - -function InviteUsersScreen({ - invitees, - setInvitees, - goToScreen, -}: { - invitees: string[]; - setInvitees: (invitees: string[]) => void; - goToScreen: (screen: CurrentScreenKey) => void; -}) { - const { contacts } = useContext(ActionContext); - const currentUserId = useCurrentUserId(); - const { onScrollChange, screenKey } = useContext(ActionContext); - - return ( - - - - - - Select Members - - { - setInvitees([]); - goToScreen('CreateGroup'); - }} - > - Skip - - - - - - - ); -} diff --git a/packages/ui/src/components/CreateGroupView.tsx b/packages/ui/src/components/CreateGroupView.tsx new file mode 100644 index 0000000000..861016bc53 --- /dev/null +++ b/packages/ui/src/components/CreateGroupView.tsx @@ -0,0 +1,107 @@ +import * as db from '@tloncorp/shared/dist/db'; +import { useCallback, useState } from 'react'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { Text, View, XStack, YStack } from 'tamagui'; + +import { + AppDataContextProvider, + useContacts, + useCurrentUserId, +} from '../contexts'; +import { ActionSheet } from './ActionSheet'; +import { CreateGroupWidget } from './AddChats'; +import { Button } from './Button'; +import { TextButton } from './Buttons'; +import { ContactBook } from './ContactBook'; +import { GenericHeader } from './GenericHeader'; +import { Icon } from './Icon'; + +type screen = 'InviteUsers' | 'CreateGroup'; + +const titles = { + InviteUsers: 'Select Members', + CreateGroup: 'Create Group', +}; + +export function CreateGroupView({ + goBack, + navigateToChannel, +}: { + goBack: () => void; + navigateToChannel: (channel: db.Channel) => void; +}) { + const { bottom } = useSafeAreaInsets(); + const [screen, setScreen] = useState('InviteUsers'); + const [invitees, setInvitees] = useState([]); + const contacts = useContacts(); + const currentUserId = useCurrentUserId(); + + const handleCreatedGroup = useCallback( + ({ channel }: { channel: db.Channel }) => { + navigateToChannel(channel); + }, + [navigateToChannel] + ); + + return ( + + + screen === 'InviteUsers' ? goBack() : setScreen('InviteUsers') + } + showSessionStatus={false} + rightContent={ + screen === 'InviteUsers' ? ( + { + setInvitees([]); + setScreen('CreateGroup'); + }} + > + Skip + + ) : null + } + /> + + {screen === 'InviteUsers' ? ( + + + + Select members + + + + + + ) : ( + + )} + + + ); +} diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx index bd876acca1..fdf658531d 100644 --- a/packages/ui/src/index.tsx +++ b/packages/ui/src/index.tsx @@ -26,6 +26,7 @@ export * from './components/ContactList'; export { default as ContactName } from './components/ContactName'; export * from './components/ContactRow'; export * from './components/ContentReference'; +export * from './components/CreateGroupView'; export * from './components/PostContent'; export * from './components/DeleteSheet'; export * from './components/EditProfileScreenView'; From 3ae280b0b0b5e8755d340b1bdd31c20a43b459ef Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Thu, 19 Sep 2024 10:38:08 -0400 Subject: [PATCH 090/157] add skip button back in for now --- packages/shared/src/store/groupActions.ts | 1 + packages/ui/src/components/InviteUsersWidget.tsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/shared/src/store/groupActions.ts b/packages/shared/src/store/groupActions.ts index e637fa4632..e3a8ee71f9 100644 --- a/packages/shared/src/store/groupActions.ts +++ b/packages/shared/src/store/groupActions.ts @@ -28,6 +28,7 @@ export async function createGroup({ groupId, currentUserId, }); + logger.log(`created group ${groupId}`); await sync.syncGroup(groupId); await sync.syncUnreads(); // ensure current user gets registered as a member of the channel diff --git a/packages/ui/src/components/InviteUsersWidget.tsx b/packages/ui/src/components/InviteUsersWidget.tsx index bb7fad5dbd..5c109bbc9b 100644 --- a/packages/ui/src/components/InviteUsersWidget.tsx +++ b/packages/ui/src/components/InviteUsersWidget.tsx @@ -121,7 +121,7 @@ const InviteUsersWidgetComponent = ({ onSelectedChange={setInvitees} /> - + - {/* */} + ); From f5314193b024b28936a27636da715a19842d5e72 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Thu, 19 Sep 2024 09:42:53 -0500 Subject: [PATCH 091/157] Fix import --- packages/app/features/top/CreateGroupScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/features/top/CreateGroupScreen.tsx b/packages/app/features/top/CreateGroupScreen.tsx index e2d58e14a2..04f8868fa8 100644 --- a/packages/app/features/top/CreateGroupScreen.tsx +++ b/packages/app/features/top/CreateGroupScreen.tsx @@ -1,5 +1,5 @@ import { CreateGroupView } from '@tloncorp/ui'; -import { useGroupNavigation } from 'packages/app/hooks/useGroupNavigation'; +import { useGroupNavigation } from '../../hooks/useGroupNavigation'; export function CreateGroupScreen({ goBack }: { goBack: () => void }) { const { goToChannel } = useGroupNavigation(); From 6818cfbd4c452a79a33045ef2e5dea974020ad33 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 19 Sep 2024 10:58:06 -0400 Subject: [PATCH 092/157] expose + profile: moves all shared styles, reformats widgets --- desk/app/expose.hoon | 2 +- desk/app/expose/style/page.css | 29 --- desk/app/expose/style/shared.css | 241 +++++++++++++++-------- desk/app/expose/style/widget.css | 4 +- desk/app/profile.hoon | 150 ++------------ desk/app/profile/page.css | 27 +++ desk/app/profile/widgets.hoon | 328 ++++--------------------------- 7 files changed, 242 insertions(+), 539 deletions(-) create mode 100644 desk/app/profile/page.css diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 324e85d667..8fa9ecb641 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -293,7 +293,7 @@ ?~ aa=(rush i.t.wer.a dum:ag) | ?~ bb=(rush i.t.wer.b dum:ag) & (gth u.aa u.bb) - :- ;style:"{(trip style-shared)} {(trip style-widget)}" + :- ;style:"{(trip style-widget)}" =- (snoc - time-script-node:r) %+ murn cis |= ref=cite:c diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index a054d83098..dd31c71ebb 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -71,32 +71,3 @@ height: 1.5625em; object-fit: cover; } - -.tlon-badge { - position: fixed; - right: 1em; - bottom: 1em; - padding: 0.5em 0.75em; - border: 0.125em solid var(--border); - border-radius: 0.5em; - background: var(--background); - box-shadow: - 0 0.125em 0.25em var(--selection), - 0 0 1em var(--selection); - font-size: 0.75em; -} - -@media screen and (min-width: 960px) { - .tlon-badge { - right: 2em; - bottom: 2em; - } -} - -.tlon-badge a { - display: flex; - align-items: center; - color: var(--text-main); - gap: 0.5em; - text-decoration: none; -} diff --git a/desk/app/expose/style/shared.css b/desk/app/expose/style/shared.css index ebea615deb..351c8e031b 100644 --- a/desk/app/expose/style/shared.css +++ b/desk/app/expose/style/shared.css @@ -1,3 +1,5 @@ +/* == VARIABLES == */ + :root { --background-body: #fff; --background: #fff; @@ -11,7 +13,7 @@ --border: #e5e5e5; --code: #1a1818; --animation-duration: 0.1s; - --button-base: #f5f5f5; + --button-base: #fff; --button-hover: #e5e5e5; --scrollbar-thumb: rgb(244 244 244); --scrollbar-thumb-hover: var(--button-hover); @@ -36,7 +38,7 @@ --border: #333; --code: #fff; --animation-duration: 0.1s; - --button-base: #322e2e; + --button-base: #1a1818; --button-hover: #4c4c4c; --scrollbar-thumb: var(--button-hover); --scrollbar-thumb-hover: rgb(0 0 0); @@ -48,6 +50,8 @@ } } +/* == BODY STYLES == */ + html { scrollbar-color: rgb(244 244 244) #fff; scrollbar-color: var(--scrollbar-thumb) var(--background-body); @@ -107,6 +111,8 @@ body { } } +/* == HEADINGS, BLOCKQUOTES, ADDRESSES == */ + h1, h2, h3, @@ -224,18 +230,6 @@ address { font-style: normal; } -a[href^='mailto\:']::before { - content: '📧 '; -} - -a[href^='tel\:']::before { - content: '📞 '; -} - -a[href^='sms\:']::before { - content: '💬 '; -} - mark { padding: 0 0.125em; border-radius: 0.125em; @@ -251,12 +245,46 @@ mark { } } +/* == LINKS == */ + +a { + color: #008eff; + color: var(--links); + text-decoration: underline; +} + +@media (prefers-color-scheme: dark) { + a { + color: #008eff; + color: var(--links); + } +} + +a:hover { + text-decoration: underline; +} + +a[href^='mailto\:']::before { + content: '📧 '; +} + +a[href^='tel\:']::before { + content: '📞 '; +} + +a[href^='sms\:']::before { + content: '💬 '; +} + a > code, a > strong { color: inherit; } +/* == BUTTONS, INPUTS, SELECTS, ETC. == */ + button, +.button, select, input[type='submit'], input[type='reset'], @@ -306,30 +334,49 @@ input { } } -button { - padding: 0.5em; - border: none; - border-radius: 0.25em; - margin-right: 0.25em; - margin-bottom: 0.25em; +button, +.button { + display: flex; + align-items: center; + justify-content: center; + padding: 0.75em 1.5em; + border: 1px solid var(--border); + border-radius: 1.5em; background-color: var(--button-base); - background-color: #f5f5f5; + background-color: #fff; color: #1a1818; color: var(--form-text); font-family: inherit; font-size: inherit; + text-decoration: none; outline: none; } @media (prefers-color-scheme: dark) { - button { - background-color: #322e2e; + button, + .button { + background-color: #1a1818; background-color: var(--button-base); color: #fff; color: var(--form-text); } } +button:hover, +.button:hover { + background: #e5e5e5; + background: var(--button-hover); + text-decoration: none; +} + +@media (prefers-color-scheme: dark) { + button:hover, + .button:hover { + background: #4c4c4c; + background: var(--button-hover); + } +} + textarea { padding: 0.5em; border: none; @@ -430,11 +477,6 @@ input[type='button'] { } } -button:hover { - background: #e5e5e5; - background: var(--button-hover); -} - @media (prefers-color-scheme: dark) { button:hover { background: #4c4c4c; @@ -508,6 +550,7 @@ input:not([type='checkbox'], [type='radio']), input[type='range'], select, button, +.button, textarea { appearance: none; } @@ -547,36 +590,6 @@ select { } } -@media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } -} - -@media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } -} - -@media (prefers-color-scheme: dark) { - select { - background: #1a1818 - url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='62.5' width='116.9' fill='%23efefef'%3E %3Cpath d='M115.3,1.6 C113.7,0 111.1,0 109.5,1.6 L58.5,52.7 L7.4,1.6 C5.8,0 3.2,0 1.6,1.6 C0,3.2 0,5.8 1.6,7.4 L55.5,61.3 C56.3,62.1 57.3,62.5 58.4,62.5 C59.4,62.5 60.5,62.1 61.3,61.3 L115.2,7.4 C116.9,5.8 116.9,3.2 115.3,1.6Z'/%3E %3C/svg%3E") - calc(100% - 10.125em) 50% / 10.125em no-repeat; - background: var(--background) var(--select-arrow) calc(100% - 10.125em) 50% / - 10.125em no-repeat; - } -} - select::-ms-expand { display: none; } @@ -635,19 +648,10 @@ textarea:focus { } } -input[type='checkbox']:active, -input[type='radio']:active, -input[type='submit']:active, -input[type='reset']:active, -input[type='button']:active, -input[type='range']:active, -button:active { - transform: translateY(0.125em); -} - input:disabled, select:disabled, button:disabled, +.button:disabled, textarea:disabled { cursor: not-allowed; opacity: 0.5; @@ -895,22 +899,7 @@ input[type='range']:focus::-ms-fill-upper { } } -a { - color: #008eff; - color: var(--links); - text-decoration: underline; -} - -@media (prefers-color-scheme: dark) { - a { - color: #008eff; - color: var(--links); - } -} - -a:hover { - text-decoration: underline; -} +/* == CODE, TIME, SAMP, VAR, KBD == */ code { padding: 0.125em 0.25em; @@ -1011,6 +1000,8 @@ kbd { } } +/* == IMG, VIDEO, HR == */ + img, video { max-width: 100%; @@ -1030,6 +1021,8 @@ hr { } } +/* == TABLE == */ + table { width: 100%; margin-bottom: 0.5em; @@ -1109,6 +1102,8 @@ tbody tr:nth-child(even) button:hover { } } +/* == SCROLLBAR + TEXT SELECTION == */ + ::-webkit-scrollbar { width: 0.5em; height: 0.5em; @@ -1175,6 +1170,8 @@ tbody tr:nth-child(even) button:hover { } } +/* == SUMMARY + DETAILS == */ + details { display: flex; overflow: hidden; @@ -1244,6 +1241,8 @@ summary::-webkit-details-marker { } } +/* == DIALOG == */ + dialog { padding: 0.5em 1.5em; border: none; @@ -1289,6 +1288,8 @@ dialog::backdrop { background: #0000009c; } +/* == PRINT == */ + @media print { body, pre, @@ -1296,6 +1297,7 @@ dialog::backdrop { summary, details, button, + .button, input, textarea { background-color: #fff; @@ -1317,6 +1319,7 @@ dialog::backdrop { pre, code, button, + .button, input, textarea, footer, @@ -1342,3 +1345,73 @@ dialog::backdrop { text-decoration: underline; } } + +/* == COMPONENTS == */ + +/* == ISLAND == */ + +.island { + padding: 2em; + border-radius: 1.5em; + margin: 0 auto; + background: #fff; + background: var(--background); +} + +@media (prefers-color-scheme: dark) { + .island { + background: #322e2e; + background: var(--background); + } +} + +/* == TLON BADGE == */ + +.tlon-badge { + position: fixed; + right: 1em; + bottom: 1em; + padding: 0.5em 0.75em; + border: 0.125em solid var(--border); + border-radius: 0.5em; + background: var(--background); + box-shadow: + 0 0.125em 0.25em var(--selection), + 0 0 1em var(--selection); + font-size: 0.75em; +} + +@media screen and (min-width: 960px) { + .tlon-badge { + right: 2em; + bottom: 2em; + } +} + +.tlon-badge a { + display: flex; + align-items: center; + color: var(--text-main); + gap: 0.5em; + text-decoration: none; +} + +/* == HERO BUTTON == */ + +.hero-button { + display: flex; + align-items: center; + justify-content: center; + background: var(--text-main); + color: var(--background); + border-radius: 0.5em; + padding: 1em 1.5em; + text-decoration: none; + box-shadow: + 0 0.125em 0.25em var(--selection), + 0 0 1em var(--selection); +} + +.hero-button:hover { + text-decoration: none; +} diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index 9fa2b522c3..0b0b5eb950 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -3,7 +3,7 @@ otherwise style may affect other widgets on the profile (alternatively do inline style attributes) */ -#groups--expose-all { +/* #groups--expose-all { border: 1px solid red; } #groups--expose-all a { @@ -12,4 +12,4 @@ } #groups--expose-all .exposed { border: 1px solid black; -} +} */ diff --git a/desk/app/profile.hoon b/desk/app/profile.hoon index 453cd7d4e2..159cf557ca 100644 --- a/desk/app/profile.hoon +++ b/desk/app/profile.hoon @@ -9,6 +9,9 @@ /+ dbug, verb, sigil /= stock-widgets /app/profile/widgets :: +/* style-shared %css /app/expose/style/shared/css +/* style-page %css /app/profile/page/css +:: |% +$ state-0 $: %0 @@ -134,6 +137,12 @@ :: :- [%pass /eyre/cache %arvo %e %set-response '/profile' `[| %payload payload]] (spout:rudder id payload) + ?: =('/expose/style/shared.css' url.request) + :- [200 ['content-type' 'text/css']~] + `(as-octs:mimes:html style-shared) + ?: =('/profile/page.css' url.request) + :- [200 ['content-type' 'text/css']~] + `(as-octs:mimes:html style-page) %- paint:rudder =/ =query:rudder (purse:rudder url.request) :: if the request is not for /profile, we redirect to landscape @@ -175,127 +184,6 @@ ;+ body == :: - ++ style - ''' - @import url("https://fonts.cdnfonts.com/css/source-code-pro"); - - .body { - width: 100vw; - max-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - margin: 0; - padding: 0; - background-color: white; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; - color: black; - -webkit-font-smoothing: antialiased; /* Chrome, Safari */ - -moz-osx-font-smoothing: grayscale; /* Firefox */ - text-rendering: optimizeLegibility; /* General */ - } - - .widget-column { - width: 600px; - max-width: 85vw; - display: flex; - flex-direction: column; - align-items: center; - } - - .call-to-action { - position: fixed; - bottom: 36px; - left: 36px; - border: 2px solid black; - padding: 6px 12px; - border-radius: 8px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; - display: flex; - align-items: center; - justify-content: center; - text-decoration: none; - color: black; - background-color: white; - } - - #call-to-action-icon-path { - fill: black; - fill-opacity: 1; - } - - .call-to-action-text { - margin: 0 0 0 8px; - font-weight: 500; - } - - .cta-banner { - z-index: 9999; - position: fixed; - text-decoration: none; - color: white; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - font-size: 17px; - font-weight: 500; - line-height: 22px; - background-color: black; - } - - .widget { - position: relative; - box-sizing: border-box; - width: 100%; - max-width: 345px; - margin: 0 auto 20px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; - color: black; - } - - @media (max-width: 480px) { - .cta-banner { - bottom: 0px; - left: 0px; - } - - .widget-column { - padding-bottom: 56px; - } - } - - @media (prefers-color-scheme: dark) { - .cta-banner { - background-color: white; - color: black; - } - - .body { - background-color: #000000; - } - - .widget { - color: white; - } - - .call-to-action { - border: 2px solid white; - color: white; - background-color: black; - bottom: 24px; - left: 24px; - } - - #call-to-action-icon-path { - fill: white; - } - } - ''' - :: ++ head =/ ship=tape (scow %p our.bowl) =/ name=tape @@ -304,7 +192,8 @@ "{(trip nickname.u.ours)} ({ship})" ;head ;title:"{name}" - ;style:"{(trip style)}" + ;link(rel "stylesheet", href "/expose/style/shared.css"); + ;link(rel "stylesheet", href "/profile/page.css"); ;meta(charset "utf-8"); ;meta(name "viewport", content "width=device-width, initial-scale=1"); :: @@ -348,22 +237,19 @@ ?. (~(has by (~(gut by widgets) desk ~)) term) ~ %- some - ;div.widget(id "{(trip desk)}--{(trip term)}") + ;div.island.widget(id "{(trip desk)}--{(trip term)}") ;* marl.body:(~(got by (~(got by widgets) desk)) term) == == ;* ?. tlon-cta ~ :_ ~ - ;a.cta-banner(href "https://join.tlon.io") - ;svg.hero-button-svg - =width "18" - =height "18" - =viewBox "0 0 18 18" - =xmlns "http://www.w3.org/2000/svg" - ;path - =d "M15.4151 0.259814L0.497261 1.82774C0.222631 1.85661 0.0233995 2.10264 0.0522642 2.37727L0.391982 5.60946C0.420847 5.88409 0.666877 6.08332 0.941507 6.05446L5.41686 5.58408C5.96612 5.52635 6.45818 5.92482 6.51591 6.47407L6.79029 9.08469C6.84081 9.5653 6.49215 9.99585 6.01155 10.0464C5.53095 10.0969 5.10039 9.74822 5.04988 9.26762L4.85389 7.40289C4.82502 7.12826 4.57899 6.92903 4.30436 6.95789L1.07217 7.29761C0.797538 7.32648 0.598306 7.57251 0.627171 7.84714L1.56793 16.7978C1.62566 17.3471 2.11772 17.7456 2.66698 17.6878L16.5903 16.2244C17.1395 16.1667 17.538 15.6746 17.4803 15.1254L16.5395 6.17468C16.5107 5.90005 16.2646 5.70082 15.99 5.72968L12.7578 6.0694C12.4832 6.09827 12.2839 6.3443 12.3128 6.61893L12.5088 8.48366C12.5593 8.96426 12.2107 9.39481 11.73 9.44533C11.2494 9.49584 10.8189 9.14718 10.7684 8.66658L10.494 6.05596C10.4363 5.5067 10.8347 5.01464 11.384 4.95691L15.8593 4.48653C16.134 4.45767 16.3332 4.21164 16.3043 3.93701L15.9646 0.70481C15.9357 0.430181 15.6897 0.230949 15.4151 0.259814Z"; + ;div.tlon-badge + ;a(href "https://tlon.io") + ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); + ;span + ; Powered by Tlon + == == - Get a Tlon Profile == == -- diff --git a/desk/app/profile/page.css b/desk/app/profile/page.css new file mode 100644 index 0000000000..4f1ca079fa --- /dev/null +++ b/desk/app/profile/page.css @@ -0,0 +1,27 @@ +.widget-column { + max-width: 30em; + margin: 0 auto; + display: flex; + flex-direction: column; + gap: 2em; +} + +.widget-column .widget { + width: calc(100% - 4em); + position: relative; +} + +.widget h2 { + margin-top: 0; + font-weight: 400; + font-size: 0.8em; + color: var(--text-muted); +} + +.widget p:empty { + display: none; +} + +.widget p:not(:empty):last-child { + margin-bottom: 0; +} diff --git a/desk/app/profile/widgets.hoon b/desk/app/profile/widgets.hoon index acf5302af2..0459a71c07 100644 --- a/desk/app/profile/widgets.hoon +++ b/desk/app/profile/widgets.hoon @@ -26,90 +26,17 @@ ^- marl =/ style=@t ''' - .hero-button { - position: relative; + #groups--join-button { width: 100%; - padding: 12px 0px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; - font-size: 20px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 56px; - font-weight: 500; - border: solid 2px black; - outline: none; - text-decoration: none; - color: black; - } - - .hero-button:active { - background-color: black; - color: white; - } - - .hero-button-svg { - height: 20px; - width: 20px; - margin-right: 14px; - fill: white; - } - - .blurred-sphere { - z-index: 9; - background-color: #0068D4; - border-radius: 50%; - opacity: 0.4; - filter: blur(24.000255584716797px); - height: 80px; - width: 80px; - } - - .sphere-1 { - position: absolute; - top: 8px; - left: 14px; - } - - .sphere-2 { - position: absolute; - bottom: 16px; - right: 24px; - height: 80px; - width: 80px; - } - - @media (prefers-color-scheme: dark) { - .hero-button { - background-color: none; - border: solid 2px white; - color: white; - } - - .hero-button:active { - background-color: white; - color: black; - } - - .hero-button-svg { - fill: black; - } + background: transparent; + padding: 0; } ''' :~ ;style:"{(trip style)}" :: - ;a.hero-button/"https://join.tlon.io/dm-{(slag 1 (scow %p our.bowl))}" - :: ;svg.hero-button-svg - :: =width "18" - :: =height "18" - :: =viewBox "0 0 18 18" - :: =xmlns "http://www.w3.org/2000/svg" - :: ;path - :: =d "M15.4151 0.259814L0.497261 1.82774C0.222631 1.85661 0.0233995 2.10264 0.0522642 2.37727L0.391982 5.60946C0.420847 5.88409 0.666877 6.08332 0.941507 6.05446L5.41686 5.58408C5.96612 5.52635 6.45818 5.92482 6.51591 6.47407L6.79029 9.08469C6.84081 9.5653 6.49215 9.99585 6.01155 10.0464C5.53095 10.0969 5.10039 9.74822 5.04988 9.26762L4.85389 7.40289C4.82502 7.12826 4.57899 6.92903 4.30436 6.95789L1.07217 7.29761C0.797538 7.32648 0.598306 7.57251 0.627171 7.84714L1.56793 16.7978C1.62566 17.3471 2.11772 17.7456 2.66698 17.6878L16.5903 16.2244C17.1395 16.1667 17.538 15.6746 17.4803 15.1254L16.5395 6.17468C16.5107 5.90005 16.2646 5.70082 15.99 5.72968L12.7578 6.0694C12.4832 6.09827 12.2839 6.3443 12.3128 6.61893L12.5088 8.48366C12.5593 8.96426 12.2107 9.39481 11.73 9.44533C11.2494 9.49584 10.8189 9.14718 10.7684 8.66658L10.494 6.05596C10.4363 5.5067 10.8347 5.01464 11.384 4.95691L15.8593 4.48653C16.134 4.45767 16.3332 4.21164 16.3043 3.93701L15.9646 0.70481C15.9357 0.430181 15.6897 0.230949 15.4151 0.259814Z"; - :: == - ;span:"Message me on Tlon" + ;a.button/"https://join.tlon.io/dm-{(slag 1 (scow %p our.bowl))}" + ;span:"Message" == == :: @@ -117,36 +44,13 @@ ^- marl =/ style=@t ''' - .profile-bio { - margin-top: 8px; - width: 345px; - } - - .profile-bio > p { - font-size: 17px; - font-style: normal; - font-weight: 400; - } - - .profile-bio-title { - opacity: 50%; - margin-top: 0; - margin-bottom: 0; - } - - .profile-bio-content { - margin-top: 12px; - font-size: 17px; - line-height: 22px; - letter-spacing: -0.408px; - - } + .profile-bio {} ''' :~ ;style:"{(trip style)}" :: ;div.profile-bio - ;p.profile-bio-title: Info + ;h2.profile-bio-title: Info ;p.profile-bio-content ;* =* stand-in =/ class=tape @@ -162,7 +66,7 @@ =. time (sub time (mod time ~d1)) (scow %da time) :_ ~ - ;p + ;p(style "margin-bottom: 0;") ;em:"A {class} flying through space since {since}..." == ?~ ours stand-in @@ -179,197 +83,46 @@ ^- marl =/ style=@t ''' - .widget-padding { - min-height: 80px; - width: 100%; - } - - #profile-background { - width: 100%; - height: 100%; - object-fit: cover; - position: absolute; - z-index: 1; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -o-user-select: none; - user-select: none; - } - - #profile-content { + #groups--profile { position: relative; - width: 100%; - height: 220px; - - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: start; - - padding: 0px 36px 0px 36px; - z-index: 99; - } - - #profile-headline { - display: flex; - align-items: center; - position: relative; - z-index: 11; - } - - .fade-text { - background: linear-gradient( - to bottom, - rgb(255, 255, 255) 0%, - rgb(255, 255, 255) 20%, - rgba(255, 255, 255, 0.1) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - color: transparent; - display: inline-block; - } - - @media (max-width: 480px) { - - .widget-padding { - min-height: 24px; - } - - .fade-text { - background: linear-gradient( - to bottom, - rgb(255, 255, 255) 0%, - rgb(255, 255, 255) 5%, - rgba(255, 255, 255, 0.1) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - color: transparent; - display: inline-block; - } - } - - #profile-with-header { - margin: 0 auto 20px; - position: relative; - width: 345px; - height: 345px; - max-width: 85vw; - max-height: 85vw; - border-radius: 40px; - - position: relative; - aspect-ratio: 1 / 1; overflow: hidden; - - display: flex; - justify-content: center; - align-items: flex-end; - - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; - color: white; - } - - .profile-headline-container { - width: 100%; - margin-left: 20px; - margin-bottom: 20px; + background: linear-gradient(to bottom, transparent, var(--background-alt)); } - .profile-headline { - position: relative; - z-index: 999; - height: 100px; - width: 100%; display: flex; - justify-content: flex-start; - align-items: center; + align-content: center; + gap: 1em; } - - .profile-headline-avatar { - z-index: 6; - width: 100px; - overflow: hidden; - aspect-ratio: 1 / 1; - border-radius: 12px; - margin-right: 16px; - object-fit: cover; - } - .profile-headline-avatar-sigil { - width: 100px; + border-radius: 1em; overflow: hidden; - aspect-ratio: 1 / 1; - border-radius: 12px; - margin-right: 16px; + } + .profile-headline-title { display: flex; + flex-direction: column; justify-content: center; - align-items: center; - } - .profile-headline-avatar svg { - width: 100% !important; - height: 100% !important; } - - .profile-headline-nickname { - z-index: 99; - font-size: 20px; - font-weight: 500; + .profile-headline-title > p:first-child { margin-top: 0; - margin-bottom: 0; } - + .profile-headline-nickname, .profile-headline-username { - z-index: 99; - font-size: 16px; - margin-top: 4px; - opacity: 60%; - margin-top: 0; - margin-bottom: 0; + margin: 0; } - .profile-headline-overlay { - z-index: 5; - position: absolute; - width: 100%; - height: 100px; - bottom: 0; - left: 0; - backdrop-filter: blur(1px); - background: rgba(0, 0, 0, 0.3); - box-shadow: 0 -20px 30px 18px rgba(0, 0, 0, 0.3); + .profile-with-header { + aspect-ratio: 1/1; + display: flex; + align-items: flex-end; } - .profile-headline-overlay-2 { - z-index: 4; + #profile-background { position: absolute; - width: 100%; - height: 80px; - bottom: 0; + top: 0; left: 0; - backdrop-filter: blur(1px); - background: rgba(0, 0, 0, 0.3); - box-shadow: 0 -20px 30px 18px rgba(0, 0, 0, 0.3); - } - .profile-headline-overlay-3 { - z-index: 4; - position: absolute; width: 100%; - height: 40px; - bottom: 0; - left: 0; - backdrop-filter: blur(2px); - background: rgba(0, 0, 0, 0.3); - box-shadow: 0 -20px 30px 18px rgba(0, 0, 0, 0.3); - } - - #profile-without-header { - width: 345px; - display: flex; - margin-bottom: 20px; + height: 100%; + object-fit: cover; + z-index: -1; + border-radius: 2em; } ''' :~ @@ -381,7 +134,7 @@ ?: =('' u.cover.u.ours) ~ `u.cover.u.ours ?~ src - ;div#profile-without-header + ;div.profile-without-header ;div.profile-headline ;+ ?: &(?=(^ ours) ?=(^ avatar.u.ours) !=('' u.avatar.u.ours)) ;img.profile-headline-avatar @@ -401,31 +154,29 @@ :: ;div.profile-headline-title ;* =* name (cite:title our.bowl) - =* plain ;p.profile-headline-nickname(title "{(scow %p our.bowl)}"):"{name}" + =* plain ;h1.profile-headline-nickname(title "{(scow %p our.bowl)}"):"{name}" ?~ ours [plain]~ ?: =('' nickname.u.ours) [plain]~ - :~ ;p.profile-headline-nickname:"{(trip nickname.u.ours)}" + :~ ;h1.profile-headline-nickname:"{(trip nickname.u.ours)}" ;p.profile-headline-username(title "{(scow %p our.bowl)}"):"{name}" == == == == - ;div#profile-with-header + ;div.profile-with-header ;img#profile-background =src "{(trip u.src)}" =alt "Background"; - ;div.profile-headline-container - ;div.profile-headline + ;div.profile-headline ;+ ?: &(?=(^ ours) ?=(^ avatar.u.ours) !=('' u.avatar.u.ours)) ;img.profile-headline-avatar =src "{(trip u.avatar.u.ours)}" =alt "Avatar"; =/ value=@ux ?~(ours 0x0 color.u.ours) =/ color=tape ((x-co:co 6) value) - ;div.profile-headline-avatar(style "background-color: #{color}") + ;div.profile-headline-avatar-sigil(style "background-color: #{color}") ;+ %. our.bowl %_ sigil - size 80 bg '#'^color ::REVIEW groups fe caps the color's lightness, instead of :: choosing between white/black fg. should we, too? @@ -435,19 +186,14 @@ :: ;div.profile-headline-title ;* =* name (cite:title our.bowl) - =* plain ;p.profile-headline-nickname(title "{(scow %p our.bowl)}"):"{name}" + =* plain ;h1.profile-headline-nickname(title "{(scow %p our.bowl)}"):"{name}" ?~ ours [plain]~ ?: =('' nickname.u.ours) [plain]~ - :~ ;p.profile-headline-nickname:"{(trip nickname.u.ours)}" + :~ ;h1.profile-headline-nickname:"{(trip nickname.u.ours)}" ;p.profile-headline-username(title "{(scow %p our.bowl)}"):"{name}" == == == - :: - == - ;div.profile-headline-overlay; - ;div.profile-headline-overlay-2; - ;div.profile-headline-overlay-3; == == :: From 5f9d11e3e2f6dc85b5f5ae141a3923883b309108 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 19 Sep 2024 11:02:39 -0400 Subject: [PATCH 093/157] profile: handle image avatars --- desk/app/profile/widgets.hoon | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/desk/app/profile/widgets.hoon b/desk/app/profile/widgets.hoon index 0459a71c07..a1602e99f5 100644 --- a/desk/app/profile/widgets.hoon +++ b/desk/app/profile/widgets.hoon @@ -97,6 +97,12 @@ border-radius: 1em; overflow: hidden; } + .profile-headline-avatar { + border-radius: 1em; + object-fit: cover; + width: 128px; + height: 128px; + } .profile-headline-title { display: flex; flex-direction: column; From 41a9fc6762aab7a6fb6e31e5fb93bfd5c79d8fca Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 19 Sep 2024 11:10:56 -0400 Subject: [PATCH 094/157] profile: align no-header avatar/sigil to left --- desk/app/profile/widgets.hoon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/desk/app/profile/widgets.hoon b/desk/app/profile/widgets.hoon index a1602e99f5..59e2b72c45 100644 --- a/desk/app/profile/widgets.hoon +++ b/desk/app/profile/widgets.hoon @@ -115,6 +115,9 @@ .profile-headline-username { margin: 0; } + .profile-without-header .profile-headline { + margin-left: -2em; + } .profile-with-header { aspect-ratio: 1/1; display: flex; From b4261b224a0b62edec572502cefbf1db0daa1c69 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 19 Sep 2024 15:28:41 +0000 Subject: [PATCH 095/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index cd3a465f0c..6660e20f2e 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v1.fk3sv.qf8if.jhsgs.q0lpu.lh16m.glob' 0v1.fk3sv.qf8if.jhsgs.q0lpu.lh16m] + glob-http+['https://bootstrap.urbit.org/glob-0v7.iep52.5er7v.dvejp.lnukj.6gotr.glob' 0v7.iep52.5er7v.dvejp.lnukj.6gotr] base+'groups' version+[6 3 0] website+'https://tlon.io' From cd9c565eceeb382291787a71c7afc0d8e9796eff Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 19 Sep 2024 11:39:17 -0400 Subject: [PATCH 096/157] expose, profile: address PR comments --- desk/app/expose.hoon | 1 - desk/app/expose/style/shared.css | 11 ++++++++++- desk/app/profile.hoon | 10 +++------- desk/app/profile/{ => style}/page.css | 0 4 files changed, 13 insertions(+), 9 deletions(-) rename desk/app/profile/{ => style}/page.css (100%) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 8fa9ecb641..d95e2a6c17 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -214,7 +214,6 @@ ++ badge ;div.tlon-badge ;a(href "https://tlon.io") - ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); ;span ; Powered by Tlon == diff --git a/desk/app/expose/style/shared.css b/desk/app/expose/style/shared.css index 351c8e031b..0ff5c94319 100644 --- a/desk/app/expose/style/shared.css +++ b/desk/app/expose/style/shared.css @@ -1379,6 +1379,7 @@ dialog::backdrop { 0 0.125em 0.25em var(--selection), 0 0 1em var(--selection); font-size: 0.75em; + background: #fff; } @media screen and (min-width: 960px) { @@ -1391,11 +1392,19 @@ dialog::backdrop { .tlon-badge a { display: flex; align-items: center; - color: var(--text-main); + color: #000; gap: 0.5em; text-decoration: none; } +.tlon-badge a:before { + content: ''; + width: 18px; + height: 18px; + background: url("data:image/svg+xml;charset=utf-8,%3C?xml version='1.0' encoding='utf-8'?%3E %3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' height='160' width='160' fill='%23000'%3E %3Cpath d='M155 0H5a5 5 0 0 0-5 5v32.5a5 5 0 0 0 5 5h45c5.523 0 10 4.477 10 10v26.25a8.75 8.75 0 1 1-17.5 0V60a5 5 0 0 0-5-5H5a5 5 0 0 0-5 5v90c0 5.523 4.477 10 10 10h140c5.523 0 10-4.477 10-10V60a5 5 0 0 0-5-5h-32.5a5 5 0 0 0-5 5v18.75a8.75 8.75 0 0 1-17.5 0V52.5c0-5.523 4.477-10 10-10h45a5 5 0 0 0 5-5V5a5 5 0 0 0-5-5Z'/%3E %3C/svg%3E"); + background-size: cover; +} + /* == HERO BUTTON == */ .hero-button { diff --git a/desk/app/profile.hoon b/desk/app/profile.hoon index 159cf557ca..eca01acc83 100644 --- a/desk/app/profile.hoon +++ b/desk/app/profile.hoon @@ -10,7 +10,7 @@ /= stock-widgets /app/profile/widgets :: /* style-shared %css /app/expose/style/shared/css -/* style-page %css /app/profile/page/css +/* style-page %css /app/profile/style/page/css :: |% +$ state-0 @@ -137,10 +137,7 @@ :: :- [%pass /eyre/cache %arvo %e %set-response '/profile' `[| %payload payload]] (spout:rudder id payload) - ?: =('/expose/style/shared.css' url.request) - :- [200 ['content-type' 'text/css']~] - `(as-octs:mimes:html style-shared) - ?: =('/profile/page.css' url.request) + ?: =('/profile/style/page.css' url.request) :- [200 ['content-type' 'text/css']~] `(as-octs:mimes:html style-page) %- paint:rudder @@ -193,7 +190,7 @@ ;head ;title:"{name}" ;link(rel "stylesheet", href "/expose/style/shared.css"); - ;link(rel "stylesheet", href "/profile/page.css"); + ;link(rel "stylesheet", href "/profile/style/page.css"); ;meta(charset "utf-8"); ;meta(name "viewport", content "width=device-width, initial-scale=1"); :: @@ -245,7 +242,6 @@ :_ ~ ;div.tlon-badge ;a(href "https://tlon.io") - ;img@"https://tlon.io/icon.svg"(alt "Tlon logo", width "18"); ;span ; Powered by Tlon == diff --git a/desk/app/profile/page.css b/desk/app/profile/style/page.css similarity index 100% rename from desk/app/profile/page.css rename to desk/app/profile/style/page.css From 384e74e4b32398783f44dd0cba8fbe548138192f Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Thu, 19 Sep 2024 11:55:30 -0400 Subject: [PATCH 097/157] remove skip button from invite sheet --- packages/ui/src/components/InviteUsersWidget.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/ui/src/components/InviteUsersWidget.tsx b/packages/ui/src/components/InviteUsersWidget.tsx index 871e4bf47e..4b6cdd41b0 100644 --- a/packages/ui/src/components/InviteUsersWidget.tsx +++ b/packages/ui/src/components/InviteUsersWidget.tsx @@ -103,10 +103,6 @@ const InviteUsersWidgetComponent = ({ describe, ]); - const handleSkipButtonPress = useCallback(() => { - onInviteComplete(); - }, [onInviteComplete]); - const buttonText = useMemo(() => { if (invitees.length === 0 && status === 'ready') { return `Invite friends that aren't on Tlon`; @@ -129,7 +125,7 @@ const InviteUsersWidgetComponent = ({ onSelectedChange={setInvitees} /> - + - ); From 828e70c664f2ae114797e15f2a023561a1cdee08 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Thu, 19 Sep 2024 13:32:02 -0400 Subject: [PATCH 098/157] fix stale detection, only consider ready if have deeplink url, shorter timeout, more error logging --- packages/shared/src/store/lure.ts | 40 +++++++++++++++++++------------ 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index 3ef6acbb45..3be25fac0b 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -16,7 +16,7 @@ interface LureMetadata { fields: Record; } -const LURE_REQUEST_TIMEOUT = 10 * 1000; +const LURE_REQUEST_TIMEOUT = 3 * 1000; interface Lure { fetched: boolean; @@ -140,11 +140,16 @@ export const useLureState = create((set, get) => ({ path: `/group-enabled/${flag}`, }, LURE_REQUEST_TIMEOUT - ).then((en) => { - lureLogger.log(performance.now(), 'enabled fetched', flag); - - return en; - }); + ) + .then((en) => { + lureLogger.log(performance.now(), 'enabled fetched', flag); + + return en; + }) + .catch((e) => { + lureLogger.error(`group-enabled failed`, e); + return prevLure?.enabled; + }); }, prevLure?.enabled), // url (includes the token as last element of the path) asyncWithDefault(async () => { @@ -152,10 +157,15 @@ export const useLureState = create((set, get) => ({ return subscribeOnce( { app: 'reel', path: `/v1/id-link/${flag}` }, LURE_REQUEST_TIMEOUT - ).then((u) => { - lureLogger.log(performance.now(), 'url fetched', u, flag); - return u; - }); + ) + .then((u) => { + lureLogger.log(performance.now(), 'url fetched', u, flag); + return u; + }) + .catch((e) => { + lureLogger.error(`id-link failed`, e); + return prevLure?.url; + }); }, prevLure?.url), ]); @@ -333,12 +343,12 @@ export function useLureLinkStatus({ return 'disabled'; } - if (url && checkOldLureToken(url)) { + if ((url && checkOldLureToken(url)) || (fetched && !url)) { return 'stale'; } - if (!url || !checkLureToken(url) || !fetched || !checked) { - lureLogger.log('loading', fetched, checked, url); + if (!url || !checkLureToken(url) || !fetched || !checked || !deepLinkUrl) { + lureLogger.log('loading', fetched, checked, url, deepLinkUrl); return 'loading'; } @@ -347,11 +357,11 @@ export function useLureLinkStatus({ } return 'ready'; - }, [supported, fetched, enabled, url, good, checked]); + }, [supported, fetched, enabled, url, checked, deepLinkUrl, good]); lureLogger.log('url', url, 'deepLinkUrl', deepLinkUrl, 'status', status); - return { status, shareUrl: deepLinkUrl ?? url, toggle, describe }; + return { status, shareUrl: deepLinkUrl, toggle, describe }; } // hack: we get an intermediate state while generating lure links where From 915e6d4a985c5db6e14f9dc28c0750648336d3b4 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Thu, 19 Sep 2024 14:30:21 -0500 Subject: [PATCH 099/157] group creation: make sure back button goes back to chat list after successful group creation --- .../CreateGroupScreenController.tsx | 22 ++++++++++++++++++- .../CreateGroupScreenController.tsx | 18 ++++++++++++++- .../app/features/top/CreateGroupScreen.tsx | 12 ++++++---- .../ui/src/components/CreateGroupView.tsx | 9 +------- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx b/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx index 5e1ac741ab..538e4c847a 100644 --- a/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx +++ b/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx @@ -1,5 +1,7 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { CreateGroupScreen } from '@tloncorp/app/features/top/CreateGroupScreen'; +import type * as db from '@tloncorp/shared/dist/db'; +import { useCallback } from 'react'; import type { RootStackParamList } from '../types'; @@ -11,5 +13,23 @@ type ChatListControllerProps = NativeStackScreenProps< export function CreateGroupScreenController({ navigation, }: ChatListControllerProps) { - return navigation.goBack()} />; + const handleGoToChannel = useCallback( + (channel: db.Channel) => { + navigation.reset({ + index: 1, + routes: [ + { name: 'ChatList' }, + { name: 'Channel', params: { channel } }, + ], + }); + }, + [navigation] + ); + + return ( + navigation.goBack()} + goToChannel={handleGoToChannel} + /> + ); } diff --git a/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx b/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx index d8fdd58ee4..e07dd2c163 100644 --- a/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx +++ b/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx @@ -1,8 +1,24 @@ import { CreateGroupScreen } from '@tloncorp/app/features/top/CreateGroupScreen'; +import type * as db from '@tloncorp/shared/dist/db'; +import { useCallback } from 'react'; import { useNavigate } from 'react-router'; export function CreateGroupScreenController() { const navigate = useNavigate(); - return navigate(-1)} />; + const handleGoToChannel = useCallback( + (channel: db.Channel) => { + navigate(`/group/${channel.groupId}/channel/${channel.id}`, { + replace: true, + }); + }, + [navigate] + ); + + return ( + navigate(-1)} + goToChannel={handleGoToChannel} + /> + ); } diff --git a/packages/app/features/top/CreateGroupScreen.tsx b/packages/app/features/top/CreateGroupScreen.tsx index 04f8868fa8..d53b1a347b 100644 --- a/packages/app/features/top/CreateGroupScreen.tsx +++ b/packages/app/features/top/CreateGroupScreen.tsx @@ -1,8 +1,12 @@ +import type * as db from '@tloncorp/shared/dist/db'; import { CreateGroupView } from '@tloncorp/ui'; -import { useGroupNavigation } from '../../hooks/useGroupNavigation'; - -export function CreateGroupScreen({ goBack }: { goBack: () => void }) { - const { goToChannel } = useGroupNavigation(); +export function CreateGroupScreen({ + goBack, + goToChannel, +}: { + goBack: () => void; + goToChannel: (channel: db.Channel) => void; +}) { return ; } diff --git a/packages/ui/src/components/CreateGroupView.tsx b/packages/ui/src/components/CreateGroupView.tsx index 861016bc53..fd18b1a115 100644 --- a/packages/ui/src/components/CreateGroupView.tsx +++ b/packages/ui/src/components/CreateGroupView.tsx @@ -1,28 +1,21 @@ import * as db from '@tloncorp/shared/dist/db'; import { useCallback, useState } from 'react'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { Text, View, XStack, YStack } from 'tamagui'; +import { Text, View, YStack } from 'tamagui'; import { AppDataContextProvider, useContacts, useCurrentUserId, } from '../contexts'; -import { ActionSheet } from './ActionSheet'; import { CreateGroupWidget } from './AddChats'; import { Button } from './Button'; import { TextButton } from './Buttons'; import { ContactBook } from './ContactBook'; import { GenericHeader } from './GenericHeader'; -import { Icon } from './Icon'; type screen = 'InviteUsers' | 'CreateGroup'; -const titles = { - InviteUsers: 'Select Members', - CreateGroup: 'Create Group', -}; - export function CreateGroupView({ goBack, navigateToChannel, From e42175f655cab27836732384afaecd303e48a022 Mon Sep 17 00:00:00 2001 From: David Lee Date: Thu, 19 Sep 2024 12:41:53 -0700 Subject: [PATCH 100/157] Add comment about `hasCachedNewest` --- packages/app/features/top/ChannelScreen.tsx | 43 ++++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/app/features/top/ChannelScreen.tsx b/packages/app/features/top/ChannelScreen.tsx index e34a0ca35d..adca0c9f0c 100644 --- a/packages/app/features/top/ChannelScreen.tsx +++ b/packages/app/features/top/ChannelScreen.tsx @@ -113,9 +113,48 @@ export default function ChannelScreen({ return false; } const { syncedAt, lastPostAt } = channel; - if (syncedAt && session.startTime < syncedAt) { + + if (syncedAt == null) { + return false; + } + + // `syncedAt` is only set when sync endpoint reports that there are no newer posts. + // https://github.com/tloncorp/tlon-apps/blob/adde000f4330af7e0a2e19bdfcb295f5eb9fe3da/packages/shared/src/store/sync.ts#L905-L910 + // We are guaranteed to have the most recent post before `syncedAt`; and + // we are guaranteed to have the most recent post after `session.startTime`. + + // This case checks that we have overlap between sync backfill and session subscription. + // + // ------------------------| syncedAt + // session.startTime |--------------- + if (syncedAt >= session.startTime) { return true; - } else if (lastPostAt && syncedAt && syncedAt > lastPostAt) { + } + + // `lastPostAt` is set with the channel's latest post during session init: + // https://github.com/tloncorp/tlon-apps/blob/adde000f4330af7e0a2e19bdfcb295f5eb9fe3da/packages/shared/src/store/sync.ts#L1052 + // + // Since we already checked that a session is active, this case checks + // that we've hit `syncedAt`'s "no newer posts" at some point _after_ the + // channel's most recent post's timestamp. + // + // lastPostAt + // v + // ------------------------| syncedAt + // + // This check would fail if we first caught up via sync, and then later + // started a session: in that case, there could be missing posts between + // `syncedAt`'s "no newer posts" and the start of the session: + // + // lastPostAt (post not received) + // v + // ----| syncedAt + // session.startTime |--------- + // + // NB: In that case, we *do* have the single latest post for the channel, + // but we'd likely be missing all other posts in the gap. Wait until we + // filled in the gap to show posts. + if (lastPostAt && syncedAt > lastPostAt) { return true; } return false; From 4ab5e8eace1afbba36e47ff3b6d0152ac82acb47 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 19 Sep 2024 20:52:41 +0000 Subject: [PATCH 101/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 6660e20f2e..634bfb966c 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v7.iep52.5er7v.dvejp.lnukj.6gotr.glob' 0v7.iep52.5er7v.dvejp.lnukj.6gotr] + glob-http+['https://bootstrap.urbit.org/glob-0v7.bk061.vf2h1.b3jve.t2qt7.jes9n.glob' 0v7.bk061.vf2h1.b3jve.t2qt7.jes9n] base+'groups' version+[6 3 0] website+'https://tlon.io' From 25eb19a1043bcc55cb42870e9e6f14b222463523 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Thu, 19 Sep 2024 17:03:51 -0400 Subject: [PATCH 102/157] handle lure deeplink if already authed --- .../controllers/ChatListScreenController.tsx | 2 + .../src/hooks/useDeepLinkListener.ts | 59 +++++++++++++------ apps/tlon-mobile/src/types.ts | 2 +- packages/app/contexts/branch.tsx | 4 ++ packages/app/features/top/ChatListScreen.tsx | 6 +- packages/app/hooks/useNavigationLogger.ts | 6 +- packages/shared/src/store/dbHooks.ts | 12 +--- packages/shared/src/store/groupActions.ts | 11 ++++ 8 files changed, 69 insertions(+), 33 deletions(-) diff --git a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx b/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx index 9acd7aa1d4..ed8fa7e892 100644 --- a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx +++ b/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx @@ -10,9 +10,11 @@ type ChatListControllerProps = NativeStackScreenProps< export function ChatListScreenController({ navigation, + route: { params }, }: ChatListControllerProps) { return ( { navigation.push('Channel', { channel }); }} diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index b753085916..f3a4fc94b2 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -1,36 +1,59 @@ +import { NavigationProp, useNavigation } from '@react-navigation/native'; import { useBranch, useSignupParams } from '@tloncorp/app/contexts/branch'; import { useShip } from '@tloncorp/app/contexts/ship'; import { inviteShipWithLure } from '@tloncorp/app/lib/hostingApi'; import { trackError } from '@tloncorp/app/utils/posthog'; +import { createDevLogger } from '@tloncorp/shared/dist'; +import * as store from '@tloncorp/shared/dist/store'; import { useEffect, useRef } from 'react'; +import { RootStackParamList } from '../types'; + +const logger = createDevLogger('deeplinkHandler', true); + export const useDeepLinkListener = () => { - const isInvitingRef = useRef(false); + const navigation = useNavigation>(); + const isHandlingLinkRef = useRef(false); const { ship } = useShip(); const signupParams = useSignupParams(); const { clearLure, lure } = useBranch(); - // If lure is present, invite it and mark as handled useEffect(() => { - if (ship && lure && !isInvitingRef.current) { + if (ship && lure && !isHandlingLinkRef.current) { (async () => { - try { - isInvitingRef.current = true; - console.log(`inviting ship with lure`, ship, signupParams.lureId); - await inviteShipWithLure({ ship, lure: signupParams.lureId }); - } catch (err) { - console.error( - '[useDeepLinkListener] Error inviting ship with lure:', - err - ); - if (err instanceof Error) { - trackError(err); + isHandlingLinkRef.current = true; + // if the lure was clicked prior to authenticating, trigger the automatic join & DM + if (lure.clickedPreAuth) { + try { + logger.log(`inviting ship with lure`, ship, signupParams.lureId); + await inviteShipWithLure({ ship, lure: signupParams.lureId }); + } catch (err) { + logger.error('Error inviting ship with lure:', err); + if (err instanceof Error) { + trackError(err); + } + } + } else { + // otherwise, treat it as a deeplink and navigate to the group + if (lure.invitedGroupId) { + const group = await store.getGroupPreview(lure.invitedGroupId); + if (group) { + navigation.reset({ + index: 1, + routes: [{ name: 'ChatList', params: { previewGroup: group } }], + }); + } else { + logger.error( + 'Failed to navigate to group deeplink', + lure.invitedGroupId + ); + } } - } finally { - clearLure(); - isInvitingRef.current = false; } + + clearLure(); + isHandlingLinkRef.current = false; })(); } - }, [ship, signupParams, clearLure, lure]); + }, [ship, signupParams, clearLure, lure, navigation]); }; diff --git a/apps/tlon-mobile/src/types.ts b/apps/tlon-mobile/src/types.ts index 7bfe78ef40..c5e32bce9f 100644 --- a/apps/tlon-mobile/src/types.ts +++ b/apps/tlon-mobile/src/types.ts @@ -19,7 +19,7 @@ export type WebViewStackParamList = { }; export type RootStackParamList = { - ChatList: undefined; + ChatList: { previewGroup: db.Group } | undefined; Activity: undefined; Profile: undefined; Channel: { diff --git a/packages/app/contexts/branch.tsx b/packages/app/contexts/branch.tsx index d9c72b1a79..7f0f953ce3 100644 --- a/packages/app/contexts/branch.tsx +++ b/packages/app/contexts/branch.tsx @@ -13,9 +13,11 @@ import branch from 'react-native-branch'; import { DEFAULT_LURE, DEFAULT_PRIORITY_TOKEN } from '../constants'; import storage from '../lib/storage'; import { getPathFromWer } from '../utils/string'; +import { useShip } from './ship'; interface LureData extends DeepLinkMetadata { id: string; + clickedPreAuth: boolean; } type Lure = { @@ -102,6 +104,7 @@ export const useLureMetadata = () => { export const BranchProvider = ({ children }: { children: ReactNode }) => { const [{ deepLinkPath, lure, priorityToken }, setState] = useState(INITIAL_STATE); + const { ship } = useShip(); useEffect(() => { console.debug('[branch] Subscribing to Branch listener'); @@ -120,6 +123,7 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { lure: { ...extractLureMetadata(params), id: params.lure as string, + clickedPreAuth: Boolean(ship), }, priorityToken: params.token as string | undefined, }; diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx index 9f4fce757a..46a5b58a7c 100644 --- a/packages/app/features/top/ChatListScreen.tsx +++ b/packages/app/features/top/ChatListScreen.tsx @@ -37,6 +37,7 @@ const ShowFiltersButton = ({ onPress }: { onPress: () => void }) => { }; export default function ChatListScreen({ + previewGroup, navigateToChannel, navigateToGroupChannels, navigateToSelectedPost, @@ -54,6 +55,7 @@ export default function ChatListScreen({ navigateToProfile: () => void; navigateToFindGroups: () => void; navigateToCreateGroup: () => void; + previewGroup?: db.Group; }) { const [addGroupOpen, setAddGroupOpen] = useState(false); const [screenTitle, setScreenTitle] = useState('Home'); @@ -81,7 +83,9 @@ export default function ChatListScreen({ const [activeTab, setActiveTab] = useState<'all' | 'groups' | 'messages'>( 'all' ); - const [selectedGroup, setSelectedGroup] = useState(null); + const [selectedGroup, setSelectedGroup] = useState( + previewGroup ?? null + ); const [showFilters, setShowFilters] = useState(false); const isFocused = useIsFocused(); const { data: pins } = store.usePins({ diff --git a/packages/app/hooks/useNavigationLogger.ts b/packages/app/hooks/useNavigationLogger.ts index e2c1c9658b..051ea2cf3e 100644 --- a/packages/app/hooks/useNavigationLogger.ts +++ b/packages/app/hooks/useNavigationLogger.ts @@ -11,12 +11,12 @@ export function useNavigationLogging() { useEffect(() => { // Set initial route const state = navigation.getState(); - routeNameRef.current = state ? state.routes[state.index].name : ''; + routeNameRef.current = state ? state.routes[state.index]?.name : ''; const unsubscribe = navigation.addListener('state', () => { const previousRouteName = routeNameRef?.current ?? ''; const state = navigation.getState(); - const currentRouteName = state ? state.routes[state.index].name : ''; + const currentRouteName = state ? state.routes[state.index]?.name : ''; if ( previousRouteName && @@ -31,5 +31,5 @@ export function useNavigationLogging() { }); return unsubscribe; - }, [logger, navigation]); + }, [navigation]); } diff --git a/packages/shared/src/store/dbHooks.ts b/packages/shared/src/store/dbHooks.ts index d613b88aa9..21abcaf6e3 100644 --- a/packages/shared/src/store/dbHooks.ts +++ b/packages/shared/src/store/dbHooks.ts @@ -8,6 +8,7 @@ import { useMemo } from 'react'; import * as api from '../api'; import * as db from '../db'; import * as ub from '../urbit'; +import { getGroupPreview } from './groupActions'; import { hasCustomS3Creds, hasHostingUploadCreds } from './storage'; import { syncPostReference } from './sync'; import { keyFromQueryDeps, useKeyFromQueryDeps } from './useKeyFromQueryDeps'; @@ -361,16 +362,7 @@ export const useGroupPreview = (groupId: string) => { const tableDeps = useKeyFromQueryDeps(db.getGroup); return useQuery({ queryKey: ['groupPreview', tableDeps, groupId], - queryFn: async () => { - const group = await db.getGroup({ id: groupId }); - if (group) { - return group; - } - - const groupPreview = await api.getGroupPreview(groupId); - await db.insertUnjoinedGroups([groupPreview]); - return groupPreview; - }, + queryFn: async () => getGroupPreview(groupId), }); }; diff --git a/packages/shared/src/store/groupActions.ts b/packages/shared/src/store/groupActions.ts index e3a8ee71f9..4832806795 100644 --- a/packages/shared/src/store/groupActions.ts +++ b/packages/shared/src/store/groupActions.ts @@ -50,6 +50,17 @@ export async function createGroup({ } } +export async function getGroupPreview(groupId: string) { + const group = await db.getGroup({ id: groupId }); + if (group) { + return group; + } + + const groupPreview = await api.getGroupPreview(groupId); + await db.insertUnjoinedGroups([groupPreview]); + return groupPreview; +} + export async function acceptGroupInvitation(group: db.Group) { logger.log('accepting group invitation', group.id); await db.updateGroup({ id: group.id, joinStatus: 'joining' }); From e18740568ec81a571375edca19aa60da1d274f47 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 19 Sep 2024 20:21:41 -0400 Subject: [PATCH 103/157] expose: widget styling --- desk/app/expose.hoon | 38 ++++++++----- desk/app/expose/style/widget.css | 95 +++++++++++++++++++++++++++----- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index d95e2a6c17..37cbb55b36 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -306,37 +306,47 @@ =, post.u.pon ?- -.kind-data.post.u.pon %chat - ;a.exposed.chat/"/expose{link}" - ;div.meta + ;div.exposed + ;div.content + ;a.chat/"/expose{link}" + ;* (story:en-manx:u content) + == + == + ;div.author-row ;+ (author-node:r [our now] author) ;+ (render-datetime:r sent) == - ;div.content - ;* (story:en-manx:u content) - == == :: %diary - ;a.exposed.diary/"/expose{link}" + ;div.exposed ;* ?: =('' image.kind-data) ~ :_ ~ - ;img@"{(trip image.kind-data)}"; - ;h3:"{(trip title.kind-data)}" - ;+ (render-datetime:r sent) - ;+ (author-node:r [our now] author) + ;a.diary/"/expose{link}" + ;img@"{(trip image.kind-data)}"; + == + ;a.diary/"/expose{link}" + ;h3:"{(trip title.kind-data)}" + == + ;div.author-row + ;+ (author-node:r [our now] author) + ;+ (render-datetime:r sent) + == == :: %heap ::TODO for the kinds of children the div.content gets for heap posts, :: having an %a (grand)parent breaks the html rendering, :: putting the inner divs outside/after the a.exposed - ;a.exposed.heap/"/expose{link}" + ;div.exposed ;div.content - ;* (story:en-manx:u content) + ;a.heap/"/expose{link}" + ;* (story:en-manx:u content) + == == - ;div.meta - ;+ (render-datetime:r sent) + ;div.author-row ;+ (author-node:r [our now] author) + ;+ (render-datetime:r sent) == == == diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index 0b0b5eb950..7d0d9b6882 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -1,15 +1,82 @@ -/* TODO - prefer scoping rules to just elements under #groups--expose-all, - otherwise style may affect other widgets on the profile - (alternatively do inline style attributes) -*/ -/* #groups--expose-all { - border: 1px solid red; -} -#groups--expose-all a { - display: block; - margin: 1em; -} -#groups--expose-all .exposed { - border: 1px solid black; +#groups--expose-all { + padding: 0 2em; + overflow: hidden; +} + +.exposed { + width: 100%; + padding: 2em 2em; + margin-left: -2em; + border-bottom: 0.125em solid var(--border); +} + +/* .exposed:hover { + background: var(--selection); } */ + +.exposed a { + text-decoration: none; +} + +.exposed .content { + margin-bottom: 1em; +} + +.exposed .content p:first-of-type { + margin-top: 0; +} + +.exposed .content a { + color: var(--text-main); +} + +.exposed:last-of-type { + border-bottom: none; +} + +.exposed img { + border-radius: 0.5em; + margin-bottom: 1em; +} + +.exposed h3 { + font-size: 2em; + font-weight: 400; + letter-spacing: -0.025em; + line-height: 1; + margin-top: 0; +} + +.exposed .author-row { + font-size: 0.8em; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2em; +} + +.exposed:last-of-type .author-row { + padding-bottom: 0; +} + +.exposed .author-row time { + padding: 0; + background: transparent; + color: var(--text-muted); +} + +.exposed .author { + display: flex; + align-items: center; + color: var(--text-muted); + font-weight: 500; + gap: 0.5em; +} + +.exposed .author .avatar { + overflow: hidden; + border-radius: 0.25em; + width: 1.5625em; + height: 1.5625em; + object-fit: cover; +} From 7361a56cd47aae43e2a11d2654af39c2b2d478ec Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 20 Sep 2024 00:38:53 -0400 Subject: [PATCH 104/157] pull group ref out from pasted deeplinks and lure links --- packages/shared/src/logic/branch.ts | 28 ++++ packages/shared/src/logic/deeplinks.ts | 25 ++++ packages/shared/src/logic/index.ts | 1 + .../ui/src/components/MessageInput/helpers.ts | 96 +++++++++++++ .../components/MessageInput/index.native.tsx | 136 +++++++++--------- .../ui/src/components/MessageInput/index.tsx | 2 + 6 files changed, 224 insertions(+), 64 deletions(-) create mode 100644 packages/shared/src/logic/deeplinks.ts create mode 100644 packages/ui/src/components/MessageInput/helpers.ts diff --git a/packages/shared/src/logic/branch.ts b/packages/shared/src/logic/branch.ts index c39d18967b..2ef1067b97 100644 --- a/packages/shared/src/logic/branch.ts +++ b/packages/shared/src/logic/branch.ts @@ -26,6 +26,26 @@ export const getDeepLink = async ( return url; }; +export const getBranchLinkMeta = async ( + branchUrl: string, + branchKey: string +) => { + const params = new URLSearchParams(); + params.set('url', branchUrl); + params.set('branch_key', branchKey); + const response = await fetchBranchApi(`/v1/url?${params}`); + if (!response.ok) { + return undefined; + } + + const payload = await response.json(); + if (!payload || !payload.data) { + return undefined; + } + + return payload.data; +}; + export type DeepLinkType = 'lure' | 'wer'; export interface DeepLinkMetadata { @@ -62,6 +82,14 @@ export function extractLureMetadata(branchParams: any) { }; } +export function isLureMeta(input: unknown): input is DeepLinkMetadata { + if (!input || typeof input !== 'object') { + return false; + } + + return 'invitedGroupId' in input; +} + export async function getDmLink( ship: string, branchDomain: string, diff --git a/packages/shared/src/logic/deeplinks.ts b/packages/shared/src/logic/deeplinks.ts new file mode 100644 index 0000000000..e5ddad05c4 --- /dev/null +++ b/packages/shared/src/logic/deeplinks.ts @@ -0,0 +1,25 @@ +import { ContentReference } from '../api'; +import { citeToPath } from '../urbit'; +import { getBranchLinkMeta, isLureMeta } from './branch'; + +export async function getReferenceFromDeeplink( + url: string, + branchKey: string +): Promise<{ reference: ContentReference; path: string } | null> { + const linkMeta = await getBranchLinkMeta(url, branchKey); + + if (linkMeta && typeof linkMeta === 'object') { + if (isLureMeta(linkMeta) && linkMeta.invitedGroupId) { + return { + reference: { + type: 'reference', + referenceType: 'group', + groupId: linkMeta.invitedGroupId, + }, + path: citeToPath({ group: linkMeta.invitedGroupId }), + }; + } + } + + return null; +} diff --git a/packages/shared/src/logic/index.ts b/packages/shared/src/logic/index.ts index a0d0fcab1e..bf136709b7 100644 --- a/packages/shared/src/logic/index.ts +++ b/packages/shared/src/logic/index.ts @@ -5,3 +5,4 @@ export * from './embed'; export * from './types'; export * from './activity'; export * from './branch'; +export * from './deeplinks'; diff --git a/packages/ui/src/components/MessageInput/helpers.ts b/packages/ui/src/components/MessageInput/helpers.ts new file mode 100644 index 0000000000..bacbe015bf --- /dev/null +++ b/packages/ui/src/components/MessageInput/helpers.ts @@ -0,0 +1,96 @@ +import { EditorBridge } from '@10play/tentap-editor'; +import { createDevLogger, tiptap } from '@tloncorp/shared/dist'; +import { + Block, + Inline, + constructStory, + isInline, +} from '@tloncorp/shared/dist/urbit'; + +import { Attachment } from '../../contexts'; + +const logger = createDevLogger('processReference', true); + +export async function processReferenceAndUpdateEditor({ + editor, + pastedText, + matchRegex, + processMatch, +}: { + editor: EditorBridge; + pastedText: string; + matchRegex: RegExp; + processMatch: (match: string) => Promise; +}): Promise { + try { + logger.log('checking against', matchRegex); + const match = pastedText.match(matchRegex); + + if (match) { + logger.log('found match', match[0]); + const attachment = await processMatch(match[0]); + + if (attachment) { + logger.log('extracted attachment', attachment); + + // remove the attachments corresponding text from the editor + const json = await editor.getJSON(); + const filteredJson = filterRegexFromJson(json, matchRegex); + + logger.log(`updating editor`, filteredJson); + // @ts-expect-error setContent does accept JSONContent + editor.setContent(filteredJson); + + return attachment; + } + } + } catch (e) { + logger.error('error processing reference', e); + } + + return null; +} + +// check editor JSON for text or link matches and remove them +function filterRegexFromJson(initialJson: object, matchRegex: RegExp): object { + const allContent = tiptap.JSONToInlines(initialJson); + + const filteredContent = allContent + .map((content) => { + if (typeof content === 'string') { + return content.replace(matchRegex, ''); + } else if (isInline(content) && 'link' in content) { + if (content.link?.href && content.link?.href.match(matchRegex)) { + return null; + } + } + return content; + }) + .filter((content) => content !== null && content !== '') as ( + | Inline + | Block + )[]; + + const filteredInlines = filteredContent.filter( + (c) => typeof c === 'string' || (typeof c === 'object' && isInline(c)) + ) as Inline[]; + + const filteredBlocks = (filteredContent.filter( + (c) => typeof c !== 'string' && 'block' in c + ) || []) as unknown as Block[]; + + // it looks like construct story can handle blocks. Do we have to push them separately? + const newStory = constructStory(filteredInlines); + + if (filteredBlocks && filteredBlocks.length > 0) { + newStory.push( + ...filteredBlocks.map((block) => ({ + block: block, + })) + ); + } + + const newJson = tiptap.diaryMixedToJSON(newStory); + + return newJson; +} diff --git a/packages/ui/src/components/MessageInput/index.native.tsx b/packages/ui/src/components/MessageInput/index.native.tsx index 3901e48edc..bc87fd0e64 100644 --- a/packages/ui/src/components/MessageInput/index.native.tsx +++ b/packages/ui/src/components/MessageInput/index.native.tsx @@ -27,7 +27,6 @@ import { import * as db from '@tloncorp/shared/dist/db'; import { Block, - Image, Inline, JSONContent, Story, @@ -36,6 +35,7 @@ import { isInline, pathToCite, } from '@tloncorp/shared/dist/urbit'; +import * as logic from '@tloncorp/shared/src/logic'; import { forwardRef, useCallback, @@ -51,6 +51,7 @@ import type { WebViewMessageEvent } from 'react-native-webview'; import { YStack, getToken, useWindowDimensions } from 'tamagui'; import { XStack } from 'tamagui'; +import { useBranchDomain, useBranchKey } from '../../contexts'; import { Attachment, UploadedImageAttachment, @@ -58,6 +59,7 @@ import { } from '../../contexts/attachment'; import { AttachmentPreviewList } from './AttachmentPreviewList'; import { MessageInputContainer, MessageInputProps } from './MessageInputBase'; +import { processReferenceAndUpdateEditor } from './helpers'; const messageInputLogger = createDevLogger('MessageInput', false); @@ -149,6 +151,8 @@ export const MessageInput = forwardRef( }, })); + const branchDomain = useBranchDomain(); + const branchKey = useBranchKey(); const [isSending, setIsSending] = useState(false); const [hasSetInitialContent, setHasSetInitialContent] = useState(false); const [editorCrashed, setEditorCrashed] = useState(); @@ -396,75 +400,79 @@ export const MessageInput = forwardRef( const handlePaste = useCallback( async (pastedText: string) => { - if (pastedText) { - const isRef = pastedText.match(tiptap.REF_REGEX); - - if (isRef) { - const cite = pathToCite(isRef[0]); - + // check for ref from pasted cite paths + const citePathAttachment = await processReferenceAndUpdateEditor({ + editor, + pastedText, + matchRegex: tiptap.REF_REGEX, + processMatch: async (match) => { + const cite = pathToCite(match); if (cite) { const reference = toContentReference(cite); - if (reference) { - addAttachment({ - type: 'reference', - reference, - path: isRef[0], - }); - } - - const json = await editor.getJSON(); - const inlines = tiptap - .JSONToInlines(json) - .filter( - (c) => - typeof c === 'string' || - (typeof c === 'object' && isInline(c)) - ) as Inline[]; - const blocks = - tiptap - .JSONToInlines(json) - .filter((c) => typeof c !== 'string' && 'block' in c) || []; - - // then we need to find all the inlines without refs - // so we can render the input text without refs - const inlinesWithOutRefs = inlines - .map((inline) => { - if (typeof inline === 'string') { - const inlineLength = inline.length; - const refLength = - inline.match(tiptap.REF_REGEX)?.[0].length || 0; - - if (inlineLength === refLength) { - return null; - } - - return inline.replace(tiptap.REF_REGEX, ''); - } - return inline; - }) - .filter((inline) => inline !== null) as string[]; - - // we construct a story here so we can insert blocks back in - // and then convert it back to tiptap's JSON format - const newStory = constructStory(inlinesWithOutRefs); - - if (blocks && blocks.length > 0) { - newStory.push( - ...blocks.map((block) => ({ - block: block as unknown as Block, - })) - ); - } + return reference + ? { type: 'reference', reference, path: match } + : null; + } + return null; + }, + }); + if (citePathAttachment) { + addAttachment(citePathAttachment); + } - const newJson = tiptap.diaryMixedToJSON(newStory); + // check for refs from pasted deeplinks + const DEEPLINK_REGEX = new RegExp(`^(https?://)?${branchDomain}/\\S+$`); + const deepLinkAttachment = await processReferenceAndUpdateEditor({ + editor, + pastedText, + matchRegex: DEEPLINK_REGEX, + processMatch: async (deeplink) => { + const deeplinkRef = await logic.getReferenceFromDeeplink( + deeplink, + branchKey + ); + return deeplinkRef + ? { + type: 'reference', + reference: deeplinkRef.reference, + path: deeplinkRef.path, + } + : null; + }, + }); + if (deepLinkAttachment) { + addAttachment(deepLinkAttachment); + } - // @ts-expect-error setContent does accept JSONContent - editor.setContent(newJson); - } - } + // check for refs from pasted lure links (after fallback redirect) + const TLON_LURE_REGEX = + /^(https?:\/\/)?(tlon\.network\/lure\/)(0v[^/]+)$/; + const lureLinkAttachment = await processReferenceAndUpdateEditor({ + editor, + pastedText, + matchRegex: TLON_LURE_REGEX, + processMatch: async (tlonLure) => { + const parts = tlonLure.split('/'); + const token = parts[parts.length - 1]; + if (!token) return null; + const deeplinkRef = await logic.getReferenceFromDeeplink( + `https://${branchDomain}/${token}`, + branchKey + ); + return deeplinkRef + ? { + type: 'reference', + reference: deeplinkRef.reference, + path: deeplinkRef.path, + } + : null; + }, + }); + if (lureLinkAttachment) { + addAttachment(lureLinkAttachment); } }, - [editor, addAttachment] + [branchDomain, branchKey, addAttachment, editor] ); const onSelectMention = useCallback( diff --git a/packages/ui/src/components/MessageInput/index.tsx b/packages/ui/src/components/MessageInput/index.tsx index 194da5e821..702fd0223f 100644 --- a/packages/ui/src/components/MessageInput/index.tsx +++ b/packages/ui/src/components/MessageInput/index.tsx @@ -274,6 +274,8 @@ export function MessageInput({ storeDraft(json); }); + // TODO: Looks like paste isn't handled on web yet? When adding support, check + // against the corresponding native implementaion const handlePaste = useCallback( async (pastedText: string) => { if (!editor) { From 071de1ab1eca2b1c15f85d15e5e50aeca3b225b6 Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 06:18:58 -0700 Subject: [PATCH 105/157] expose: don't render links inside widget previews Apparently nesting tags is illegal, and browsers will actively malform your document tree to avoid having to render such cases. To avoid that, we add an optional flag to the +en-manx:channel-utils core that can be set to false to disable the rendering of anchor elements. For images, they won't be wrapped inside an tag at all. For text links, we wrap them in a span.faux-a instead, so that they may still be styled to look similar to real anchors. --- desk/app/expose.hoon | 8 +++++--- desk/lib/channel-utils.hoon | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 37cbb55b36..f39f234c94 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -309,8 +309,9 @@ ;div.exposed ;div.content ;a.chat/"/expose{link}" - ;* (story:en-manx:u content) - == + ;* %. content + =<(story(anchors |) en-manx:u) + == == ;div.author-row ;+ (author-node:r [our now] author) @@ -341,7 +342,8 @@ ;div.exposed ;div.content ;a.heap/"/expose{link}" - ;* (story:en-manx:u content) + ;* %. content + =<(story(anchors |) en-manx:u) == == ;div.author-row diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index a95b9f5e78..8e3c387262 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -455,6 +455,10 @@ == :: ++ en-manx ::NOTE more commonly, marl, but that's just (list manx) + :: anchors: whether to render tags around images and links. + :: you may not want this when nesting the content inside of another + :: , browsers consider this illegal and will mangle the document. + =/ anchors=? & |% ++ content story ++ story @@ -485,11 +489,15 @@ ;+ =/ src=tape (trip src.block) ;div.image - ;a/"{src}"(target "_blank", rel "noreferrer") + ;+ + =/ img ;img@"{src}" =height "{(a-co:co height.block)}" =width "{(a-co:co width.block)}" =alt "{?:(=('' alt.block) "image" (trip alt.block))}"; + ?. anchors img + ;a/"{src}"(target "_blank", rel "noreferrer") + ;+ img == == :: @@ -611,10 +619,15 @@ %link ::TODO prefix // if no protocol in url =/ url=tape (trip p.inline) + =/ txt=tape ?:(=('' q.inline) url (trip q.inline)) + ?. anchors + ;span.faux-a:"{txt}" ;a/"{url}" =target "_blank" =rel "noreferrer" - ; "{?:(=('' q.inline) url (trip q.inline))}" + ::NOTE sail inserts trailing \0a if we do ; {txt}, + :: which looks bad when links get underlined + ;+ [%$ [%$ "{txt}"]~]~ == :: %task From 44b7dc44c9bff8fa26eb4baca586851e2dcc0040 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 20 Sep 2024 09:21:59 -0400 Subject: [PATCH 106/157] better name + comment for auto-join gating --- apps/tlon-mobile/src/hooks/useDeepLinkListener.ts | 2 +- packages/app/contexts/branch.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index f3a4fc94b2..e9c576a7cf 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -23,7 +23,7 @@ export const useDeepLinkListener = () => { (async () => { isHandlingLinkRef.current = true; // if the lure was clicked prior to authenticating, trigger the automatic join & DM - if (lure.clickedPreAuth) { + if (lure.shouldAutoJoin) { try { logger.log(`inviting ship with lure`, ship, signupParams.lureId); await inviteShipWithLure({ ship, lure: signupParams.lureId }); diff --git a/packages/app/contexts/branch.tsx b/packages/app/contexts/branch.tsx index 7f0f953ce3..aeffda7a9f 100644 --- a/packages/app/contexts/branch.tsx +++ b/packages/app/contexts/branch.tsx @@ -17,7 +17,7 @@ import { useShip } from './ship'; interface LureData extends DeepLinkMetadata { id: string; - clickedPreAuth: boolean; + shouldAutoJoin: boolean; } type Lure = { @@ -123,7 +123,8 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { lure: { ...extractLureMetadata(params), id: params.lure as string, - clickedPreAuth: Boolean(ship), + // if not already authenticated, we should run Lure's invite auto-join capability after signing in + shouldAutoJoin: Boolean(!ship), }, priorityToken: params.token as string | undefined, }; From 764cc2d7921a191371e0e6532dc6f59d0477fa2e Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 20 Sep 2024 09:25:40 -0400 Subject: [PATCH 107/157] use existing syncGroupPreviews method instead of recreating --- .../tlon-mobile/src/hooks/useDeepLinkListener.ts | 4 +++- packages/shared/src/store/dbHooks.ts | 16 +++++----------- packages/shared/src/store/groupActions.ts | 11 ----------- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index e9c576a7cf..dc18e972aa 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -36,7 +36,9 @@ export const useDeepLinkListener = () => { } else { // otherwise, treat it as a deeplink and navigate to the group if (lure.invitedGroupId) { - const group = await store.getGroupPreview(lure.invitedGroupId); + const [group] = await store.syncGroupPreviews([ + lure.invitedGroupId, + ]); if (group) { navigation.reset({ index: 1, diff --git a/packages/shared/src/store/dbHooks.ts b/packages/shared/src/store/dbHooks.ts index 21abcaf6e3..e58e2ff0c5 100644 --- a/packages/shared/src/store/dbHooks.ts +++ b/packages/shared/src/store/dbHooks.ts @@ -8,9 +8,8 @@ import { useMemo } from 'react'; import * as api from '../api'; import * as db from '../db'; import * as ub from '../urbit'; -import { getGroupPreview } from './groupActions'; import { hasCustomS3Creds, hasHostingUploadCreds } from './storage'; -import { syncPostReference } from './sync'; +import { syncGroupPreviews, syncPostReference } from './sync'; import { keyFromQueryDeps, useKeyFromQueryDeps } from './useKeyFromQueryDeps'; export * from './useChannelSearch'; @@ -313,14 +312,6 @@ export const useGroups = (options: db.GetGroupsOptions) => { }); }; -export const useGroupPreviews = (groupIds: string[]) => { - const depsKey = useKeyFromQueryDeps(db.getGroupPreviews); - return useQuery({ - queryKey: ['groupPreviews', depsKey, groupIds], - queryFn: () => db.getGroupPreviews(groupIds), - }); -}; - export const useGroup = (options: { id?: string }) => { return useQuery({ enabled: !!options.id, @@ -362,7 +353,10 @@ export const useGroupPreview = (groupId: string) => { const tableDeps = useKeyFromQueryDeps(db.getGroup); return useQuery({ queryKey: ['groupPreview', tableDeps, groupId], - queryFn: async () => getGroupPreview(groupId), + queryFn: async () => { + const [preview] = await syncGroupPreviews([groupId]); + return preview; + }, }); }; diff --git a/packages/shared/src/store/groupActions.ts b/packages/shared/src/store/groupActions.ts index 4832806795..e3a8ee71f9 100644 --- a/packages/shared/src/store/groupActions.ts +++ b/packages/shared/src/store/groupActions.ts @@ -50,17 +50,6 @@ export async function createGroup({ } } -export async function getGroupPreview(groupId: string) { - const group = await db.getGroup({ id: groupId }); - if (group) { - return group; - } - - const groupPreview = await api.getGroupPreview(groupId); - await db.insertUnjoinedGroups([groupPreview]); - return groupPreview; -} - export async function acceptGroupInvitation(group: db.Group) { logger.log('accepting group invitation', group.id); await db.updateGroup({ id: group.id, joinStatus: 'joining' }); From 0841e714b19de41db29130911534f51387e77d59 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 20 Sep 2024 10:31:53 -0400 Subject: [PATCH 108/157] expose: minor style tweaks --- desk/app/expose/style/page.css | 9 ++++++++- desk/app/expose/style/shared.css | 9 ++++----- desk/app/expose/style/widget.css | 11 ++++++----- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index dd31c71ebb..b7ea19ed7f 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -17,6 +17,10 @@ background: var(--background); } +.expose-content > header { + margin-bottom: 2em; +} + .expose-content p:empty { display: none; } @@ -39,10 +43,13 @@ } .author-row { + width: 100%; + font-size: 0.8em; + margin-left: -2.5rem; display: flex; align-items: center; justify-content: space-between; - padding: 0 0 0.75em; + padding: 0 2.5em 2em; border-bottom: 0.125em solid var(--border); gap: 2em; } diff --git a/desk/app/expose/style/shared.css b/desk/app/expose/style/shared.css index 0ff5c94319..7ba932b30b 100644 --- a/desk/app/expose/style/shared.css +++ b/desk/app/expose/style/shared.css @@ -1372,14 +1372,13 @@ dialog::backdrop { right: 1em; bottom: 1em; padding: 0.5em 0.75em; - border: 0.125em solid var(--border); + border: 0.125em solid #e5e5e5; border-radius: 0.5em; - background: var(--background); + background: #fff; box-shadow: - 0 0.125em 0.25em var(--selection), - 0 0 1em var(--selection); + 0 0.125em 0.25em rgb(24 24 24 / 8%), + 0 0 1em rgb(24 24 24 / 8%); font-size: 0.75em; - background: #fff; } @media screen and (min-width: 960px) { diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index 7d0d9b6882..e15da2f88f 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -10,9 +10,9 @@ border-bottom: 0.125em solid var(--border); } -/* .exposed:hover { - background: var(--selection); -} */ +.exposed:last-of-type { + border-bottom: none; +} .exposed a { text-decoration: none; @@ -30,8 +30,9 @@ color: var(--text-main); } -.exposed:last-of-type { - border-bottom: none; +.exposed .faux-a { + text-decoration: underline; + color: var(--links); } .exposed img { From d8a718043f1362661e3c7bf17de41d3b46d593a3 Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 06:50:40 -0700 Subject: [PATCH 109/157] expose, profile: move http-utils into lib This was shared code, and quite standalone anyway, so is probably more at home in its own lib file. Somewhat redundant with /lib/server, but more tailored to our current usage patterns. --- desk/app/expose.hoon | 47 +--------------------------- desk/app/profile.hoon | 72 ++++++------------------------------------- 2 files changed, 10 insertions(+), 109 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index f39f234c94..79088a7be4 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -6,7 +6,7 @@ :: /expose/that/reference/as/copied/123456789 :: /- c=cite, d=channels, co=contacts -/+ sigil, u=channel-utils, +/+ sigil, u=channel-utils, hutils=http-utils, dbug, verb :: /* style-shared %css /app/expose/style/shared/css @@ -367,51 +367,6 @@ ?. ?=([[@ ^] *] u.for) ~ `con.for.u.for -:: -++ hutils :: http request utils - ::NOTE most of the below are also available in /lib/server, but we - :: reimplement them here for independence's sake - |% - +$ order [id=@ta inbound-request:eyre] - +$ query [trail args=(list [key=@t value=@t])] - +$ trail [ext=(unit @ta) site=(list @t)] - +$ reply - $% [%page bod=manx] :: html page - [%xtra hed=header-list:http bod=manx] :: html page w/ heads - == - :: - ++ purse :: url cord to query - |= url=@t - ^- query - (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) - :: - ++ press :: manx to octs - (cork en-xml:html as-octt:mimes:html) - :: - ++ paint :: render response into payload - |= =reply - ^- simple-payload:http - ?- -.reply - %page [[200 ['content-type' 'text/html']~] `(press bod.reply)] - %xtra =? hed.reply ?=(~ (get-header:http 'content-type' hed.reply)) - ['content-type'^'text/html' hed.reply] - [[200 hed.reply] `(press bod.reply)] - == - :: - ++ spout :: build full response cards - |= [eyre-id=@ta simple-payload:http] - ^- (list card) - =/ =path /http-response/[eyre-id] - :~ [%give %fact ~[path] [%http-response-header !>(response-header)]] - [%give %fact ~[path] [%http-response-data !>(data)]] - [%give %kick ~[path] ~] - == - :: - ++ store :: set cache entry - |= [url=@t entry=(unit cache-entry:eyre)] - ^- card - [%pass /eyre/cache %arvo %e %set-response url entry] - -- -- :: %- agent:dbug diff --git a/desk/app/profile.hoon b/desk/app/profile.hoon index eca01acc83..d480c71662 100644 --- a/desk/app/profile.hoon +++ b/desk/app/profile.hoon @@ -6,7 +6,7 @@ :: can choose which widgets to display on their public page. :: /- contacts -/+ dbug, verb, sigil +/+ dbug, verb, sigil, hutils=http-utils /= stock-widgets /app/profile/widgets :: /* style-shared %css /app/expose/style/shared/css @@ -128,20 +128,20 @@ == :: ++ serve - |= order:rudder + |= order:hutils ^- (list card) =; payload=simple-payload:http - ?. =('/profile' url.request) (spout:rudder id payload) + ?. =('/profile' url.request) (spout:hutils id payload) :: if we got requested the profile page, that means it's not in cache. :: serve the response, but also add it into cache. :: - :- [%pass /eyre/cache %arvo %e %set-response '/profile' `[| %payload payload]] - (spout:rudder id payload) + :- (store:hutils '/profile' `[| %payload payload]) + (spout:hutils id payload) ?: =('/profile/style/page.css' url.request) :- [200 ['content-type' 'text/css']~] `(as-octs:mimes:html style-page) - %- paint:rudder - =/ =query:rudder (purse:rudder url.request) + %- paint:hutils + =/ =query:hutils (purse:hutils url.request) :: if the request is not for /profile, we redirect to landscape :: ?. ?=([%profile ~] site.query) @@ -152,7 +152,7 @@ ^- (list card) ?. bound ~ =/ payload=simple-payload:http - (paint:rudder %page render-page) + (paint:hutils %page render-page) [%pass /eyre/cache %arvo %e %set-response '/profile' `[| %payload payload]]~ :: ++ update-group-widgets @@ -249,60 +249,6 @@ == == -- -:: -++ rudder :: http request utils - ::NOTE most of the below are also available in /lib/server, but we - :: reimplement them here for independence's sake - |% - +$ order [id=@ta inbound-request:eyre] - +$ query [trail args=(list [key=@t value=@t])] - +$ trail [ext=(unit @ta) site=(list @t)] - +$ reply - $% [%page bod=manx] :: html page - [%xtra hed=header-list:http bod=manx] :: html page w/ heads - [%next loc=@t msg=@t] :: 303, succeeded - [%move loc=@t] :: 308, use other - [%auth loc=@t] :: 307, please log in - == - :: - ++ purse :: url cord to query - |= url=@t - ^- query - (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) - :: - ++ press :: manx to octs - (cork en-xml:html as-octt:mimes:html) - :: - ++ paint :: render response into payload - |= =reply - ^- simple-payload:http - ?- -.reply - %page [[200 ['content-type' 'text/html']~] `(press bod.reply)] - %xtra =? hed.reply ?=(~ (get-header:http 'content-type' hed.reply)) - ['content-type'^'text/html' hed.reply] - [[200 hed.reply] `(press bod.reply)] - %next =; loc [[303 ['location' loc]~] ~] - ?~ msg.reply loc.reply - %+ rap 3 - :~ loc.reply - ?:(?=(^ (find "?" (trip loc.reply))) '&' '?') - 'rmsg=' - (crip (en-urlt:html (trip msg.reply))) - == - %move [[308 ['location' loc.reply]~] ~] - %auth =/ loc (crip (en-urlt:html (trip loc.reply))) - [[307 ['location' (cat 3 '/~/login?redirect=' loc)]~] ~] - == - :: - ++ spout :: build full response cards - |= [eyre-id=@ta simple-payload:http] - ^- (list card) - =/ =path /http-response/[eyre-id] - :~ [%give %fact ~[path] [%http-response-header !>(response-header)]] - [%give %fact ~[path] [%http-response-data !>(data)]] - [%give %kick ~[path] ~] - == - -- -- :: %- agent:dbug @@ -372,7 +318,7 @@ :: %handle-http-request :_ this - (serve:do !<(order:rudder:do vase)) + (serve:do !<(order:hutils:do vase)) :: %egg-any =+ !<(=egg-any:gall vase) From 9a31daf70efca9adab0a6c1230fc79f534396f48 Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 07:59:34 -0700 Subject: [PATCH 110/157] expose: move rendering logic & utils out Rendering logic goes into dedicated rendering files under /app/expose. Some utils relating to contacts and posts go into their respective existing library files. --- desk/app/expose.hoon | 362 ++---------------------------------- desk/app/expose/page.hoon | 153 +++++++++++++++ desk/app/expose/render.hoon | 79 ++++++++ desk/app/expose/widget.hoon | 85 +++++++++ desk/lib/channel-utils.hoon | 25 ++- desk/sur/contacts.hoon | 13 ++ 6 files changed, 371 insertions(+), 346 deletions(-) create mode 100644 desk/app/expose/page.hoon create mode 100644 desk/app/expose/render.hoon create mode 100644 desk/app/expose/widget.hoon diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 79088a7be4..6a259be369 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -6,12 +6,12 @@ :: /expose/that/reference/as/copied/123456789 :: /- c=cite, d=channels, co=contacts -/+ sigil, u=channel-utils, hutils=http-utils, +/+ u=channel-utils, hutils=http-utils, dbug, verb :: +/= page /app/expose/page +/= widget /app/expose/widget /* style-shared %css /app/expose/style/shared/css -/* style-widget %css /app/expose/style/widget/css -/* style-page %css /app/expose/style/page/css :: |% +$ state-0 @@ -26,347 +26,19 @@ :: +$ card card:agent:gall :: -++ r :: generic-ish rendering utils +++ e :: expose utils |% - ++ author-node - |= [[our=@p now=@da] author=ship] - ^- manx - =/ aco=(unit contact:co) - (get-contact [our now] author) - ;div.author - ;div.avatar - ;+ - ?: &(?=(^ aco) ?=(^ avatar.u.aco) !=('' u.avatar.u.aco)) - ;img@"{(trip u.avatar.u.aco)}"(alt "Author's avatar"); - =/ val=@ux ?~(aco 0x0 color.u.aco) - =/ col=tape ((x-co:^co 6) val) - %. author - %_ sigil - size 25 - icon & - bg '#'^col - fg ?:((gth (div (roll (rip 3 val) add) 3) 180) "black" "white") ::REVIEW - == - == - :: - ;+ - =* nom ;span:"{(scow %p author)}" - ?~ aco nom - ?: =('' nickname.u.aco) nom - ;span(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" - == - :: - ++ render-datetime ::TODO date-only mode - |= =time - ^- manx - =, chrono:userlib - =; utc=tape - ::NOTE timestamp-utc class and ms attr used by +time-script, - :: which replaces this rendering with the local time - ;time.timestamp-utc(ms (a-co:^co (unm time))) - ; {utc} - == - =/ =date (yore time) - |^ "{(snag (dec m.date) mon:yu)} ". - "{(num d.t.date)}{(ith d.t.date)}, ". - "{(num y.date)}, ". - "{(dum h.t.date)}:{(dum m.t.date)} (UTC)" - ++ num a-co:^co - ++ dum (d-co:^co 2) - ++ ith - |= n=@ud - ?- n - %1 "st" - %2 "nd" - %3 "rd" - ?(%11 %12 %13) "th" - :: - @ - ?: (lth n 10) "th" - $(n (mod n 10)) - == - -- - :: - ++ time-script-node - ;script(type "text/javascript"):"{(trip time-script)}" - ++ time-script - ''' - const a = document.getElementsByClassName('timestamp-utc'); - for (const e of a) { - const t = new Date(Number(e.attributes['ms'].value)); - e.innerText = t.toLocaleString('en-US', {month: 'long'}) + ' ' - + (a=>a+=[,"st","nd","rd"][a.match`1?.$`]||"th")(''+t.getDate()) + ', ' - + t.getFullYear() + ', ' - + t.toLocaleString('en-US', {hour: '2-digit', minute: '2-digit', hour12: false}); - }; - ''' - -- -:: -++ e :: expose rendering - |% - ++ render-post - |= [our=@p now=@da] - |= [=nest:g:c msg=post:d] - ^- (unit manx) - =/ aco=(unit contact:co) - (get-contact [our now] author.msg) - :: - ::TODO if we render replies then we can "unroll" whole chat threads too (: - ::TODO just key off the kind-data, no? - |^ ?+ p.nest ~ - %chat - ?> ?=(%chat -.kind-data.msg) - =/ title=tape - (trip (rap 3 (turn (first-inline:u content.msg) flatten-inline:u))) - %- some - %: build "chat" - (heads title ~) - [chat-prelude]~ - (story:en-manx:u content.msg) - == - :: - %diary - ?> ?=(%diary -.kind-data.msg) - =* kd kind-data.msg - =/ title=tape (trip title.kd) - %- some - %: build "diary" - (heads title ?:(=('' image.kd) ~ `image.kd)) - :: - ?: =('' image.kd) (diary-prelude title) - :- ;img.cover@"{(trip image.kd)}"(alt "Cover image"); - (diary-prelude title) - :: - (story:en-manx:u content.msg) - == - :: - %heap - ?> ?=(%heap -.kind-data.msg) - =/ title=tape - ?: &(?=(^ title.kind-data.msg) !=('' u.title.kind-data.msg)) - (trip u.title.kind-data.msg) - ::NOTE could flatten the first-inline, but we don't. showing that - :: as both h1 and content is strange - "" - %- some - %: build "chat" - (heads ?:(=("" title) "Gallery item" title) ~) - (heap-prelude title) - (story:en-manx:u content.msg) - == - == - :: - ++ build - |= [tag=tape hes=manx pre=marl bod=marl] - ^- manx - ;html - ;+ hes - ;body(class tag) - ;article.expose-content - ;header - ;* pre - == - ;* bod - == - ;+ badge - == - ;+ time-script-node:r - == - :: - ++ heads - |= [title=tape img=(unit @t)] - ;head - ;title:"{title}" - ;link(rel "stylesheet", href "/expose/style/shared.css"); - ;style:"{(trip style-page)}" - :: - ;meta(charset "utf-8"); - ;meta(name "viewport", content "width=device-width, initial-scale=1"); - :: - ;meta(name "robots", content "noindex, nofollow, noimageindex"); - :: - ::REVIEW make sure this is the right/new app id - ;meta(property "apple-itunes-app", content "app-id=6451392109"); - ::NOTE at the time of writing, android supports no such thing - :: - ::TODO could get even smarter about description, preview image, etc - ;meta(property "og:title", content title); - ;meta(property "twitter:title", content title); - ;meta(property "og:site_name", content "Tlon"); - ;meta(property "og:type", content "article"); - ;meta(property "og:article:author:username", content (scow %p author.msg)); - :: - ;* ?~ img - :_ ~ - ;meta(property "twitter:card", content "summary"); - =/ img=tape (trip u.img) - :~ ;meta(property "twitter:card", content "summary_large_image"); - ;meta(property "og:image", content img); - ;meta(property "twitter:image", content img); - == - :: - ;* ?~ aco ~ - ?: =('' nickname.u.aco) ~ - :_ ~ - ;meta(property "og:article:author:first_name", content (trip nickname.u.aco)); - == - :: - ++ badge - ;div.tlon-badge - ;a(href "https://tlon.io") - ;span - ; Powered by Tlon - == - == - == - :: - ++ chat-prelude - ^- manx - ;div.author-row - ;+ (author-node:r [our now] author.msg) - ;+ (render-datetime:r sent.msg) - == - :: - ++ diary-prelude - |= title=tape - ^- marl - :~ ;h1:"{title}" - ;div.author-row - ;+ (author-node:r [our now] author.msg) - ;+ (render-datetime:r sent.msg) - == - == - :: - ++ heap-prelude - |= title=tape - ^- marl - =- ?: =("" title) [-]~ - :- ;h1:"{title}" - [-]~ - ;div.author-row - ;+ (author-node:r [our now] author.msg) - ;+ (render-datetime:r sent.msg) - == - -- - :: - ++ post-from-cite - |= [our=@p now=@da ref=cite:c] - ^- (unit [=nest:g:c =post:d]) - ?. ?=(%chan -.ref) - ~ - ::TODO the whole "deconstruct the ref path" situation is horrendous - ?. ?=([?(%msg %note %curio) @ ~] wer.ref) - ~ - =, ref - =/ base=path - %+ weld - /(scot %p our)/channels/(scot %da now) - /v2/[p.nest]/(scot %p p.q.nest)/[q.q.nest] - ?. .^(? %gu base) ~ - :+ ~ nest - .^ post:d %gx - %+ weld base - /posts/post/(scot %ud (rash i.t.wer dum:ag))/channel-post-2 - == - :: ++ update-widget - |= [[our=@p now=@da] open=(set cite:c)] + |= [=bowl:gall open=(set cite:c)] ^- (list card) - ?. .^(? %gu /(scot %p our)/profile/(scot %da now)/$) + ?. .^(? %gu /(scot %p our.bowl)/profile/(scot %da now.bowl)/$) ~ - ~> %bout.[0 'updating expose widget'] =; widget=[%0 desc=@t %marl marl] =/ =cage noun+!>([%command %update-widget %groups %expose-all widget]) - [%pass /profile/widget/all %agent [our %profile] %poke cage]~ + [%pass /profile/widget/all %agent [our.bowl %profile] %poke cage]~ :^ %0 'Publicized content' %marl - ^- marl - :: - =/ cis=(list cite:c) - ::REVIEW maybe limit to the latest n? - %+ sort ~(tap in open) - :: newest first (assumes id nr in path is a timestamp) - :: - |= [a=cite:c b=cite:c] - ?. ?=([%chan * ?(%msg %note %curio) @ *] a) | - ?. ?=([%chan * ?(%msg %note %curio) @ *] b) & - ?~ aa=(rush i.t.wer.a dum:ag) | - ?~ bb=(rush i.t.wer.b dum:ag) & - (gth u.aa u.bb) - :- ;style:"{(trip style-widget)}" - =- (snoc - time-script-node:r) - %+ murn cis - |= ref=cite:c - ^- (unit manx) - =/ pon=(unit [=nest:g:c =post:d]) - (post-from-cite our now ref) - ?~ pon ~ - %- some - =/ link=tape - (spud (print:c ref)) - =, post.u.pon - ?- -.kind-data.post.u.pon - %chat - ;div.exposed - ;div.content - ;a.chat/"/expose{link}" - ;* %. content - =<(story(anchors |) en-manx:u) - == - == - ;div.author-row - ;+ (author-node:r [our now] author) - ;+ (render-datetime:r sent) - == - == - :: - %diary - ;div.exposed - ;* ?: =('' image.kind-data) ~ - :_ ~ - ;a.diary/"/expose{link}" - ;img@"{(trip image.kind-data)}"; - == - ;a.diary/"/expose{link}" - ;h3:"{(trip title.kind-data)}" - == - ;div.author-row - ;+ (author-node:r [our now] author) - ;+ (render-datetime:r sent) - == - == - :: - %heap - ::TODO for the kinds of children the div.content gets for heap posts, - :: having an %a (grand)parent breaks the html rendering, - :: putting the inner divs outside/after the a.exposed - ;div.exposed - ;div.content - ;a.heap/"/expose{link}" - ;* %. content - =<(story(anchors |) en-manx:u) - == - == - ;div.author-row - ;+ (author-node:r [our now] author) - ;+ (render-datetime:r sent) - == - == - == + (render:widget bowl open) -- -:: -++ get-contact - |= [[our=@p now=@da] who=@p] - => [+< co=co ..zuse] :: memoization aid - ^- (unit contact:co) ~+ - =/ base=path /(scot %p our)/contacts/(scot %da now) - ?. ~+ .^(? %gu (weld base /$)) - ~ - =+ ~+ .^(rol=rolodex:co %gx (weld base /all/contact-rolodex)) - ?~ for=(~(get by rol) who) - ~ - ?. ?=([[@ ^] *] u.for) - ~ - `con.for.u.for -- :: %- agent:dbug @@ -405,14 +77,14 @@ =/ ref=cite:c (parse:c path.act) =/ msg=(unit [=nest:g:c =post:d]) - (post-from-cite:e our.bowl now.bowl ref) + (grab-post:cite:u bowl ref) ?> ?=(^ msg) =/ pag=(unit manx) - ((render-post:e [our now]:bowl) u.msg) + (render:page bowl u.msg) ?> ?=(^ pag) =. open (~(put in open) ref) :_ this - :_ (update-widget:e [our now]:bowl open) + :_ (update-widget:e bowl open) %+ store:hutils (cat 3 '/expose' (spat path.act)) `[| %payload (paint:hutils %page u.pag)] @@ -424,7 +96,7 @@ [~ this] =. open (~(del in open) ref) :_ this - :_ (update-widget:e [our now]:bowl open) + :_ (update-widget:e bowl open) %+ store:hutils (cat 3 '/expose' (spat path.act)) :^ ~ | %payload @@ -463,8 +135,8 @@ ?. (~(has in open) u.ref) ~ %+ biff - (post-from-cite:e our.bowl now.bowl u.ref) - (render-post:e [our now]:bowl) + (grab-post:cite:u bowl u.ref) + (cury render:page bowl) == :: ++ on-watch @@ -488,7 +160,7 @@ `(as-octs:mimes:html style-shared) `[| %payload [200 ['content-type' 'text/css'] ~] bod] %+ weld - (update-widget:e [our now]:bowl open) + (update-widget:e bowl open) %+ murn ~(tap in open) |= ref=cite:c ^- (unit card) ::TODO or should this remove from cache also? @@ -496,10 +168,10 @@ ::TODO reconsider. if we just remove the cache entry, we'll re-render :: on-demand instead of all-at-once, which may be slow. =/ msg=(unit [=nest:g:c =post:d]) - (post-from-cite:e our.bowl now.bowl ref) + (grab-post:cite:u bowl ref) ?~ msg ~ =/ pag=(unit manx) - ((render-post:e [our now]:bowl) u.msg) + (render:page bowl u.msg) ?~ pag ~ %- some %+ store:hutils diff --git a/desk/app/expose/page.hoon b/desk/app/expose/page.hoon new file mode 100644 index 0000000000..50f00c0a86 --- /dev/null +++ b/desk/app/expose/page.hoon @@ -0,0 +1,153 @@ +:: expose page: rendering for content pages +:: +/- c=cite, d=channels, co=contacts +/+ u=channel-utils +:: +/= r /app/expose/render +/* style-page %css /app/expose/style/page/css +:: +|% +++ render + |= [=bowl:gall =nest:g:c msg=post:d] + ^- (unit manx) + =/ aco=(unit contact:co) + (get-contact:co bowl author.msg) + :: + ::TODO if we render replies then we can "unroll" whole chat threads too (: + ::TODO just key off the kind-data, no? + |^ ?+ p.nest ~ + %chat + ?> ?=(%chat -.kind-data.msg) + =/ title=tape + (trip (rap 3 (turn (first-inline:u content.msg) flatten-inline:u))) + %- some + %: build "chat" + (heads title ~) + [chat-prelude]~ + (story:en-manx:u content.msg) + == + :: + %diary + ?> ?=(%diary -.kind-data.msg) + =* kd kind-data.msg + =/ title=tape (trip title.kd) + %- some + %: build "diary" + (heads title ?:(=('' image.kd) ~ `image.kd)) + :: + ?: =('' image.kd) (diary-prelude title) + :- ;img.cover@"{(trip image.kd)}"(alt "Cover image"); + (diary-prelude title) + :: + (story:en-manx:u content.msg) + == + :: + %heap + ?> ?=(%heap -.kind-data.msg) + =/ title=tape + ?: &(?=(^ title.kind-data.msg) !=('' u.title.kind-data.msg)) + (trip u.title.kind-data.msg) + ::NOTE could flatten the first-inline, but we don't. showing that + :: as both h1 and content is strange + "" + %- some + %: build "chat" + (heads ?:(=("" title) "Gallery item" title) ~) + (heap-prelude title) + (story:en-manx:u content.msg) + == + == + :: + ++ build + |= [tag=tape hes=manx pre=marl bod=marl] + ^- manx + ;html + ;+ hes + ;body(class tag) + ;article.expose-content + ;header + ;* pre + == + ;* bod + == + ;+ badge + == + ;+ time-script-node:r + == + :: + ++ heads + |= [title=tape img=(unit @t)] + ;head + ;title:"{title}" + ;link(rel "stylesheet", href "/expose/style/shared.css"); + ;style:"{(trip style-page)}" + :: + ;meta(charset "utf-8"); + ;meta(name "viewport", content "width=device-width, initial-scale=1"); + :: + ;meta(name "robots", content "noindex, nofollow, noimageindex"); + :: + ::REVIEW make sure this is the right/new app id + ;meta(property "apple-itunes-app", content "app-id=6451392109"); + ::NOTE at the time of writing, android supports no such thing + :: + ::TODO could get even smarter about description, preview image, etc + ;meta(property "og:title", content title); + ;meta(property "twitter:title", content title); + ;meta(property "og:site_name", content "Tlon"); + ;meta(property "og:type", content "article"); + ;meta(property "og:article:author:username", content (scow %p author.msg)); + :: + ;* ?~ img + :_ ~ + ;meta(property "twitter:card", content "summary"); + =/ img=tape (trip u.img) + :~ ;meta(property "twitter:card", content "summary_large_image"); + ;meta(property "og:image", content img); + ;meta(property "twitter:image", content img); + == + :: + ;* ?~ aco ~ + ?: =('' nickname.u.aco) ~ + :_ ~ + ;meta(property "og:article:author:first_name", content (trip nickname.u.aco)); + == + :: + ++ badge + ;div.tlon-badge + ;a(href "https://tlon.io") + ;span + ; Powered by Tlon + == + == + == + :: + ++ chat-prelude + ^- manx + ;div.author-row + ;+ (author:r bowl author.msg) + ;+ (datetime:r sent.msg) + == + :: + ++ diary-prelude + |= title=tape + ^- marl + :~ ;h1:"{title}" + ;div.author-row + ;+ (author:r bowl author.msg) + ;+ (datetime:r sent.msg) + == + == + :: + ++ heap-prelude + |= title=tape + ^- marl + =- ?: =("" title) [-]~ + :- ;h1:"{title}" + [-]~ + ;div.author-row + ;+ (author:r bowl author.msg) + ;+ (datetime:r sent.msg) + == + -- +-- diff --git a/desk/app/expose/render.hoon b/desk/app/expose/render.hoon new file mode 100644 index 0000000000..4b244f19ef --- /dev/null +++ b/desk/app/expose/render.hoon @@ -0,0 +1,79 @@ +:: expose render: rendering utilities for pages & the widget +:: +/- co=contacts +/+ sigil +:: +|% +++ author + |= [=bowl:gall author=ship] + ^- manx + =/ aco=(unit contact:co) + (get-contact:co bowl author) + ;div.author + ;div.avatar + ;+ + ?: &(?=(^ aco) ?=(^ avatar.u.aco) !=('' u.avatar.u.aco)) + ;img@"{(trip u.avatar.u.aco)}"(alt "Author's avatar"); + =/ val=@ux ?~(aco 0x0 color.u.aco) + =/ col=tape ((x-co:^co 6) val) + %. author + %_ sigil + size 25 + icon & + bg '#'^col + fg ?:((gth (div (roll (rip 3 val) add) 3) 180) "black" "white") ::REVIEW + == + == + :: + ;+ + =* nom ;span:"{(scow %p author)}" + ?~ aco nom + ?: =('' nickname.u.aco) nom + ;span(title "{(scow %p author)}"):"{(trip nickname.u.aco)}" + == +:: +++ datetime ::TODO date-only mode + |= =time + ^- manx + =, chrono:userlib + =; utc=tape + ::NOTE timestamp-utc class and ms attr used by +time-script, + :: which replaces this rendering with the local time + ;time.timestamp-utc(ms (a-co:^co (unm time))) + ; {utc} + == + =/ =date (yore time) + |^ "{(snag (dec m.date) mon:yu)} ". + "{(num d.t.date)}{(ith d.t.date)}, ". + "{(num y.date)}, ". + "{(dum h.t.date)}:{(dum m.t.date)} (UTC)" + ++ num a-co:^co + ++ dum (d-co:^co 2) + ++ ith + |= n=@ud + ?- n + %1 "st" + %2 "nd" + %3 "rd" + ?(%11 %12 %13) "th" + :: + @ + ?: (lth n 10) "th" + $(n (mod n 10)) + == + -- +:: +++ time-script-node + ;script(type "text/javascript"):"{(trip time-script)}" +++ time-script + ''' + const a = document.getElementsByClassName('timestamp-utc'); + for (const e of a) { + const t = new Date(Number(e.attributes['ms'].value)); + e.innerText = t.toLocaleString('en-US', {month: 'long'}) + ' ' + + (a=>a+=[,"st","nd","rd"][a.match`1?.$`]||"th")(''+t.getDate()) + ', ' + + t.getFullYear() + ', ' + + t.toLocaleString('en-US', {hour: '2-digit', minute: '2-digit', hour12: false}); + }; + ''' +-- diff --git a/desk/app/expose/widget.hoon b/desk/app/expose/widget.hoon new file mode 100644 index 0000000000..ac4ae77273 --- /dev/null +++ b/desk/app/expose/widget.hoon @@ -0,0 +1,85 @@ +:: expose widget: rendering logic for the public profile widget +:: +/- c=cite, d=channels +/+ u=channel-utils +:: +/= r /app/expose/render +/* style-widget %css /app/expose/style/widget/css +:: +|% +++ render + |= [=bowl:gall open=(set cite:c)] + ^- marl + =/ cis=(list cite:c) + ::REVIEW maybe limit to the latest n? + %+ sort ~(tap in open) + :: newest first (assumes id nr in path is a timestamp) + :: + |= [a=cite:c b=cite:c] + ?. ?=([%chan * ?(%msg %note %curio) @ *] a) | + ?. ?=([%chan * ?(%msg %note %curio) @ *] b) & + ?~ aa=(rush i.t.wer.a dum:ag) | + ?~ bb=(rush i.t.wer.b dum:ag) & + (gth u.aa u.bb) + :- ;style:"{(trip style-widget)}" + =- (snoc - time-script-node:r) + %+ murn cis + |= ref=cite:c + ^- (unit manx) + =/ pon=(unit [=nest:g:c =post:d]) + (grab-post:cite:u bowl ref) + ?~ pon ~ + %- some + =/ link=tape + (spud (print:c ref)) + =, post.u.pon + ?- -.kind-data.post.u.pon + %chat + ;div.exposed + ;div.content + ;a.chat/"/expose{link}" + ;* %. content + =<(story(anchors |) en-manx:u) + == + == + ;div.author-row + ;+ (author:r bowl author) + ;+ (datetime:r sent) + == + == + :: + %diary + ;div.exposed + ;* ?: =('' image.kind-data) ~ + :_ ~ + ;a.diary/"/expose{link}" + ;img@"{(trip image.kind-data)}"; + == + ;a.diary/"/expose{link}" + ;h3:"{(trip title.kind-data)}" + == + ;div.author-row + ;+ (author:r bowl author) + ;+ (datetime:r sent) + == + == + :: + %heap + ::TODO for the kinds of children the div.content gets for heap posts, + :: having an %a (grand)parent breaks the html rendering, + :: putting the inner divs outside/after the a.exposed + ~& [%heap-content content] + ;div.exposed + ;div.content + ;a.heap/"/expose{link}" + ;* %. content + =<(story(anchors |) en-manx:u) + == + == + ;div.author-row + ;+ (author:r bowl author) + ;+ (datetime:r sent) + == + == + == +-- diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index 8e3c387262..498e48ad76 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -1,4 +1,4 @@ -/- c=channels, g=groups +/- c=channels, g=groups, ci=cite :: convert a post to a preview for a "said" response :: |% @@ -417,6 +417,29 @@ (test her nest) -- :: +++ cite + |% + ++ grab-post + |= [=bowl:gall ref=cite:ci] + ^- (unit [=nest:g =post:c]) + ?. ?=(%chan -.ref) + ~ + ::TODO the whole "deconstruct the ref path" situation is horrendous + ?. ?=([?(%msg %note %curio) @ ~] wer.ref) + ~ + =, ref + =/ base=path + %+ weld + /(scot %p our.bowl)/channels/(scot %da now.bowl) + /v2/[p.nest]/(scot %p p.q.nest)/[q.q.nest] + ?. .^(? %gu base) ~ + :+ ~ nest + .^ post:c %gx + %+ weld base + /posts/post/(scot %ud (rash i.t.wer dum:ag))/channel-post-2 + == + -- +:: ++ flatten-inline |= i=inline:c ^- cord diff --git a/desk/sur/contacts.hoon b/desk/sur/contacts.hoon index d54de76701..415be0197f 100644 --- a/desk/sur/contacts.hoon +++ b/desk/sur/contacts.hoon @@ -86,4 +86,17 @@ :: +$ news :: local [who=ship con=$@(~ contact)] +:: +++ get-contact + |= [=bowl:gall who=@p] + ^- (unit contact) ~+ + =/ base=path /(scot %p our.bowl)/contacts/(scot %da now.bowl) + ?. ~+ .^(? %gu (weld base /$)) + ~ + =+ ~+ .^(rol=rolodex %gx (weld base /all/contact-rolodex)) + ?~ for=(~(get by rol) who) + ~ + ?. ?=([[@ ^] *] u.for) + ~ + `con.for.u.for -- From 1a96fef42e4b61e88fc441d53203c41baffa82be Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 08:01:53 -0700 Subject: [PATCH 111/157] expose: add heading to widget --- desk/app/expose/widget.hoon | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/desk/app/expose/widget.hoon b/desk/app/expose/widget.hoon index ac4ae77273..6a37e24cda 100644 --- a/desk/app/expose/widget.hoon +++ b/desk/app/expose/widget.hoon @@ -21,7 +21,8 @@ ?~ aa=(rush i.t.wer.a dum:ag) | ?~ bb=(rush i.t.wer.b dum:ag) & (gth u.aa u.bb) - :- ;style:"{(trip style-widget)}" + :+ ;style:"{(trip style-widget)}" + ;h2:"Published content" =- (snoc - time-script-node:r) %+ murn cis |= ref=cite:c From b880e299d5ca926dd5ac7f58ea04d2e5c353c58d Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 08:09:00 -0700 Subject: [PATCH 112/157] channel-utils: remove rfc --- desk/lib/channel-utils.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index 498e48ad76..0c6624851e 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -461,7 +461,7 @@ ?: ?=(%inline -.i.content) p.i.content ?+ -.p.i.content $(content t.content) - %header q.p.i.content ::REVIEW questionable + %header q.p.i.content :: %listing |- From f58b58b00dbd02c093ba48b44cedfe08340dd64c Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 08:18:40 -0700 Subject: [PATCH 113/157] expose: unify page prelude rendering --- desk/app/expose/page.hoon | 44 +++++++++++++-------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/desk/app/expose/page.hoon b/desk/app/expose/page.hoon index 50f00c0a86..a0af6b48ec 100644 --- a/desk/app/expose/page.hoon +++ b/desk/app/expose/page.hoon @@ -23,7 +23,7 @@ %- some %: build "chat" (heads title ~) - [chat-prelude]~ + (prelude ~) (story:en-manx:u content.msg) == :: @@ -35,9 +35,9 @@ %: build "diary" (heads title ?:(=('' image.kd) ~ `image.kd)) :: - ?: =('' image.kd) (diary-prelude title) + ?: =('' image.kd) (prelude `title) :- ;img.cover@"{(trip image.kd)}"(alt "Cover image"); - (diary-prelude title) + (prelude `title) :: (story:en-manx:u content.msg) == @@ -53,7 +53,7 @@ %- some %: build "chat" (heads ?:(=("" title) "Gallery item" title) ~) - (heap-prelude title) + (prelude `title) (story:en-manx:u content.msg) == == @@ -122,32 +122,18 @@ == == :: - ++ chat-prelude - ^- manx - ;div.author-row - ;+ (author:r bowl author.msg) - ;+ (datetime:r sent.msg) - == - :: - ++ diary-prelude - |= title=tape - ^- marl - :~ ;h1:"{title}" - ;div.author-row - ;+ (author:r bowl author.msg) - ;+ (datetime:r sent.msg) - == - == - :: - ++ heap-prelude - |= title=tape + ++ prelude + |= title=(unit tape) ^- marl - =- ?: =("" title) [-]~ - :- ;h1:"{title}" - [-]~ - ;div.author-row - ;+ (author:r bowl author.msg) - ;+ (datetime:r sent.msg) + =/ main=manx + ;div.author-row + ;+ (author:r bowl author.msg) + ;+ (datetime:r sent.msg) + == + ?~ title [main]~ + ?: =("" u.title) [main]~ + :~ ;h1:"{u.title}" + main == -- -- From 2bdd67d80eb14a580f75b74e6d4df2c9f7431707 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 20 Sep 2024 10:20:54 -0500 Subject: [PATCH 114/157] grouper: should only send DM if inviter --- desk/app/grouper.hoon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/desk/app/grouper.hoon b/desk/app/grouper.hoon index bb20fd14d0..50819467fa 100644 --- a/desk/app/grouper.hoon +++ b/desk/app/grouper.hoon @@ -135,6 +135,9 @@ ?> ?=([%bite-2 *] bite) :_ this =; caz=(list card) + ?~ inviter=(~(get by fields.metadata.bite) 'inviter') + ~&("no inviter field for token: {}" ~) + ?. =((slav %p u.inviter) our.bowl) ~ =/ wir=^wire /dm/(scot %p joiner.bite) =/ =dock [our.bowl %chat] =/ =id:c [our now]:bowl From 8360b1ea871108f745f0516d4b4a14dc99b40630 Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Fri, 20 Sep 2024 11:22:37 -0400 Subject: [PATCH 115/157] fix error when post content is null (#3945) --- .../src/components/PostContent/contentUtils.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/PostContent/contentUtils.tsx b/packages/ui/src/components/PostContent/contentUtils.tsx index 2179702b11..b008a06fe0 100644 --- a/packages/ui/src/components/PostContent/contentUtils.tsx +++ b/packages/ui/src/components/PostContent/contentUtils.tsx @@ -168,9 +168,13 @@ export function convertContent(input: unknown): PostContent { return blocks; } - const story: NonNullable = + const story: api.PostContent = typeof input === 'string' ? JSON.parse(input) : input; + if (!story) { + return blocks; + } + for (const verse of story) { if ('type' in verse && verse.type === 'reference') { blocks.push(verse); @@ -192,8 +196,13 @@ export function convertContent(input: unknown): PostContent { export function usePostContent(post: Post): BlockData[] { return useMemo(() => { - return convertContent(post.content); - }, [post.content]); + try { + return convertContent(post.content); + } catch (e) { + console.error('Failed to convert post content:', e); + return []; + } + }, [post]); } /** From 2150e37e7e800c0c8a7ad32564f6532f4c61fe5b Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 20 Sep 2024 11:30:45 -0400 Subject: [PATCH 116/157] fix dependency array for deeplink handler effect --- packages/app/contexts/branch.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/contexts/branch.tsx b/packages/app/contexts/branch.tsx index aeffda7a9f..05ac82ade5 100644 --- a/packages/app/contexts/branch.tsx +++ b/packages/app/contexts/branch.tsx @@ -104,7 +104,7 @@ export const useLureMetadata = () => { export const BranchProvider = ({ children }: { children: ReactNode }) => { const [{ deepLinkPath, lure, priorityToken }, setState] = useState(INITIAL_STATE); - const { ship } = useShip(); + const { isAuthenticated } = useShip(); useEffect(() => { console.debug('[branch] Subscribing to Branch listener'); @@ -124,7 +124,7 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { ...extractLureMetadata(params), id: params.lure as string, // if not already authenticated, we should run Lure's invite auto-join capability after signing in - shouldAutoJoin: Boolean(!ship), + shouldAutoJoin: !isAuthenticated, }, priorityToken: params.token as string | undefined, }; @@ -163,7 +163,7 @@ export const BranchProvider = ({ children }: { children: ReactNode }) => { console.debug('[branch] Unsubscribing from Branch listener'); unsubscribe(); }; - }, []); + }, [isAuthenticated]); const clearLure = useCallback(() => { console.debug('[branch] Clearing lure state'); From 2599b7e8ab473388c7026df1355c72c7e84eedcc Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 09:26:50 -0700 Subject: [PATCH 117/157] lib: actually commit http-utils Missed this in an earlier commit. --- desk/lib/http-utils.hoon | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 desk/lib/http-utils.hoon diff --git a/desk/lib/http-utils.hoon b/desk/lib/http-utils.hoon new file mode 100644 index 0000000000..fdf1de4b6b --- /dev/null +++ b/desk/lib/http-utils.hoon @@ -0,0 +1,45 @@ +:: http-utils: helpers +:: +|% ++$ order [id=@ta inbound-request:eyre] ++$ query [trail args=(list [key=@t value=@t])] ++$ trail [ext=(unit @ta) site=(list @t)] ++$ reply + $% [%page bod=manx] :: html page + [%xtra hed=header-list:http bod=manx] :: html page w/ heads + [%move loc=@t] :: 308, use other + == +:: +++ purse :: url cord to query + |= url=@t + ^- query + (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) +:: +++ press :: manx to octs + (cork en-xml:html as-octt:mimes:html) +:: +++ paint :: render response into payload + |= =reply + ^- simple-payload:http + ?- -.reply + %page [[200 ['content-type' 'text/html']~] `(press bod.reply)] + %xtra =? hed.reply ?=(~ (get-header:http 'content-type' hed.reply)) + ['content-type'^'text/html' hed.reply] + [[200 hed.reply] `(press bod.reply)] + %move [[308 ['location' loc.reply]~] ~] + == +:: +++ spout :: build full response cards + |= [eyre-id=@ta simple-payload:http] + ^- (list card:agent:gall) + =/ =path /http-response/[eyre-id] + :~ [%give %fact ~[path] [%http-response-header !>(response-header)]] + [%give %fact ~[path] [%http-response-data !>(data)]] + [%give %kick ~[path] ~] + == +:: +++ store :: set cache entry + |= [url=@t entry=(unit cache-entry:eyre)] + ^- card:agent:gall + [%pass /eyre/cache %arvo %e %set-response url entry] +-- From 7865b5e27173ecb9009429ae9ea6bb9c795ed657 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 20 Sep 2024 12:34:29 -0400 Subject: [PATCH 118/157] expose: final widget tweaks --- desk/app/expose/style/widget.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index e15da2f88f..9223d8d183 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -1,5 +1,4 @@ #groups--expose-all { - padding: 0 2em; overflow: hidden; } @@ -10,6 +9,10 @@ border-bottom: 0.125em solid var(--border); } +.exposed:first-of-type { + padding-top: 1em; +} + .exposed:last-of-type { border-bottom: none; } From 1572286c86ad331b6f47b9f53ea2cf0453619fba Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 20 Sep 2024 12:35:41 -0400 Subject: [PATCH 119/157] expose: fix padding oops --- desk/app/expose/style/widget.css | 1 + 1 file changed, 1 insertion(+) diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index 9223d8d183..572630e4e9 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -15,6 +15,7 @@ .exposed:last-of-type { border-bottom: none; + padding-bottom: 0; } .exposed a { From 00da143e98cb5723f669251eaf2a95faad96f6f9 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 20 Sep 2024 12:41:55 -0400 Subject: [PATCH 120/157] expose: fully-isomorphic page layout --- desk/app/expose/style/page.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index b7ea19ed7f..9535d6f210 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -1,8 +1,8 @@ @media screen and (min-width: 960px) { - body.chat { + body { display: flex; overflow: auto; - min-height: 100vh; + min-height: calc(100vh - 2em); flex-direction: column; align-items: center; justify-content: center; From 64c722498eef5b4bbaa97f1125c4f2f7ec09338f Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 20 Sep 2024 12:46:31 -0400 Subject: [PATCH 121/157] more defense and logging --- .../src/hooks/useDeepLinkListener.ts | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts index dc18e972aa..c995ee8669 100644 --- a/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts +++ b/apps/tlon-mobile/src/hooks/useDeepLinkListener.ts @@ -22,39 +22,46 @@ export const useDeepLinkListener = () => { if (ship && lure && !isHandlingLinkRef.current) { (async () => { isHandlingLinkRef.current = true; - // if the lure was clicked prior to authenticating, trigger the automatic join & DM - if (lure.shouldAutoJoin) { - try { - logger.log(`inviting ship with lure`, ship, signupParams.lureId); - await inviteShipWithLure({ ship, lure: signupParams.lureId }); - } catch (err) { - logger.error('Error inviting ship with lure:', err); - if (err instanceof Error) { - trackError(err); + logger.log(`handling deep link`, lure, signupParams); + try { + // if the lure was clicked prior to authenticating, trigger the automatic join & DM + if (lure.shouldAutoJoin) { + try { + logger.log(`inviting ship with lure`, ship, signupParams.lureId); + await inviteShipWithLure({ ship, lure: signupParams.lureId }); + } catch (err) { + logger.error('Error inviting ship with lure:', err); + if (err instanceof Error) { + trackError(err); + } } - } - } else { - // otherwise, treat it as a deeplink and navigate to the group - if (lure.invitedGroupId) { - const [group] = await store.syncGroupPreviews([ - lure.invitedGroupId, - ]); - if (group) { - navigation.reset({ - index: 1, - routes: [{ name: 'ChatList', params: { previewGroup: group } }], - }); - } else { - logger.error( - 'Failed to navigate to group deeplink', - lure.invitedGroupId - ); + } else { + // otherwise, treat it as a deeplink and navigate to the group + if (lure.invitedGroupId) { + const [group] = await store.syncGroupPreviews([ + lure.invitedGroupId, + ]); + if (group) { + navigation.reset({ + index: 1, + routes: [ + { name: 'ChatList', params: { previewGroup: group } }, + ], + }); + } else { + logger.error( + 'Failed to navigate to group deeplink', + lure.invitedGroupId + ); + } } } + } catch (e) { + logger.error('Failed to handle deep link', lure, e); + } finally { + clearLure(); + isHandlingLinkRef.current = false; } - - clearLure(); - isHandlingLinkRef.current = false; })(); } }, [ship, signupParams, clearLure, lure, navigation]); From 6da3a868e379aee9affd801e2e1964bec0a926a2 Mon Sep 17 00:00:00 2001 From: David Lee Date: Thu, 19 Sep 2024 21:38:52 -0700 Subject: [PATCH 122/157] Use null pagination token to signal no newer posts --- packages/shared/src/store/useChannelPosts.ts | 36 ++++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/shared/src/store/useChannelPosts.ts b/packages/shared/src/store/useChannelPosts.ts index e1129db905..577f0ee0e7 100644 --- a/packages/shared/src/store/useChannelPosts.ts +++ b/packages/shared/src/store/useChannelPosts.ts @@ -19,8 +19,17 @@ import { SyncPriority } from './syncQueue'; const postsLogger = createDevLogger('useChannelPosts', false); +type PostQueryPage = { + posts: db.Post[]; + /** + * False when a sync page reports that there are no more newer posts. + * Obviously, new posts can be made after this is set: in practice, we + * should have switched over to a subscription by then. + */ + canFetchNewerPosts: boolean; +}; type UseChannelPostsPageParams = db.GetChannelPostsOptions; -type PostQueryData = InfiniteData; +type PostQueryData = InfiniteData; type SubscriptionPost = [db.Post, string | undefined]; type UseChanelPostsParams = UseChannelPostsPageParams & { @@ -52,7 +61,7 @@ export const useChannelPosts = (options: UseChanelPostsParams) => { count: firstPageCount, } as UseChannelPostsPageParams, refetchOnMount: false, - queryFn: async (ctx): Promise => { + queryFn: async (ctx): Promise => { const queryOptions = ctx.pageParam || options; postsLogger.log('loading posts', queryOptions); // We should figure out why this is necessary. @@ -66,7 +75,7 @@ export const useChannelPosts = (options: UseChanelPostsParams) => { const cached = await db.getChannelPosts(queryOptions); if (cached?.length) { postsLogger.log('returning', cached.length, 'posts from db'); - return cached; + return { posts: cached, canFetchNewerPosts: true }; } postsLogger.log('no posts found in database, loading from api...'); @@ -84,7 +93,10 @@ export const useChannelPosts = (options: UseChanelPostsParams) => { secondResult?.length, 'posts from db after syncing from api' ); - return secondResult ?? []; + return { + posts: secondResult ?? [], + canFetchNewerPosts: res.newer != null, + }; }, queryKey, getNextPageParam: ( @@ -92,7 +104,7 @@ export const useChannelPosts = (options: UseChanelPostsParams) => { _allPages, lastPageParam ): UseChannelPostsPageParams | undefined => { - const lastPageIsEmpty = !lastPage[lastPage.length - 1]?.id; + const lastPageIsEmpty = !lastPage.posts.at(-1)?.id; if (lastPageIsEmpty) { // If we've only tried to get newer posts + that's failed, try using the // same cursor to get older posts instead. This can happen when the @@ -110,25 +122,21 @@ export const useChannelPosts = (options: UseChanelPostsParams) => { return { ...options, mode: 'older', - cursor: lastPage[lastPage.length - 1]?.id, + cursor: lastPage.posts.at(-1)?.id, }; }, getPreviousPageParam: ( firstPage, _allPages, - firstPageParam + _firstPageParam ): UseChannelPostsPageParams | undefined => { - const firstPageIsEmpty = !firstPage[0]?.id; - if ( - firstPageIsEmpty || - (firstPageParam?.mode === 'newest' && options.hasCachedNewest) - ) { + if (!firstPage.canFetchNewerPosts) { return undefined; } return { ...options, mode: 'newer', - cursor: firstPage[0]?.id, + cursor: firstPage.posts[0]?.id, }; }, }); @@ -147,7 +155,7 @@ export const useChannelPosts = (options: UseChanelPostsParams) => { useSubscriptionPostListener(handleNewPost); const rawPosts = useMemo(() => { - const queryPosts = query.data?.pages.flatMap((p) => p) ?? null; + const queryPosts = query.data?.pages.flatMap((p) => p.posts) ?? null; if (!newPosts.length || query.hasPreviousPage) { return queryPosts; } From c8f53fd286ada5034f2521229788da816e13fadc Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Fri, 20 Sep 2024 15:04:25 -0400 Subject: [PATCH 123/157] you do not have a deeplink if using fallback!! --- packages/shared/src/logic/branch.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/shared/src/logic/branch.ts b/packages/shared/src/logic/branch.ts index 2ef1067b97..4b031721e1 100644 --- a/packages/shared/src/logic/branch.ts +++ b/packages/shared/src/logic/branch.ts @@ -151,9 +151,7 @@ export const createDeepLink = async ({ } else { data.wer = path; } - let url = await getDeepLink(alias, branchDomain, branchKey).catch( - () => fallbackUrl - ); + let url = await getDeepLink(alias, branchDomain, branchKey).catch(() => null); if (!url) { console.log(`No existing deeplink for ${alias}, creating new one`); const response = await fetchBranchApi('/v1/url', { From 6e30218115fad062e86f71ffbb390be46ddc3ff9 Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 15:46:30 -0700 Subject: [PATCH 124/157] expose: update cache in response to post edits Previously, the cache would become stale whenever a post was edited, only updating on agent reload or manual re-add of the post. Now, we drink the channels subscription firehose, and react to changes to content that we're publishing. --- desk/app/expose.hoon | 125 +++++++++++++++++++++++++++++------- desk/lib/channel-utils.hoon | 11 ++++ 2 files changed, 114 insertions(+), 22 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 6a259be369..88c79059fa 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -28,16 +28,53 @@ :: ++ e :: expose utils |% - ++ update-widget + ++ refresh-widget |= [=bowl:gall open=(set cite:c)] ^- (list card) ?. .^(? %gu /(scot %p our.bowl)/profile/(scot %da now.bowl)/$) ~ + ::TODO should remove widget if no published content? + :: or just show "nothing yet..." =; widget=[%0 desc=@t %marl marl] =/ =cage noun+!>([%command %update-widget %groups %expose-all widget]) [%pass /profile/widget/all %agent [our.bowl %profile] %poke cage]~ :^ %0 'Publicized content' %marl (render:widget bowl open) + :: + ++ refresh-pages + |= [=bowl:gall cis=(list cite:c)] + ^- (list card) + %+ turn cis + |= ref=cite:c + ^- card + %+ store:hutils + (cat 3 '/expose' (spat (print:c ref))) + ::TODO maybe find a way to dedupe with logic in %show and %handle-http-req + ::NOTE for sane amounts of content, doing an all-at-once re-rendering + :: remains reasonably fast. scry overhead (for contacts/profile deets) + :: is the slowest part, and doing all pages at once means we can + :: memoize and only run those scries once + =/ msg=(unit [=nest:g:c =post:d]) + (grab-post:cite:u bowl ref) + ?~ msg + :: if the message (no longer) exists in the backend store, make sure to + :: remove it from cache also. whatever deletes the store chooses to + :: respect, we should respect also. + :: + ~ + =/ pag=(unit manx) + (render:page bowl u.msg) + ?~ pag + :: if we cannot render the page for whatever reason, we cannot guarantee + :: its up-to-date-ness. delete it from cache, so we don't risk serving + :: stale content. + ~ + `[| %payload (paint:hutils %page u.pag)] + :: + ++ clear-page + |= ref=cite:c + ^- card + (store:hutils (cat 3 '/expose' (spat (print:c ref))) ~) -- -- :: @@ -52,7 +89,9 @@ ++ on-init ^- (quip card _this) :_ this - [%pass /eyre/connect %arvo %e %connect [~ /expose] dap.bowl]~ + :~ [%pass /eyre/connect %arvo %e %connect [~ /expose] dap.bowl] + [%pass /channels %agent [our.bowl %channels] %watch /v1] + == :: ++ on-save !>(state) ++ on-load @@ -84,7 +123,7 @@ ?> ?=(^ pag) =. open (~(put in open) ref) :_ this - :_ (update-widget:e bowl open) + :_ (refresh-widget:e bowl open) %+ store:hutils (cat 3 '/expose' (spat path.act)) `[| %payload (paint:hutils %page u.pag)] @@ -96,7 +135,7 @@ [~ this] =. open (~(del in open) ref) :_ this - :_ (update-widget:e bowl open) + :_ (refresh-widget:e bowl open) %+ store:hutils (cat 3 '/expose' (spat path.act)) :^ ~ | %payload @@ -160,23 +199,66 @@ `(as-octs:mimes:html style-shared) `[| %payload [200 ['content-type' 'text/css'] ~] bod] %+ weld - (update-widget:e bowl open) - %+ murn ~(tap in open) - |= ref=cite:c - ^- (unit card) ::TODO or should this remove from cache also? - ::TODO maybe find a way to dedupe with logic in %show and %handle-http-req - ::TODO reconsider. if we just remove the cache entry, we'll re-render - :: on-demand instead of all-at-once, which may be slow. - =/ msg=(unit [=nest:g:c =post:d]) - (grab-post:cite:u bowl ref) - ?~ msg ~ - =/ pag=(unit manx) - (render:page bowl u.msg) - ?~ pag ~ - %- some - %+ store:hutils - (cat 3 '/expose' (spat (print:c ref))) - `[| %payload (paint:hutils %page u.pag)] + (refresh-widget:e bowl open) + (refresh-pages:e bowl ~(tap in open)) + == +:: +++ on-agent + |= [=wire =sign:agent:gall] + ^- (quip card _this) + ?. ?=([%channels ~] wire) [~ this] + ?- -.sign + %poke-ack !! + %kick [[%pass /channels %agent [our.bowl %channels] %watch /v1]~ this] + :: + %watch-ack + ?~ p.sign [~ this] + ~& >>> [dap.bowl %rejected-by-channels] + [~ this] + :: + %fact + ?. =(%channel-response-1 p.cage.sign) [~ this] + =+ !<(r-channels:d q.cage.sign) + ::REVIEW should this handle %posts also? + ?+ -.r-channel [~ this] + %post + =/ new=(unit $@(%del kind-data:d)) + ?+ -.r-post.r-channel ~ + %set + ?~ post.r-post.r-channel `%del + `kind-data.u.post.r-post.r-channel + :: + %essay + `kind-data.essay.r-post.r-channel + == + ?~ new [~ this] + ?^ u.new + :: post was updated, refresh the relevant page's cache entry + :: + :_ this + =/ ref=cite:c + (from-post:cite:u nest id.r-channel u.new) + ?. (~(has in open) ref) ~ + %+ weld + (refresh-widget:e bowl open) + (refresh-pages:e bowl ref ~) + :: post was deleted. if we have it, clear it out. + :: + ::TODO this won't hold up in a freeform-channels world... + :: but not sure how else we'd get the msg type info for the cite. + =/ =kind-data:d + ?- -.nest + %chat [%chat ~] + %diary [%diary '' ''] + %heap [%heap ~] + == + =/ ref=cite:c + (from-post:cite:u nest id.r-channel kind-data) + ?. (~(has in open) ref) [~ this] + =. open (~(del in open) ref) + :_ this + [(clear-page:e ref) (refresh-widget:e bowl open)] + == == :: ++ on-peek @@ -188,7 +270,6 @@ == :: ++ on-leave |=(* [~ this]) -++ on-agent |=(* [~ this]) :: ++ on-fail |= [=term =tang] diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index 0c6624851e..32605270bb 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -438,6 +438,17 @@ %+ weld base /posts/post/(scot %ud (rash i.t.wer dum:ag))/channel-post-2 == + :: + ++ from-post + |= [=nest:g =id-post:c =kind-data:c] + ^- cite:ci + =/ kind + ?- -.kind-data + %chat %msg + %diary %note + %heap %curio + == + [%chan nest /[kind]/(crip (a-co:co id-post))] -- :: ++ flatten-inline From 2cecc7ad51e053bd7a025e48ceb5bab81950eb7e Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 17:01:37 -0700 Subject: [PATCH 125/157] expose: add minimal scry for all exposed posts --- desk/app/expose.hoon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index 88c79059fa..e7b39be4c2 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -265,6 +265,9 @@ |= =path ^- (unit (unit cage)) ?+ path [~ ~] + [%x %show ~] + ``noun+!>(open) + :: [?(%x %u) %show *] ``loob+!>((~(has in open) (parse:c t.t.path))) == From fbfcf6f84a1f580632dafaa72c5c4ee1361763e7 Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 17:07:30 -0700 Subject: [PATCH 126/157] http-utils: add doctype tag, to avoid quirks mode --- desk/lib/http-utils.hoon | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/desk/lib/http-utils.hoon b/desk/lib/http-utils.hoon index fdf1de4b6b..4ebbebbfe6 100644 --- a/desk/lib/http-utils.hoon +++ b/desk/lib/http-utils.hoon @@ -15,8 +15,10 @@ ^- query (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) :: -++ press :: manx to octs - (cork en-xml:html as-octt:mimes:html) +++ press :: manx to html octs + |= =manx + %- as-octt:mimes:html + (weld "\0a" (en-xml:html manx)) :: ++ paint :: render response into payload |= =reply From e7cf28e47e0e6308ef3c9422accf0f600753777e Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 17:18:36 -0700 Subject: [PATCH 127/157] expose: various small todo's and cleanup --- desk/app/expose.hoon | 7 ++++--- desk/app/expose/page.hoon | 6 +----- desk/app/expose/widget.hoon | 2 +- desk/lib/channel-utils.hoon | 5 +++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index e7b39be4c2..afe8a8c83f 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -16,7 +16,7 @@ |% +$ state-0 $: %0 - open=(set cite:c) ::TODO could support ranges of msgs? + open=(set cite:c) == :: +$ action @@ -38,7 +38,7 @@ =; widget=[%0 desc=@t %marl marl] =/ =cage noun+!>([%command %update-widget %groups %expose-all widget]) [%pass /profile/widget/all %agent [our.bowl %profile] %poke cage]~ - :^ %0 'Publicized content' %marl + :^ %0 'Published content' %marl (render:widget bowl open) :: ++ refresh-pages @@ -190,7 +190,8 @@ ~| wire ?+ wire !! [%eyre %connect ~] - [~ this] ::TODO print if not successful + ~& >>> [dap.bowl %failed-to-eyre-connect] + [~ this] :: [%refresh ~] :_ this diff --git a/desk/app/expose/page.hoon b/desk/app/expose/page.hoon index a0af6b48ec..e6abfd1bab 100644 --- a/desk/app/expose/page.hoon +++ b/desk/app/expose/page.hoon @@ -14,10 +14,8 @@ (get-contact:co bowl author.msg) :: ::TODO if we render replies then we can "unroll" whole chat threads too (: - ::TODO just key off the kind-data, no? - |^ ?+ p.nest ~ + |^ ?- -.kind-data.msg %chat - ?> ?=(%chat -.kind-data.msg) =/ title=tape (trip (rap 3 (turn (first-inline:u content.msg) flatten-inline:u))) %- some @@ -28,7 +26,6 @@ == :: %diary - ?> ?=(%diary -.kind-data.msg) =* kd kind-data.msg =/ title=tape (trip title.kd) %- some @@ -43,7 +40,6 @@ == :: %heap - ?> ?=(%heap -.kind-data.msg) =/ title=tape ?: &(?=(^ title.kind-data.msg) !=('' u.title.kind-data.msg)) (trip u.title.kind-data.msg) diff --git a/desk/app/expose/widget.hoon b/desk/app/expose/widget.hoon index 6a37e24cda..a147043c36 100644 --- a/desk/app/expose/widget.hoon +++ b/desk/app/expose/widget.hoon @@ -12,6 +12,7 @@ ^- marl =/ cis=(list cite:c) ::REVIEW maybe limit to the latest n? + ::TODO if we don't limit, at least +turn into posts first, sort by their times %+ sort ~(tap in open) :: newest first (assumes id nr in path is a timestamp) :: @@ -69,7 +70,6 @@ ::TODO for the kinds of children the div.content gets for heap posts, :: having an %a (grand)parent breaks the html rendering, :: putting the inner divs outside/after the a.exposed - ~& [%heap-content content] ;div.exposed ;div.content ;a.heap/"/expose{link}" diff --git a/desk/lib/channel-utils.hoon b/desk/lib/channel-utils.hoon index 32605270bb..26e070491c 100644 --- a/desk/lib/channel-utils.hoon +++ b/desk/lib/channel-utils.hoon @@ -538,7 +538,7 @@ %cite ;+ ;div.cite - ; [reference xx] ::TODO link to /expose if chan ref? + ; [reference] ::TODO link to /expose if chan ref? == :: %header @@ -651,8 +651,9 @@ ;span.tag:"[tag xx]" :: %link - ::TODO prefix // if no protocol in url =/ url=tape (trip p.inline) + =? url ?=(~ (find "://" url)) + (weld "//" url) =/ txt=tape ?:(=('' q.inline) url (trip q.inline)) ?. anchors ;span.faux-a:"{txt}" From 989241f86772588df15515198e26bbd2fbc5ccaa Mon Sep 17 00:00:00 2001 From: fang Date: Fri, 20 Sep 2024 17:23:16 -0700 Subject: [PATCH 128/157] expose: put placeholder message in empty widget --- desk/app/expose.hoon | 2 -- desk/app/expose/widget.hoon | 13 ++++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/desk/app/expose.hoon b/desk/app/expose.hoon index afe8a8c83f..7e3e6d3b3c 100644 --- a/desk/app/expose.hoon +++ b/desk/app/expose.hoon @@ -33,8 +33,6 @@ ^- (list card) ?. .^(? %gu /(scot %p our.bowl)/profile/(scot %da now.bowl)/$) ~ - ::TODO should remove widget if no published content? - :: or just show "nothing yet..." =; widget=[%0 desc=@t %marl marl] =/ =cage noun+!>([%command %update-widget %groups %expose-all widget]) [%pass /profile/widget/all %agent [our.bowl %profile] %poke cage]~ diff --git a/desk/app/expose/widget.hoon b/desk/app/expose/widget.hoon index a147043c36..357c611a8e 100644 --- a/desk/app/expose/widget.hoon +++ b/desk/app/expose/widget.hoon @@ -10,7 +10,14 @@ ++ render |= [=bowl:gall open=(set cite:c)] ^- marl - =/ cis=(list cite:c) + :+ ;style:"{(trip style-widget)}" + ;h2:"Published content" + :: + ?: =(~ open) + ;+ ;span.crickets:"Nothing yet..." + :: + =- (snoc - time-script-node:r) + %+ murn ::REVIEW maybe limit to the latest n? ::TODO if we don't limit, at least +turn into posts first, sort by their times %+ sort ~(tap in open) @@ -22,10 +29,6 @@ ?~ aa=(rush i.t.wer.a dum:ag) | ?~ bb=(rush i.t.wer.b dum:ag) & (gth u.aa u.bb) - :+ ;style:"{(trip style-widget)}" - ;h2:"Published content" - =- (snoc - time-script-node:r) - %+ murn cis |= ref=cite:c ^- (unit manx) =/ pon=(unit [=nest:g:c =post:d]) From ae805939b5d8921eb3ab47411540853c6af0fbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Paraniak?= Date: Mon, 23 Sep 2024 16:45:34 +0800 Subject: [PATCH 129/157] lib-negotiate: set src.bowl in +on-load when simulating kicks --- desk/lib/negotiate.hoon | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/desk/lib/negotiate.hoon b/desk/lib/negotiate.hoon index 4e298e5ab6..ebefb161c8 100644 --- a/desk/lib/negotiate.hoon +++ b/desk/lib/negotiate.hoon @@ -522,7 +522,10 @@ =* sub i.suz =. cards (snoc cards [%pass wire.sub %agent gill.sub %leave ~]) =. wex.bowl (~(del by wex.bowl) -.sub) - =^ caz inner (on-agent:og wire.sub %kick ~) + =^ caz inner + %. [wire.sub %kick ~] + =. src.bowl p.gill.i.suz + ~(on-agent inner inner-bowl:up) =^ caz state (play-cards:up caz) $(cards (weld cards caz), suz t.suz) :: From a09ac53ab662c0307fca290d6c0195c7ad69d4cc Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Mon, 23 Sep 2024 14:24:30 -0500 Subject: [PATCH 130/157] groups: make sure we leave channels in all cases of leaving a group --- desk/app/groups.hoon | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/desk/app/groups.hoon b/desk/app/groups.hoon index f9cb4758a4..47071086be 100644 --- a/desk/app/groups.hoon +++ b/desk/app/groups.hoon @@ -146,7 +146,7 @@ =+ !<(=flag:g vase) ?> from-self ?< =(our.bowl p.flag) - go-abet:go-leave:(go-abed:group-core flag) + go-abet:(go-leave:(go-abed:group-core flag) &) :: %group-create ?> from-self @@ -1031,17 +1031,16 @@ -- :: ++ go-leave - =/ joined-channels - %- ~(gas in *(set nest:g)) - %+ murn ~(tap in channels.group) - |= [ch=nest:g =channel:g] - [~ ch] + |= send-remove=? + =/ joined-channels ~(tap in ~(key by channels.group)) =. cor - (emil (leave-channels:go-pass ~(tap in joined-channels))) + (emil (leave-channels:go-pass joined-channels)) =. cor (submit-activity [%del %group flag]) - =. cor (emit remove-self:go-pass) - =. cor (emit %give %fact ~[/groups /groups/ui] group-leave+!>(flag)) + =? cor send-remove + (emit remove-self:go-pass) + =. cor + (emit %give %fact ~[/groups /groups/ui] group-leave+!>(flag)) go-core(gone &) :: ++ go-init @@ -1392,7 +1391,7 @@ %zone (go-zone-update +.diff) %meta (go-meta-update p.diff) %secret (go-secret-update p.diff) - %del go-core(gone &) + %del (go-leave |) %flag-content (go-flag-content +:diff) == :: @@ -1778,7 +1777,7 @@ =. go-core (go-activity %kick i.ships) $(ships t.ships) ?: (~(has in ships) our.bowl) - go-core(gone &) + (go-leave |) go-core :: %add-sects From c4edd275aea32df86236c8be1e91c7de7d97172d Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Mon, 23 Sep 2024 18:05:06 -0700 Subject: [PATCH 131/157] accept native or web editor in helper function and handle method calls accordingly --- .../ui/src/components/MessageInput/helpers.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/MessageInput/helpers.ts b/packages/ui/src/components/MessageInput/helpers.ts index bacbe015bf..b42fd10e79 100644 --- a/packages/ui/src/components/MessageInput/helpers.ts +++ b/packages/ui/src/components/MessageInput/helpers.ts @@ -1,4 +1,5 @@ import { EditorBridge } from '@10play/tentap-editor'; +import { Editor } from '@tiptap/react'; import { createDevLogger, tiptap } from '@tloncorp/shared/dist'; import { Block, @@ -17,7 +18,7 @@ export async function processReferenceAndUpdateEditor({ matchRegex, processMatch, }: { - editor: EditorBridge; + editor: EditorBridge | Editor; pastedText: string; matchRegex: RegExp; processMatch: (match: string) => Promise; @@ -38,8 +39,16 @@ export async function processReferenceAndUpdateEditor({ const filteredJson = filterRegexFromJson(json, matchRegex); logger.log(`updating editor`, filteredJson); - // @ts-expect-error setContent does accept JSONContent - editor.setContent(filteredJson); + if ('setContent' in editor) { + // EditorBridge native case + // @ts-expect-error setContent does accept JSONContent + editor.setContent(filteredJson); + } else if ('commands' in editor) { + // Editor web case + editor.commands.setContent(filteredJson); + } else { + logger.error('Unknown editor type'); + } return attachment; } From c5f3ed740c437042257432e0b55c3859a74cf016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Paraniak?= Date: Wed, 25 Sep 2024 12:37:36 +0800 Subject: [PATCH 132/157] lib-negotiate: improve subscription %kick fix --- desk/lib/negotiate.hoon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desk/lib/negotiate.hoon b/desk/lib/negotiate.hoon index ebefb161c8..b43e74625c 100644 --- a/desk/lib/negotiate.hoon +++ b/desk/lib/negotiate.hoon @@ -523,8 +523,8 @@ =. cards (snoc cards [%pass wire.sub %agent gill.sub %leave ~]) =. wex.bowl (~(del by wex.bowl) -.sub) =^ caz inner - %. [wire.sub %kick ~] - =. src.bowl p.gill.i.suz + =. src.bowl p.gill.sub + (on-agent:og wire.sub %kick ~) ~(on-agent inner inner-bowl:up) =^ caz state (play-cards:up caz) $(cards (weld cards caz), suz t.suz) From 879bcdcf24837f234866d12022740170247a591a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Paraniak?= Date: Wed, 25 Sep 2024 13:02:23 +0800 Subject: [PATCH 133/157] lib-negotiate: remove leftover line --- desk/lib/negotiate.hoon | 1 - 1 file changed, 1 deletion(-) diff --git a/desk/lib/negotiate.hoon b/desk/lib/negotiate.hoon index b43e74625c..4800e2ab96 100644 --- a/desk/lib/negotiate.hoon +++ b/desk/lib/negotiate.hoon @@ -525,7 +525,6 @@ =^ caz inner =. src.bowl p.gill.sub (on-agent:og wire.sub %kick ~) - ~(on-agent inner inner-bowl:up) =^ caz state (play-cards:up caz) $(cards (weld cards caz), suz t.suz) :: From dc58c73a2d05d3a2d91ee04e00b280e0b55067a8 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Wed, 25 Sep 2024 15:13:43 -0700 Subject: [PATCH 134/157] Switch to react-navigation on web, consolidate screens, remove controllers --- .../src/components/AuthenticatedApp.tsx | 2 +- .../controllers/ActivityScreenController.tsx | 50 - .../controllers/AppInfoScreenController.tsx | 20 - .../BlockedUsersScreenController.tsx | 10 - .../ChannelMembersScreenController.tsx | 22 - .../ChannelMetaScreenController.tsx | 20 - .../controllers/ChannelScreenController.tsx | 38 - .../ChannelSearchScreenController.tsx | 38 - .../controllers/ChatListScreenController.tsx | 42 - .../CreateGroupScreenController.tsx | 35 - .../EditChannelScreenController.tsx | 21 - .../EditProfileScreenController.tsx | 10 - .../FeatureFlagScreenController.tsx | 15 - .../FindGroupsScreenController.tsx | 15 - .../GroupChannelsScreenController.tsx | 30 - .../GroupMembersScreenController.tsx | 20 - .../controllers/GroupMetaScreenController.tsx | 20 - .../GroupPrivacyScreenController.tsx | 22 - .../GroupRolesScreenController.tsx | 18 - .../ImageViewerScreenController.tsx | 24 - .../ManageAccountScreenController.tsx | 18 - .../ManageChannelsScreenController.tsx | 25 - .../src/controllers/PostScreenController.tsx | 27 - .../controllers/ProfileScreenController.tsx | 50 - ...shNotificationSettingsScreenController.tsx | 14 - .../UserBugReportScreenController.tsx | 15 - .../UserProfileScreenController.tsx | 37 - .../src/navigation/GroupSettingsStack.tsx | 42 - apps/tlon-mobile/src/navigation/RootStack.tsx | 118 - apps/tlon-mobile/tsconfig.json | 5 +- apps/tlon-web-new/package.json | 5 +- apps/tlon-web-new/src/app.tsx | 182 +- .../controllers/ActivityScreenController.tsx | 46 - .../controllers/AppInfoScreenController.tsx | 17 - .../BlockedUsersScreenController.tsx | 7 - .../ChannelMembersScreenController.tsx | 15 - .../ChannelMetaScreenController.tsx | 15 - .../controllers/ChannelScreenController.tsx | 43 - .../ChannelSearchScreenController.tsx | 50 - .../controllers/ChatListScreenController.tsx | 57 - .../CreateGroupScreenController.tsx | 24 - .../EditChannelScreenController.tsx | 17 - .../EditProfileScreenController.tsx | 12 - .../FeatureFlagScreenController.tsx | 7 - .../FindGroupsScreenController.tsx | 8 - .../GroupChannelsScreenController.tsx | 27 - .../GroupMembersScreenController.tsx | 11 - .../controllers/GroupMetaScreenController.tsx | 11 - .../GroupPrivacyScreenController.tsx | 11 - .../GroupRolesScreenController.tsx | 11 - .../ImageViewerScreenController.tsx | 16 - .../ManageAccountScreenController.tsx | 12 - .../ManageChannelsScreenController.tsx | 19 - .../src/controllers/PostScreenController.tsx | 27 - .../controllers/ProfileScreenController.tsx | 39 - ...shNotificationSettingsScreenController.tsx | 7 - .../UserBugReportScreenController.tsx | 12 - .../UserProfileScreenController.tsx | 32 - apps/tlon-web-new/src/hooks.ts | 27 - apps/tlon-web-new/src/logic/routing.ts | 39 - .../tlon-web-new/src/logic/useChannelMeta.tsx | 17 - .../src/logic/useGroupIdFromRoute.tsx | 9 - apps/tlon-web-new/src/logic/utils.ts | 24 - apps/tlon-web-new/src/main.tsx | 2 +- apps/tlon-web-new/vite.config.mts | 2 - .../channels/ChannelMembersScreen.tsx | 16 +- .../features/channels/ChannelMetaScreen.tsx | 20 +- .../app/features/groups/EditChannelScreen.tsx | 25 +- .../features/groups/GroupMembersScreen.tsx | 19 +- .../app/features/groups/GroupMetaScreen.tsx | 19 +- .../features/groups/GroupPrivacyScreen.tsx | 20 +- .../app/features/groups/GroupRolesScreen.tsx | 13 +- .../features/groups/ManageChannelsScreen.tsx | 25 +- .../app/features/settings/AppInfoScreen.tsx | 22 +- .../features/settings/BlockedUsersScreen.tsx | 9 +- .../features/settings/EditProfileScreen.tsx | 14 +- .../features/settings/FeatureFlagScreen.tsx | 10 +- .../features/settings/ManageAccountScreen.tsx | 27 +- .../app/features/settings/ProfileScreen.tsx | 71 +- .../PushNotificationSettingsScreen.tsx | 16 +- .../features/settings/UserBugReportScreen.tsx | 14 +- packages/app/features/top/ActivityScreen.tsx | 41 +- packages/app/features/top/ChannelScreen.tsx | 34 +- .../app/features/top/ChannelSearchScreen.tsx | 49 +- packages/app/features/top/ChatListScreen.tsx | 64 +- .../app/features/top/CreateGroupScreen.tsx | 34 +- .../app/features/top/FindGroupsScreen.tsx | 11 +- .../app/features/top/GroupChannelsScreen.tsx | 25 +- .../app/features/top/ImageViewerScreen.tsx | 29 +- packages/app/features/top/PostScreen.tsx | 30 +- .../app/features/top/UserProfileScreen.tsx | 39 +- .../app/hooks/useChannelNavigation.native.ts | 58 - .../app/hooks/useChannelNavigation.old.ts | 76 + packages/app/hooks/useChannelNavigation.ts | 52 +- .../hooks/useChatSettingsNavigation.native.ts | 105 - .../hooks/useChatSettingsNavigation.old.ts | 65 + .../app/hooks/useChatSettingsNavigation.ts | 50 +- packages/app/hooks/useFocusEffect.native.tsx | 1 - packages/app/hooks/useFocusEffect.old.tsx | 25 + packages/app/hooks/useFocusEffect.tsx | 26 +- .../app/hooks/useGroupNavigation.native.ts | 26 - packages/app/hooks/useGroupNavigation.old.ts | 23 + packages/app/hooks/useGroupNavigation.ts | 15 +- packages/app/hooks/useIsFocused.native.ts | 1 - packages/app/hooks/useIsFocused.old.ts | 34 + packages/app/hooks/useIsFocused.ts | 35 +- packages/app/hooks/useResetDb.native.ts | 5 + packages/app/hooks/useResetDb.ts | 5 + packages/app/hooks/useWebview.native.tsx | 87 + packages/app/hooks/useWebview.tsx | 11 + .../src => packages/app}/lib/webDb.ts | 4 +- .../app/lib/webMigrator.ts | 0 .../app/lib/webTriggers.ts | 0 .../app/navigation/GroupSettingsStack.tsx | 30 + packages/app/navigation/RootStack.tsx | 103 + .../app}/navigation/SettingsStack.tsx | 15 +- packages/app/navigation/types.ts | 121 + packages/app/package.json | 3 +- packages/app/tsconfig.json | 5 +- .../ui/src/components/ProfileScreenView.tsx | 1 - pnpm-lock.yaml | 1983 +++-------------- 121 files changed, 1356 insertions(+), 3988 deletions(-) delete mode 100644 apps/tlon-mobile/src/controllers/ActivityScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/AppInfoScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/BlockedUsersScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ChannelMembersScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ChannelMetaScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ChannelScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ChannelSearchScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ChatListScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/EditChannelScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/FeatureFlagScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/GroupChannelsScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/GroupMembersScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/GroupMetaScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/GroupPrivacyScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/GroupRolesScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ImageViewerScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ManageAccountScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ManageChannelsScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/PostScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/ProfileScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/PushNotificationSettingsScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/UserBugReportScreenController.tsx delete mode 100644 apps/tlon-mobile/src/controllers/UserProfileScreenController.tsx delete mode 100644 apps/tlon-mobile/src/navigation/GroupSettingsStack.tsx delete mode 100644 apps/tlon-mobile/src/navigation/RootStack.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ActivityScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/AppInfoScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/BlockedUsersScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ChannelMembersScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ChannelMetaScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ChannelScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ChannelSearchScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ChatListScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/EditChannelScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/EditProfileScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/FeatureFlagScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/GroupChannelsScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/GroupMembersScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/GroupMetaScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/GroupPrivacyScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/GroupRolesScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ImageViewerScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ManageAccountScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ManageChannelsScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/PostScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/ProfileScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/PushNotificationSettingsScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/UserBugReportScreenController.tsx delete mode 100644 apps/tlon-web-new/src/controllers/UserProfileScreenController.tsx delete mode 100644 apps/tlon-web-new/src/hooks.ts delete mode 100644 apps/tlon-web-new/src/logic/routing.ts delete mode 100644 apps/tlon-web-new/src/logic/useChannelMeta.tsx delete mode 100644 apps/tlon-web-new/src/logic/useGroupIdFromRoute.tsx delete mode 100644 packages/app/hooks/useChannelNavigation.native.ts create mode 100644 packages/app/hooks/useChannelNavigation.old.ts delete mode 100644 packages/app/hooks/useChatSettingsNavigation.native.ts create mode 100644 packages/app/hooks/useChatSettingsNavigation.old.ts delete mode 100644 packages/app/hooks/useFocusEffect.native.tsx create mode 100644 packages/app/hooks/useFocusEffect.old.tsx delete mode 100644 packages/app/hooks/useGroupNavigation.native.ts create mode 100644 packages/app/hooks/useGroupNavigation.old.ts delete mode 100644 packages/app/hooks/useIsFocused.native.ts create mode 100644 packages/app/hooks/useIsFocused.old.ts create mode 100644 packages/app/hooks/useResetDb.native.ts create mode 100644 packages/app/hooks/useResetDb.ts create mode 100644 packages/app/hooks/useWebview.native.tsx create mode 100644 packages/app/hooks/useWebview.tsx rename {apps/tlon-web-new/src => packages/app}/lib/webDb.ts (98%) rename apps/tlon-web-new/src/lib/migrator.ts => packages/app/lib/webMigrator.ts (100%) rename apps/tlon-web-new/src/lib/triggers.ts => packages/app/lib/webTriggers.ts (100%) create mode 100644 packages/app/navigation/GroupSettingsStack.tsx create mode 100644 packages/app/navigation/RootStack.tsx rename {apps/tlon-mobile/src => packages/app}/navigation/SettingsStack.tsx (53%) create mode 100644 packages/app/navigation/types.ts diff --git a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx index eba008adf3..b67ab9a827 100644 --- a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx +++ b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx @@ -8,6 +8,7 @@ import { useNetworkLogger } from '@tloncorp/app/hooks/useNetworkLogger'; import { usePostSignup } from '@tloncorp/app/hooks/usePostSignup'; import { configureClient } from '@tloncorp/app/lib/api'; import { PlatformState } from '@tloncorp/app/lib/platformHelpers'; +import { RootStack } from '@tloncorp/app/navigation/RootStack'; import { AppDataProvider } from '@tloncorp/app/provider/AppDataProvider'; import { initializeCrashReporter, sync } from '@tloncorp/shared'; import * as store from '@tloncorp/shared/dist/store'; @@ -19,7 +20,6 @@ import useNotificationListener, { type Props as NotificationListenerProps, } from '../hooks/useNotificationListener'; import { refreshHostingAuth } from '../lib/refreshHostingAuth'; -import { RootStack } from '../navigation/RootStack'; export interface AuthenticatedAppProps { notificationListenerProps: NotificationListenerProps; diff --git a/apps/tlon-mobile/src/controllers/ActivityScreenController.tsx b/apps/tlon-mobile/src/controllers/ActivityScreenController.tsx deleted file mode 100644 index d54957bf75..0000000000 --- a/apps/tlon-mobile/src/controllers/ActivityScreenController.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { ActivityScreen } from '@tloncorp/app/features/top/ActivityScreen'; -import * as db from '@tloncorp/shared/dist/db'; -import * as store from '@tloncorp/shared/dist/store'; -import { useCallback } from 'react'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function ActivityScreenController(props: Props) { - const handleGoToChannel = useCallback( - (channel: db.Channel, selectedPostId?: string) => { - props.navigation.navigate('Channel', { channel, selectedPostId }); - }, - [props.navigation] - ); - - // TODO: if diary or gallery, figure out a way to pop open the comment - // sheet - const handleGoToThread = useCallback( - (post: db.Post) => { - // TODO: we have no way to route to specific thread message rn - props.navigation.navigate('Post', { post }); - }, - [props.navigation] - ); - - const handleGoToGroup = useCallback( - (group: db.Group) => { - store.markGroupRead(group); - props.navigation.navigate('GroupSettings', { - screen: 'GroupMembers', - params: { groupId: group.id }, - }); - }, - [props.navigation] - ); - - return ( - props.navigation.navigate('ChatList')} - navigateToActivity={() => props.navigation.navigate('Activity')} - navigateToProfile={() => props.navigation.navigate('Profile')} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/AppInfoScreenController.tsx b/apps/tlon-mobile/src/controllers/AppInfoScreenController.tsx deleted file mode 100644 index bca3f16253..0000000000 --- a/apps/tlon-mobile/src/controllers/AppInfoScreenController.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { AppInfoScreen } from '@tloncorp/app/features/settings/AppInfoScreen'; -import { useCallback } from 'react'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function AppInfoScreenController(props: Props) { - const onPressPreviewFeatures = useCallback(() => { - props.navigation.navigate('FeatureFlags'); - }, [props.navigation]); - - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/BlockedUsersScreenController.tsx b/apps/tlon-mobile/src/controllers/BlockedUsersScreenController.tsx deleted file mode 100644 index e2ae9a1dd2..0000000000 --- a/apps/tlon-mobile/src/controllers/BlockedUsersScreenController.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { BlockedUsersScreen } from '@tloncorp/app/features/settings/BlockedUsersScreen'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function BlockedUsersScreenController(props: Props) { - return props.navigation.goBack()} />; -} diff --git a/apps/tlon-mobile/src/controllers/ChannelMembersScreenController.tsx b/apps/tlon-mobile/src/controllers/ChannelMembersScreenController.tsx deleted file mode 100644 index 98033a8aba..0000000000 --- a/apps/tlon-mobile/src/controllers/ChannelMembersScreenController.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { ChannelMembersScreen } from '@tloncorp/app/features/channels/ChannelMembersScreen'; - -import { RootStackParamList } from '../types'; - -type ChannelMembersScreenProps = NativeStackScreenProps< - RootStackParamList, - 'ChannelMembers' ->; - -export function ChannelMembersScreenController( - props: ChannelMembersScreenProps -) { - const { channelId } = props.route.params; - - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/ChannelMetaScreenController.tsx b/apps/tlon-mobile/src/controllers/ChannelMetaScreenController.tsx deleted file mode 100644 index fa6acfc91c..0000000000 --- a/apps/tlon-mobile/src/controllers/ChannelMetaScreenController.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { ChannelMetaScreen } from '@tloncorp/app/features/channels/ChannelMetaScreen'; - -import { RootStackParamList } from '../types'; - -type ChannelMetaScreenProps = NativeStackScreenProps< - RootStackParamList, - 'ChannelMeta' ->; - -export function ChannelMetaScreenController(props: ChannelMetaScreenProps) { - const { channelId } = props.route.params; - - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/ChannelScreenController.tsx b/apps/tlon-mobile/src/controllers/ChannelScreenController.tsx deleted file mode 100644 index 015a8b74ab..0000000000 --- a/apps/tlon-mobile/src/controllers/ChannelScreenController.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import ChannelScreen from '@tloncorp/app/features/top/ChannelScreen'; -import * as db from '@tloncorp/shared/dist/db'; -import { useCallback } from 'react'; - -import type { RootStackParamList } from '../types'; - -type ChannelScreenControllerProps = NativeStackScreenProps< - RootStackParamList, - 'Channel' ->; - -export function ChannelScreenController(props: ChannelScreenControllerProps) { - const handleGoToDm = useCallback( - async (dmChannel: db.Channel) => { - props.navigation.push('Channel', { channel: dmChannel }); - }, - [props.navigation] - ); - - const handleGoToUserProfile = useCallback( - (userId: string) => { - props.navigation.push('UserProfile', { userId }); - }, - [props.navigation] - ); - - return ( - - ); -} diff --git a/apps/tlon-mobile/src/controllers/ChannelSearchScreenController.tsx b/apps/tlon-mobile/src/controllers/ChannelSearchScreenController.tsx deleted file mode 100644 index 581d08272a..0000000000 --- a/apps/tlon-mobile/src/controllers/ChannelSearchScreenController.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import ChannelSearchScreen from '@tloncorp/app/features/top/ChannelSearchScreen'; - -import type { RootStackParamList } from '../types'; - -type ChannelSearchScreenControllerProps = NativeStackScreenProps< - RootStackParamList, - 'ChannelSearch' ->; - -export function ChannelSearchScreenController({ - navigation, - route, -}: ChannelSearchScreenControllerProps) { - return ( - { - navigation.navigate('Channel', { - channel, - selectedPostId, - }); - }} - navigateToReply={({ id, authorId, channelId }) => { - navigation.replace('Post', { - post: { - id, - channelId, - authorId, - }, - }); - }} - cancelSearch={() => { - navigation.pop(); - }} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx b/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx deleted file mode 100644 index ed8fa7e892..0000000000 --- a/apps/tlon-mobile/src/controllers/ChatListScreenController.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import ChatListScreen from '@tloncorp/app/features/top/ChatListScreen'; - -import type { RootStackParamList } from '../types'; - -type ChatListControllerProps = NativeStackScreenProps< - RootStackParamList, - 'ChatList' ->; - -export function ChatListScreenController({ - navigation, - route: { params }, -}: ChatListControllerProps) { - return ( - { - navigation.push('Channel', { channel }); - }} - navigateToGroupChannels={(group) => { - navigation.navigate('GroupChannels', { group }); - }} - navigateToSelectedPost={(channel, postId) => { - navigation.navigate('Channel', { channel, selectedPostId: postId }); - }} - navigateToHome={() => { - navigation.navigate('ChatList'); - }} - navigateToNotifications={() => { - navigation.navigate('Activity'); - }} - navigateToProfile={() => { - navigation.navigate('Profile'); - }} - navigateToFindGroups={() => { - navigation.navigate('FindGroups'); - }} - navigateToCreateGroup={() => navigation.navigate('CreateGroup')} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx b/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx deleted file mode 100644 index 538e4c847a..0000000000 --- a/apps/tlon-mobile/src/controllers/CreateGroupScreenController.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { CreateGroupScreen } from '@tloncorp/app/features/top/CreateGroupScreen'; -import type * as db from '@tloncorp/shared/dist/db'; -import { useCallback } from 'react'; - -import type { RootStackParamList } from '../types'; - -type ChatListControllerProps = NativeStackScreenProps< - RootStackParamList, - 'ChatList' ->; - -export function CreateGroupScreenController({ - navigation, -}: ChatListControllerProps) { - const handleGoToChannel = useCallback( - (channel: db.Channel) => { - navigation.reset({ - index: 1, - routes: [ - { name: 'ChatList' }, - { name: 'Channel', params: { channel } }, - ], - }); - }, - [navigation] - ); - - return ( - navigation.goBack()} - goToChannel={handleGoToChannel} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/EditChannelScreenController.tsx b/apps/tlon-mobile/src/controllers/EditChannelScreenController.tsx deleted file mode 100644 index 139998e48f..0000000000 --- a/apps/tlon-mobile/src/controllers/EditChannelScreenController.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { EditChannelScreen } from '@tloncorp/app/features/groups/EditChannelScreen'; - -import { GroupSettingsStackParamList } from '../types'; - -type ManageChannelsScreenProps = NativeStackScreenProps< - GroupSettingsStackParamList, - 'EditChannel' ->; - -export function EditChannelScreenController(props: ManageChannelsScreenProps) { - const { groupId, channelId } = props.route.params; - - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx b/apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx deleted file mode 100644 index 61446e5b2f..0000000000 --- a/apps/tlon-mobile/src/controllers/EditProfileScreenController.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { EditProfileScreen } from '@tloncorp/app/features/settings/EditProfileScreen'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function EditProfileScreenController(props: Props) { - return props.navigation.goBack()} />; -} diff --git a/apps/tlon-mobile/src/controllers/FeatureFlagScreenController.tsx b/apps/tlon-mobile/src/controllers/FeatureFlagScreenController.tsx deleted file mode 100644 index a6e07421f2..0000000000 --- a/apps/tlon-mobile/src/controllers/FeatureFlagScreenController.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { FeatureFlagScreen } from '@tloncorp/app/features/settings/FeatureFlagScreen'; - -import type { RootStackParamList } from '../types'; - -type FeatureFlagScreenProps = NativeStackScreenProps< - RootStackParamList, - 'FeatureFlags' ->; - -export function FeatureFlagScreenController({ - navigation, -}: FeatureFlagScreenProps) { - return navigation.goBack()} />; -} diff --git a/apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx b/apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx deleted file mode 100644 index 655bfb322c..0000000000 --- a/apps/tlon-mobile/src/controllers/FindGroupsScreenController.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { FindGroupsScreen } from '@tloncorp/app/features/top/FindGroupsScreen'; - -import type { RootStackParamList } from '../types'; - -type ChatListControllerProps = NativeStackScreenProps< - RootStackParamList, - 'ChatList' ->; - -export function FindGroupsScreenController({ - navigation, -}: ChatListControllerProps) { - return navigation.goBack()} />; -} diff --git a/apps/tlon-mobile/src/controllers/GroupChannelsScreenController.tsx b/apps/tlon-mobile/src/controllers/GroupChannelsScreenController.tsx deleted file mode 100644 index a9a0e09407..0000000000 --- a/apps/tlon-mobile/src/controllers/GroupChannelsScreenController.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { GroupChannelsScreen } from '@tloncorp/app/features/top/GroupChannelsScreen'; - -import type { RootStackParamList } from '../types'; - -type GroupChannelsScreenControllerProps = NativeStackScreenProps< - RootStackParamList, - 'GroupChannels' ->; - -export function GroupChannelsScreenController({ - navigation, - route, -}: GroupChannelsScreenControllerProps) { - const groupParam = route.params.group; - - return ( - { - navigation.navigate('Channel', { - channel: channel, - }); - }} - goBack={() => { - navigation.goBack(); - }} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/GroupMembersScreenController.tsx b/apps/tlon-mobile/src/controllers/GroupMembersScreenController.tsx deleted file mode 100644 index 2f257bdd9c..0000000000 --- a/apps/tlon-mobile/src/controllers/GroupMembersScreenController.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { GroupMembersScreen } from '@tloncorp/app/features/groups/GroupMembersScreen'; - -import { GroupSettingsStackParamList } from '../types'; - -type GroupMembersScreenProps = NativeStackScreenProps< - GroupSettingsStackParamList, - 'GroupMembers' ->; - -export function GroupMembersScreenController(props: GroupMembersScreenProps) { - const { groupId } = props.route.params; - - return ( - props.navigation.goBack()} - groupId={groupId} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/GroupMetaScreenController.tsx b/apps/tlon-mobile/src/controllers/GroupMetaScreenController.tsx deleted file mode 100644 index 000f03b3ab..0000000000 --- a/apps/tlon-mobile/src/controllers/GroupMetaScreenController.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { GroupMetaScreen } from '@tloncorp/app/features/groups/GroupMetaScreen'; - -import { GroupSettingsStackParamList } from '../types'; - -type GroupMetaScreenProps = NativeStackScreenProps< - GroupSettingsStackParamList, - 'GroupMeta' ->; - -export function GroupMetaScreenController(props: GroupMetaScreenProps) { - const { groupId } = props.route.params; - - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/GroupPrivacyScreenController.tsx b/apps/tlon-mobile/src/controllers/GroupPrivacyScreenController.tsx deleted file mode 100644 index 375fb27f4b..0000000000 --- a/apps/tlon-mobile/src/controllers/GroupPrivacyScreenController.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { GroupPrivacyScreen } from '@tloncorp/app/features/groups/GroupPrivacyScreen'; - -import { GroupSettingsStackParamList } from '../types'; - -type InvitesAndPrivacyScreenProps = NativeStackScreenProps< - GroupSettingsStackParamList, - 'Privacy' ->; - -export function GroupPrivacyScreenController( - props: InvitesAndPrivacyScreenProps -) { - const { groupId } = props.route.params; - - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/GroupRolesScreenController.tsx b/apps/tlon-mobile/src/controllers/GroupRolesScreenController.tsx deleted file mode 100644 index 6616b4f642..0000000000 --- a/apps/tlon-mobile/src/controllers/GroupRolesScreenController.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { GroupRolesScreen } from '@tloncorp/app/features/groups/GroupRolesScreen'; - -import { GroupSettingsStackParamList } from '../types'; - -type GroupRolesScreenProps = NativeStackScreenProps< - GroupSettingsStackParamList, - 'GroupRoles' ->; - -export function GroupRolesScreenController(props: GroupRolesScreenProps) { - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/ImageViewerScreenController.tsx b/apps/tlon-mobile/src/controllers/ImageViewerScreenController.tsx deleted file mode 100644 index 0735772989..0000000000 --- a/apps/tlon-mobile/src/controllers/ImageViewerScreenController.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import ImageViewerScreen from '@tloncorp/app/features/top/ImageViewerScreen'; - -import type { RootStackParamList } from '../types'; - -type ImagePreviewScreenControllerProps = NativeStackScreenProps< - RootStackParamList, - 'ImageViewer' ->; - -export default function ImageViewerScreenController( - props: ImagePreviewScreenControllerProps -) { - const postParam = props.route.params.post; - const uriParam = props.route.params.uri; - - return ( - props.navigation.pop()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/ManageAccountScreenController.tsx b/apps/tlon-mobile/src/controllers/ManageAccountScreenController.tsx deleted file mode 100644 index c2318007bd..0000000000 --- a/apps/tlon-mobile/src/controllers/ManageAccountScreenController.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { ManageAccountScreen } from '@tloncorp/app/features/settings/ManageAccountScreen'; - -import { useWebView } from '../hooks/useWebView'; -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function ManageAccountScreenController(props: Props) { - const webview = useWebView(); - - return ( - props.navigation.goBack()} - webview={webview} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/ManageChannelsScreenController.tsx b/apps/tlon-mobile/src/controllers/ManageChannelsScreenController.tsx deleted file mode 100644 index aaefaa5428..0000000000 --- a/apps/tlon-mobile/src/controllers/ManageChannelsScreenController.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { ManageChannelsScreen } from '@tloncorp/app/features/groups/ManageChannelsScreen'; - -import { GroupSettingsStackParamList } from '../types'; - -type ManageChannelsScreenProps = NativeStackScreenProps< - GroupSettingsStackParamList, - 'ManageChannels' ->; - -export function ManageChannelsScreenController( - props: ManageChannelsScreenProps -) { - const { groupId } = props.route.params; - - return ( - props.navigation.goBack()} - onGoToEditChannel={(channelId) => { - props.navigation.navigate('EditChannel', { groupId, channelId }); - }} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/PostScreenController.tsx b/apps/tlon-mobile/src/controllers/PostScreenController.tsx deleted file mode 100644 index 22da2750b0..0000000000 --- a/apps/tlon-mobile/src/controllers/PostScreenController.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import PostScreen from '@tloncorp/app/features/top/PostScreen'; -import { useCallback } from 'react'; - -import type { RootStackParamList } from '../types'; - -type PostScreenControllerProps = NativeStackScreenProps< - RootStackParamList, - 'Post' ->; - -export function PostScreenController(props: PostScreenControllerProps) { - const handleGoToUserProfile = useCallback( - (userId: string) => { - props.navigation.push('UserProfile', { userId }); - }, - [props.navigation] - ); - - return ( - - ); -} diff --git a/apps/tlon-mobile/src/controllers/ProfileScreenController.tsx b/apps/tlon-mobile/src/controllers/ProfileScreenController.tsx deleted file mode 100644 index 4502125a14..0000000000 --- a/apps/tlon-mobile/src/controllers/ProfileScreenController.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import ProfileScreen from '@tloncorp/app/features/settings/ProfileScreen'; -import { useHandleLogout } from '@tloncorp/app/hooks/useHandleLogout'; -import { resetDb } from '@tloncorp/app/lib/nativeDb'; -import { useCallback } from 'react'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function ProfileScreenController(props: Props) { - const handleLogout = useHandleLogout({ resetDb }); - - const onManageAccountPressed = useCallback(() => { - props.navigation.navigate('ManageAccount'); - }, [props.navigation]); - - const onAppInfoPressed = useCallback(() => { - props.navigation.navigate('AppInfo'); - }, [props.navigation]); - - const onPushNotifPressed = useCallback(() => { - props.navigation.navigate('PushNotificationSettings'); - }, [props.navigation]); - - const onBlockedUsersPressed = useCallback(() => { - props.navigation.navigate('BlockedUsers'); - }, [props.navigation]); - - return ( - props.navigation.navigate('EditProfile')} - navigateToErrorReport={() => props.navigation.navigate('WompWomp')} - navigateToContactProfile={(userId: string) => - props.navigation.navigate('UserProfile', { userId }) - } - navigateToProfileSettings={() => { - console.log('profile'); - props.navigation.navigate('Profile'); - }} - navigateToHome={() => props.navigation.navigate('ChatList')} - navigateToNotifications={() => props.navigation.navigate('Activity')} - navigateToManageAccount={onManageAccountPressed} - navigateToAppInfo={onAppInfoPressed} - navigateToNotificationSettings={onPushNotifPressed} - navigateToBlockedUsers={onBlockedUsersPressed} - handleLogout={handleLogout} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/PushNotificationSettingsScreenController.tsx b/apps/tlon-mobile/src/controllers/PushNotificationSettingsScreenController.tsx deleted file mode 100644 index 8414e22762..0000000000 --- a/apps/tlon-mobile/src/controllers/PushNotificationSettingsScreenController.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { PushNotificationSettingsScreen } from '@tloncorp/app/features/settings/PushNotificationSettingsScreen'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function PushNotificationSettingsScreenController(props: Props) { - return ( - props.navigation.goBack()} - /> - ); -} diff --git a/apps/tlon-mobile/src/controllers/UserBugReportScreenController.tsx b/apps/tlon-mobile/src/controllers/UserBugReportScreenController.tsx deleted file mode 100644 index 1afbc2dcad..0000000000 --- a/apps/tlon-mobile/src/controllers/UserBugReportScreenController.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { UserBugReportScreen } from '@tloncorp/app/features/settings/UserBugReportScreen'; -import { useCallback } from 'react'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export function UserBugReportScreenController(props: Props) { - const onGoBack = useCallback(() => { - props.navigation.goBack(); - }, [props.navigation]); - - return ; -} diff --git a/apps/tlon-mobile/src/controllers/UserProfileScreenController.tsx b/apps/tlon-mobile/src/controllers/UserProfileScreenController.tsx deleted file mode 100644 index 158de5cf61..0000000000 --- a/apps/tlon-mobile/src/controllers/UserProfileScreenController.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { CommonActions } from '@react-navigation/native'; -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { UserProfileScreen } from '@tloncorp/app/features/top/UserProfileScreen'; -import * as store from '@tloncorp/shared/dist/store'; -import { useCallback } from 'react'; - -import { RootStackParamList } from '../types'; - -type Props = NativeStackScreenProps; - -export default function UserProfileScreenController(props: Props) { - const handleGoToDm = useCallback( - async (participants: string[]) => { - const dmChannel = await store.upsertDmChannel({ - participants, - }); - props.navigation.dispatch( - CommonActions.reset({ - index: 1, - routes: [ - { name: 'ChatList' }, - { name: 'Channel', params: { channel: dmChannel } }, - ], - }) - ); - }, - [props.navigation] - ); - - return ( - props.navigation.goBack()} - onPressGoToDm={handleGoToDm} - /> - ); -} diff --git a/apps/tlon-mobile/src/navigation/GroupSettingsStack.tsx b/apps/tlon-mobile/src/navigation/GroupSettingsStack.tsx deleted file mode 100644 index 3a2a73d7b5..0000000000 --- a/apps/tlon-mobile/src/navigation/GroupSettingsStack.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack'; - -import { EditChannelScreenController } from '../controllers/EditChannelScreenController'; -import { GroupMembersScreenController } from '../controllers/GroupMembersScreenController'; -import { GroupMetaScreenController } from '../controllers/GroupMetaScreenController'; -import { GroupPrivacyScreenController } from '../controllers/GroupPrivacyScreenController'; -import { GroupRolesScreenController } from '../controllers/GroupRolesScreenController'; -import { ManageChannelsScreenController } from '../controllers/ManageChannelsScreenController'; -import { GroupSettingsStackParamList } from '../types'; - -const GroupSettings = createNativeStackNavigator(); - -export function GroupSettingsStack() { - return ( - - - - - - - - - ); -} diff --git a/apps/tlon-mobile/src/navigation/RootStack.tsx b/apps/tlon-mobile/src/navigation/RootStack.tsx deleted file mode 100644 index ab899b18fa..0000000000 --- a/apps/tlon-mobile/src/navigation/RootStack.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { useFocusEffect } from '@react-navigation/native'; -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import { useIsDarkMode } from '@tloncorp/app/hooks/useIsDarkMode'; -import { useTheme } from '@tloncorp/ui'; -import { Platform, StatusBar } from 'react-native'; - -import { ActivityScreenController } from '../controllers/ActivityScreenController'; -import { AppInfoScreenController } from '../controllers/AppInfoScreenController'; -import { BlockedUsersScreenController } from '../controllers/BlockedUsersScreenController'; -import { ChannelMembersScreenController } from '../controllers/ChannelMembersScreenController'; -import { ChannelMetaScreenController } from '../controllers/ChannelMetaScreenController'; -import { ChannelScreenController } from '../controllers/ChannelScreenController'; -import { ChannelSearchScreenController } from '../controllers/ChannelSearchScreenController'; -import { ChatListScreenController } from '../controllers/ChatListScreenController'; -import { CreateGroupScreenController } from '../controllers/CreateGroupScreenController'; -import { EditProfileScreenController } from '../controllers/EditProfileScreenController'; -import { FeatureFlagScreenController } from '../controllers/FeatureFlagScreenController'; -import { FindGroupsScreenController } from '../controllers/FindGroupsScreenController'; -import { GroupChannelsScreenController } from '../controllers/GroupChannelsScreenController'; -import ImageViewerScreenController from '../controllers/ImageViewerScreenController'; -import { ManageAccountScreenController } from '../controllers/ManageAccountScreenController'; -import { PostScreenController } from '../controllers/PostScreenController'; -import { PushNotificationSettingsScreenController } from '../controllers/PushNotificationSettingsScreenController'; -import { UserBugReportScreenController } from '../controllers/UserBugReportScreenController'; -import UserProfileScreenController from '../controllers/UserProfileScreenController'; -import type { RootStackParamList } from '../types'; -import { GroupSettingsStack } from './GroupSettingsStack'; -import { SettingsStack } from './SettingsStack'; - -const Root = createNativeStackNavigator(); - -export function RootStack() { - const isDarkMode = useIsDarkMode(); - - // Android status bar has a solid color by default, so we clear it - useFocusEffect(() => { - if (Platform.OS === 'android') { - StatusBar.setBackgroundColor('transparent'); - StatusBar.setBarStyle(isDarkMode ? 'light-content' : 'dark-content'); - } - }); - - const theme = useTheme(); - - return ( - - {/* top level tabs */} - - - - - {/* individual screens */} - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/apps/tlon-mobile/tsconfig.json b/apps/tlon-mobile/tsconfig.json index 42150135ab..ea06657571 100644 --- a/apps/tlon-mobile/tsconfig.json +++ b/apps/tlon-mobile/tsconfig.json @@ -10,6 +10,9 @@ "svg.d.ts" ], "compilerOptions": { - "composite": true + "composite": true, + "paths": { + "sqlocal/drizzle": ["node_modules/sqlocal/dist/drizzle"] + } } } diff --git a/apps/tlon-web-new/package.json b/apps/tlon-web-new/package.json index 2005edb863..39d7c65402 100644 --- a/apps/tlon-web-new/package.json +++ b/apps/tlon-web-new/package.json @@ -47,6 +47,7 @@ "@radix-ui/react-toggle": "^1.0.2", "@radix-ui/react-toggle-group": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.0", + "@react-navigation/native": "^6.1.7", "@tamagui/vite-plugin": "1.101.3", "@tanstack/react-query": "^4.28.0", "@tanstack/react-query-devtools": "^4.28.0", @@ -135,10 +136,10 @@ "react-image-size": "^2.0.0", "react-intersection-observer": "^9.4.0", "react-native-web": "0.19.12", + "react-native-safe-area-context": "^4.9.0", + "react-native-screens": "~3.29.0", "react-oembed-container": "github:stefkampen/react-oembed-container", "react-qr-code": "^2.0.12", - "react-router": "^6.22.1", - "react-router-dom": "^6.22.1", "react-select": "^5.3.2", "react-tweet": "^3.0.4", "react-virtuoso": "^4.5.1", diff --git a/apps/tlon-web-new/src/app.tsx b/apps/tlon-web-new/src/app.tsx index 9648f902ea..c7b00020ae 100644 --- a/apps/tlon-web-new/src/app.tsx +++ b/apps/tlon-web-new/src/app.tsx @@ -1,6 +1,15 @@ // Copyright 2024, Tlon Corporation import { TooltipProvider } from '@radix-ui/react-tooltip'; +import { + DarkTheme, + DefaultTheme, + NavigationContainer, + useNavigationContainerRef, +} from '@react-navigation/native'; import { useCurrentUserId } from '@tloncorp/app/hooks/useCurrentUser'; +import { useIsDarkMode } from '@tloncorp/app/hooks/useIsDarkMode'; +import { checkDb, useMigrations } from '@tloncorp/app/lib/webDb'; +import { RootStack } from '@tloncorp/app/navigation/RootStack'; import { Provider as TamaguiProvider } from '@tloncorp/app/provider'; import { AppDataProvider } from '@tloncorp/app/provider/AppDataProvider'; import { sync } from '@tloncorp/shared'; @@ -11,23 +20,8 @@ import { usePostHog } from 'posthog-js/react'; import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react'; import { Helmet } from 'react-helmet'; import { SafeAreaProvider } from 'react-native-safe-area-context'; -import { - NavigateFunction, - Route, - BrowserRouter as Router, - Routes, - useNavigate, -} from 'react-router-dom'; - -import { ActivityScreenController } from '@/controllers/ActivityScreenController'; -import { ChannelScreenController } from '@/controllers/ChannelScreenController'; -import { ChatListScreenController } from '@/controllers/ChatListScreenController'; -import { GroupChannelsScreenController } from '@/controllers/GroupChannelsScreenController'; -import ImageViewerScreenController from '@/controllers/ImageViewerScreenController'; -import { PostScreenController } from '@/controllers/PostScreenController'; -import { ProfileScreenController } from '@/controllers/ProfileScreenController'; + import EyrieMenu from '@/eyrie/EyrieMenu'; -import { checkDb, useMigrations } from '@/lib/webDb'; import { ANALYTICS_DEFAULT_PROPERTIES } from '@/logic/analytics'; import useAppUpdates, { AppUpdateContext } from '@/logic/useAppUpdates'; import useErrorHandler from '@/logic/useErrorHandler'; @@ -37,25 +31,6 @@ import { preSig } from '@/logic/utils'; import { toggleDevTools, useLocalState, useShowDevTools } from '@/state/local'; import { useAnalyticsId, useLogActivity, useTheme } from '@/state/settings'; -import { AppInfoScreenController } from './controllers/AppInfoScreenController'; -import { BlockedUsersScreenController } from './controllers/BlockedUsersScreenController'; -import { ChannelMembersScreenController } from './controllers/ChannelMembersScreenController'; -import { ChannelSearchScreenController } from './controllers/ChannelSearchScreenController'; -import { CreateGroupScreenController } from './controllers/CreateGroupScreenController'; -import { EditChannelScreenController } from './controllers/EditChannelScreenController'; -import { EditProfileScreenController } from './controllers/EditProfileScreenController'; -import { FeatureFlagScreenController } from './controllers/FeatureFlagScreenController'; -import { FindGroupsScreenController } from './controllers/FindGroupsScreenController'; -import { GroupMembersScreenController } from './controllers/GroupMembersScreenController'; -import { GroupMetaScreenController } from './controllers/GroupMetaScreenController'; -import { GroupPrivacyScreenController } from './controllers/GroupPrivacyScreenController'; -import { GroupRolesScreenController } from './controllers/GroupRolesScreenController'; -import { ManageAccountScreenController } from './controllers/ManageAccountScreenController'; -import { ManageChannelsScreenController } from './controllers/ManageChannelsScreenController'; -import { PushNotificationSettingsScreenController } from './controllers/PushNotificationSettingsScreenController'; -import { UserBugReportScreenController } from './controllers/UserBugReportScreenController'; -import UserProfileScreenController from './controllers/UserProfileScreenController'; - const ReactQueryDevtoolsProduction = React.lazy(() => import('@tanstack/react-query-devtools/build/lib/index.prod.js').then( (d) => ({ @@ -88,18 +63,6 @@ function checkIfLoggedIn() { } } -function handleGridRedirect(navigate: NavigateFunction) { - const query = new URLSearchParams(window.location.search); - - if (query.has('landscape-note')) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - navigate(decodeURIComponent(query.get('landscape-note')!)); - } else if (query.has('grid-link')) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - navigate(decodeURIComponent(query.get('landscape-link')!)); - } -} - function AppRoutes({ isLoaded }: { isLoaded: boolean }) { const contactsQuery = store.useContacts(); const currentUserId = useCurrentUserId(); @@ -121,107 +84,22 @@ function AppRoutes({ isLoaded }: { isLoaded: boolean }) { } }, [calmSettingsQuery, isLoaded]); + const isDarkMode = useIsDarkMode(); + + const navigationContainerRef = useNavigationContainerRef(); + if (!isLoaded) { return null; } return ( - - } /> - } /> - } /> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } /> - } - /> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - } /> - + + + ); } @@ -238,7 +116,6 @@ function MigrationCheck({ children }: PropsWithChildren) { } const App = React.memo(function AppComponent() { - const navigate = useNavigate(); const handleError = useErrorHandler(); const isDarkMode = useIsDark(); const currentUserId = useCurrentUserId(); @@ -248,9 +125,8 @@ const App = React.memo(function AppComponent() { useEffect(() => { handleError(() => { checkIfLoggedIn(); - handleGridRedirect(navigate); })(); - }, [handleError, navigate]); + }, [handleError]); useEffect(() => { api.configureClient({ @@ -320,18 +196,6 @@ function RoutedApp() { [needsUpdate, triggerUpdate] ); - const basename = () => { - if (mode === 'mock' || mode === 'staging') { - return '/'; - } - - if (mode === 'alpha') { - return '/apps/tm-alpha'; - } - - return '/apps/groups'; - }; - const theme = useTheme(); const isDarkMode = useIsDark(); @@ -396,7 +260,7 @@ function RoutedApp() { }, [posthog, showDevTools]); return ( - + <> Tlon @@ -416,7 +280,7 @@ function RoutedApp() { )} - + ); } diff --git a/apps/tlon-web-new/src/controllers/ActivityScreenController.tsx b/apps/tlon-web-new/src/controllers/ActivityScreenController.tsx deleted file mode 100644 index a592e3cc41..0000000000 --- a/apps/tlon-web-new/src/controllers/ActivityScreenController.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ActivityScreen } from '@tloncorp/app/features/top/ActivityScreen'; -import * as db from '@tloncorp/shared/dist/db'; -import * as store from '@tloncorp/shared/dist/store'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function ActivityScreenController() { - const navigate = useNavigate(); - const handleGoToChannel = useCallback( - (channel: db.Channel, selectedPostId?: string) => { - navigate(`/group/${channel.groupId}/channel/${channel.id}`); - }, - [navigate] - ); - - // TODO: if diary or gallery, figure out a way to pop open the comment - // sheet - const handleGoToThread = useCallback( - (post: db.Post) => { - // TODO: we have no way to route to specific thread message rn - navigate( - `/group/${post.groupId}/channel/${post.channelId}/post/${post.authorId}/${post.id}` - ); - }, - [navigate] - ); - - const handleGoToGroup = useCallback( - (group: db.Group) => { - store.markGroupRead(group); - navigate(`/group/members/${group.id}`); - }, - [navigate] - ); - - return ( - navigate('/')} - navigateToActivity={() => '/activity'} - navigateToProfile={() => navigate('/profile')} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/AppInfoScreenController.tsx b/apps/tlon-web-new/src/controllers/AppInfoScreenController.tsx deleted file mode 100644 index c5568e58b4..0000000000 --- a/apps/tlon-web-new/src/controllers/AppInfoScreenController.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { AppInfoScreen } from '@tloncorp/app/features/settings/AppInfoScreen'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function AppInfoScreenController() { - const navigate = useNavigate(); - const onPressPreviewFeatures = useCallback(() => { - navigate('/settings/feature-flags'); - }, [navigate]); - - return ( - navigate(-1)} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/BlockedUsersScreenController.tsx b/apps/tlon-web-new/src/controllers/BlockedUsersScreenController.tsx deleted file mode 100644 index bda8e50abe..0000000000 --- a/apps/tlon-web-new/src/controllers/BlockedUsersScreenController.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { BlockedUsersScreen } from '@tloncorp/app/features/settings/BlockedUsersScreen'; -import { useNavigate } from 'react-router'; - -export function BlockedUsersScreenController() { - const navigate = useNavigate(); - return navigate(-1)} />; -} diff --git a/apps/tlon-web-new/src/controllers/ChannelMembersScreenController.tsx b/apps/tlon-web-new/src/controllers/ChannelMembersScreenController.tsx deleted file mode 100644 index 6fe36c9644..0000000000 --- a/apps/tlon-web-new/src/controllers/ChannelMembersScreenController.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { ChannelMembersScreen } from '@tloncorp/app/features/channels/ChannelMembersScreen'; -import { useNavigate, useParams } from 'react-router'; - -export function ChannelMembersScreenController() { - const { chShip: channelId } = useParams<{ chShip: string }>(); - const navigate = useNavigate(); - - if (!channelId) { - return null; - } - - return ( - navigate(-1)} /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/ChannelMetaScreenController.tsx b/apps/tlon-web-new/src/controllers/ChannelMetaScreenController.tsx deleted file mode 100644 index 98ac797c85..0000000000 --- a/apps/tlon-web-new/src/controllers/ChannelMetaScreenController.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { ChannelMetaScreen } from '@tloncorp/app/features/channels/ChannelMetaScreen'; -import { useNavigate, useParams } from 'react-router'; - -export function ChannelMetaScreenController() { - const { chShip: channelId } = useParams<{ chShip: string }>(); - const navigate = useNavigate(); - - if (!channelId) { - return null; - } - - return ( - navigate(-1)} /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/ChannelScreenController.tsx b/apps/tlon-web-new/src/controllers/ChannelScreenController.tsx deleted file mode 100644 index fbd54e38f7..0000000000 --- a/apps/tlon-web-new/src/controllers/ChannelScreenController.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import ChannelScreen from '@tloncorp/app/features/top/ChannelScreen'; -import * as db from '@tloncorp/shared/dist/db'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -import useChannelMeta from '@/logic/useChannelMeta'; - -export function ChannelScreenController() { - const navigate = useNavigate(); - const { channel, group, postId, isDm } = useChannelMeta(); - const handleGoToDm = useCallback( - async (dmChannel: db.Channel) => { - navigate(`/dm/${dmChannel.id}`); - }, - [navigate] - ); - - const handleGoToUserProfile = useCallback( - (userId: string) => { - navigate(`/profile/${userId}`); - }, - [navigate] - ); - - const handleGoBack = useCallback(() => { - navigate('..'); - }, [navigate]); - - if (!channel || (!isDm && !group)) { - return null; - } - - return ( - - ); -} diff --git a/apps/tlon-web-new/src/controllers/ChannelSearchScreenController.tsx b/apps/tlon-web-new/src/controllers/ChannelSearchScreenController.tsx deleted file mode 100644 index 68007d50d8..0000000000 --- a/apps/tlon-web-new/src/controllers/ChannelSearchScreenController.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import ChannelSearchScreen from '@tloncorp/app/features/top/ChannelSearchScreen'; -import { isGroupChannelId } from '@tloncorp/shared/dist'; -import { useNavigate } from 'react-router'; - -import useChannelMeta from '@/logic/useChannelMeta'; - -export function ChannelSearchScreenController() { - const navigate = useNavigate(); - const { channel: channelData } = useChannelMeta(); - - if (!channelData) { - return null; - } - return ( - { - if (channel.type === 'dm' || channel.type === 'groupDm') { - if (selectedPostId) { - navigate(`/dm/${channel.id}/post/${selectedPostId}`); - return; - } - navigate(`/dm/${channel.id}`); - return; - } - if (selectedPostId) { - navigate( - `/group/${channel.group?.id}/channel/${channel.id}/${selectedPostId}` - ); - return; - } - - navigate(`/group/${channel.group?.id}/channel/${channel.id}`); - }} - navigateToReply={({ id, authorId, channelId }) => { - if (isGroupChannelId(channelId)) { - navigate( - `/group/${channelData.group?.id}/channel/${channelId}/post/${authorId}/${id}` - ); - return; - } - - navigate(`/dm/${channelId}/post/${authorId}/${id}`); - }} - cancelSearch={() => { - navigate(-1); - }} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/ChatListScreenController.tsx b/apps/tlon-web-new/src/controllers/ChatListScreenController.tsx deleted file mode 100644 index 3faa062896..0000000000 --- a/apps/tlon-web-new/src/controllers/ChatListScreenController.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import ChatListScreen from '@tloncorp/app/features/top/ChatListScreen'; -import { isDmChannelId } from '@tloncorp/shared/dist'; -import * as db from '@tloncorp/shared/dist/db'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function ChatListScreenController() { - const navigate = useNavigate(); - - const handleNavigateToChannel = useCallback( - (channel: db.Channel, postId?: string | null) => { - if (isDmChannelId(channel.id)) { - if (postId) { - navigate(`/dm/${channel.id}/${postId}`); - } else { - navigate(`/dm/${channel.id}`); - } - } else { - if (postId) { - navigate(`/group/${channel.groupId}/channel/${channel.id}/${postId}`); - } else { - navigate(`/group/${channel.groupId}/channel/${channel.id}`); - } - } - }, - [navigate] - ); - - return ( - <> - { - navigate(`/dm/${channel.id}`); - }} - navigateToGroupChannels={(group) => { - navigate(`/group/${group.id}`); - }} - navigateToSelectedPost={handleNavigateToChannel} - navigateToHome={() => { - navigate('/'); - }} - navigateToNotifications={() => { - navigate('/activity'); - }} - navigateToProfile={() => { - navigate('/profile'); - }} - navigateToFindGroups={() => { - navigate('/find-groups'); - }} - navigateToCreateGroup={() => { - navigate('/create-group'); - }} - /> - - ); -} diff --git a/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx b/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx deleted file mode 100644 index e07dd2c163..0000000000 --- a/apps/tlon-web-new/src/controllers/CreateGroupScreenController.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { CreateGroupScreen } from '@tloncorp/app/features/top/CreateGroupScreen'; -import type * as db from '@tloncorp/shared/dist/db'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function CreateGroupScreenController() { - const navigate = useNavigate(); - - const handleGoToChannel = useCallback( - (channel: db.Channel) => { - navigate(`/group/${channel.groupId}/channel/${channel.id}`, { - replace: true, - }); - }, - [navigate] - ); - - return ( - navigate(-1)} - goToChannel={handleGoToChannel} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/EditChannelScreenController.tsx b/apps/tlon-web-new/src/controllers/EditChannelScreenController.tsx deleted file mode 100644 index 4edf40674f..0000000000 --- a/apps/tlon-web-new/src/controllers/EditChannelScreenController.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { EditChannelScreen } from '@tloncorp/app/features/groups/EditChannelScreen'; -import { useNavigate } from 'react-router'; - -import useChannelMeta from '@/logic/useChannelMeta'; - -export function EditChannelScreenController() { - const { groupId, channelId } = useChannelMeta(); - const navigate = useNavigate(); - - return ( - navigate(-1)} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/EditProfileScreenController.tsx b/apps/tlon-web-new/src/controllers/EditProfileScreenController.tsx deleted file mode 100644 index c6b89b16c4..0000000000 --- a/apps/tlon-web-new/src/controllers/EditProfileScreenController.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { EditProfileScreen } from '@tloncorp/app/features/settings/EditProfileScreen'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function EditProfileScreenController() { - const navigate = useNavigate(); - const onGoBack = useCallback(() => { - navigate(-1); - }, [navigate]); - - return ; -} diff --git a/apps/tlon-web-new/src/controllers/FeatureFlagScreenController.tsx b/apps/tlon-web-new/src/controllers/FeatureFlagScreenController.tsx deleted file mode 100644 index 9286fee314..0000000000 --- a/apps/tlon-web-new/src/controllers/FeatureFlagScreenController.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { FeatureFlagScreen } from '@tloncorp/app/features/settings/FeatureFlagScreen'; -import { useNavigate } from 'react-router'; - -export function FeatureFlagScreenController() { - const navigate = useNavigate(); - return navigate(-1)} />; -} diff --git a/apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx b/apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx deleted file mode 100644 index f8f319b051..0000000000 --- a/apps/tlon-web-new/src/controllers/FindGroupsScreenController.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { FindGroupsScreen } from '@tloncorp/app/features/top/FindGroupsScreen'; -import { useNavigate } from 'react-router'; - -export function FindGroupsScreenController() { - const navigate = useNavigate(); - - return navigate(-1)} />; -} diff --git a/apps/tlon-web-new/src/controllers/GroupChannelsScreenController.tsx b/apps/tlon-web-new/src/controllers/GroupChannelsScreenController.tsx deleted file mode 100644 index 4da5031d10..0000000000 --- a/apps/tlon-web-new/src/controllers/GroupChannelsScreenController.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { GroupChannelsScreen } from '@tloncorp/app/features/top/GroupChannelsScreen'; -import { useGroup } from '@tloncorp/shared/dist'; -import { useNavigate } from 'react-router'; - -import useGroupIdFromRoute from '@/logic/useGroupIdFromRoute'; - -export function GroupChannelsScreenController() { - const groupId = useGroupIdFromRoute(); - const navigate = useNavigate(); - const { data: group } = useGroup({ id: groupId }); - - if (!group) { - return null; - } - - return ( - { - navigate(`/group/${groupId}/channel/${channel.id}`); - }} - goBack={() => { - navigate(-1); - }} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/GroupMembersScreenController.tsx b/apps/tlon-web-new/src/controllers/GroupMembersScreenController.tsx deleted file mode 100644 index e2186576d5..0000000000 --- a/apps/tlon-web-new/src/controllers/GroupMembersScreenController.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { GroupMembersScreen } from '@tloncorp/app/features/groups/GroupMembersScreen'; -import { useNavigate } from 'react-router'; - -import useGroupIdFromRoute from '@/logic/useGroupIdFromRoute'; - -export function GroupMembersScreenController() { - const groupId = useGroupIdFromRoute(); - const navigate = useNavigate(); - - return navigate(-1)} groupId={groupId} />; -} diff --git a/apps/tlon-web-new/src/controllers/GroupMetaScreenController.tsx b/apps/tlon-web-new/src/controllers/GroupMetaScreenController.tsx deleted file mode 100644 index f6bfb82796..0000000000 --- a/apps/tlon-web-new/src/controllers/GroupMetaScreenController.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { GroupMetaScreen } from '@tloncorp/app/features/groups/GroupMetaScreen'; -import { useNavigate } from 'react-router'; - -import useGroupIdFromRoute from '@/logic/useGroupIdFromRoute'; - -export function GroupMetaScreenController() { - const groupId = useGroupIdFromRoute(); - const navigate = useNavigate(); - - return navigate(-1)} />; -} diff --git a/apps/tlon-web-new/src/controllers/GroupPrivacyScreenController.tsx b/apps/tlon-web-new/src/controllers/GroupPrivacyScreenController.tsx deleted file mode 100644 index b282f4382a..0000000000 --- a/apps/tlon-web-new/src/controllers/GroupPrivacyScreenController.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { GroupPrivacyScreen } from '@tloncorp/app/features/groups/GroupPrivacyScreen'; -import { useNavigate } from 'react-router'; - -import useGroupIdFromRoute from '@/logic/useGroupIdFromRoute'; - -export function GroupPrivacyScreenController() { - const groupId = useGroupIdFromRoute(); - const navigate = useNavigate(); - - return navigate(-1)} />; -} diff --git a/apps/tlon-web-new/src/controllers/GroupRolesScreenController.tsx b/apps/tlon-web-new/src/controllers/GroupRolesScreenController.tsx deleted file mode 100644 index 5f0a276768..0000000000 --- a/apps/tlon-web-new/src/controllers/GroupRolesScreenController.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { GroupRolesScreen } from '@tloncorp/app/features/groups/GroupRolesScreen'; -import { useNavigate } from 'react-router'; - -import useGroupIdFromRoute from '@/logic/useGroupIdFromRoute'; - -export function GroupRolesScreenController() { - const groupId = useGroupIdFromRoute(); - const navigate = useNavigate(); - - return navigate(-1)} />; -} diff --git a/apps/tlon-web-new/src/controllers/ImageViewerScreenController.tsx b/apps/tlon-web-new/src/controllers/ImageViewerScreenController.tsx deleted file mode 100644 index 675a00c981..0000000000 --- a/apps/tlon-web-new/src/controllers/ImageViewerScreenController.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import ImageViewerScreen from '@tloncorp/app/features/top/ImageViewerScreen'; -import { useNavigate, useParams } from 'react-router'; - -export default function ImageViewerScreenController() { - const navigate = useNavigate(); - const { postId, uri } = useParams(); - const decodedUri = decodeURIComponent(uri ?? ''); - - return ( - navigate(-1)} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/ManageAccountScreenController.tsx b/apps/tlon-web-new/src/controllers/ManageAccountScreenController.tsx deleted file mode 100644 index 0deb771930..0000000000 --- a/apps/tlon-web-new/src/controllers/ManageAccountScreenController.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { ManageAccountScreen } from '@tloncorp/app/features/settings/ManageAccountScreen'; -import { useNavigate } from 'react-router'; - -import { resetDb } from '@/lib/webDb'; - -export function ManageAccountScreenController() { - const navigate = useNavigate(); - - return ( - navigate(-1)} /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/ManageChannelsScreenController.tsx b/apps/tlon-web-new/src/controllers/ManageChannelsScreenController.tsx deleted file mode 100644 index 7f1286a156..0000000000 --- a/apps/tlon-web-new/src/controllers/ManageChannelsScreenController.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { ManageChannelsScreen } from '@tloncorp/app/features/groups/ManageChannelsScreen'; -import { useNavigate } from 'react-router'; - -import useGroupIdFromRoute from '@/logic/useGroupIdFromRoute'; - -export function ManageChannelsScreenController() { - const groupId = useGroupIdFromRoute(); - const navigate = useNavigate(); - - return ( - navigate(-1)} - onGoToEditChannel={(channelId) => { - navigate(`/group/${groupId}/channel/${channelId}/edit`); - }} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/PostScreenController.tsx b/apps/tlon-web-new/src/controllers/PostScreenController.tsx deleted file mode 100644 index 9c9a8445dc..0000000000 --- a/apps/tlon-web-new/src/controllers/PostScreenController.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import PostScreen from '@tloncorp/app/features/top/PostScreen'; -import { usePostWithThreadUnreads } from '@tloncorp/shared/dist'; -import { useCallback } from 'react'; -import { useNavigate, useParams } from 'react-router'; - -export function PostScreenController() { - const { postId } = useParams(); - const navigate = useNavigate(); - const { data: post } = usePostWithThreadUnreads({ id: postId ?? '' }); - - const handleGoToUserProfile = useCallback((userId: string) => { - // TODO: Implement profile on web. - // props.navigation.push('UserProfile', { userId }); - }, []); - - if (!post) { - return null; - } - - return ( - navigate(-1)} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/ProfileScreenController.tsx b/apps/tlon-web-new/src/controllers/ProfileScreenController.tsx deleted file mode 100644 index c6974abddd..0000000000 --- a/apps/tlon-web-new/src/controllers/ProfileScreenController.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import ProfileScreen from '@tloncorp/app/features/settings/ProfileScreen'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function ProfileScreenController() { - const navigate = useNavigate(); - const onManageAccountPressed = useCallback(() => { - navigate('/settings/manage-account'); - }, [navigate]); - - const onAppInfoPressed = useCallback(() => { - navigate('/settings/app-info'); - }, [navigate]); - - const onPushNotifPressed = useCallback(() => { - navigate('/settings/push-notifications'); - }, [navigate]); - - const onBlockedUsersPressed = useCallback(() => { - navigate('/settings/blocked-users'); - }, [navigate]); - - return ( - navigate('/profile/edit')} - navigateToErrorReport={() => navigate('/bug-report')} - navigateToContactProfile={(userId: string) => - navigate(`/profile/${userId}`) - } - navigateToHome={() => navigate('/')} - navigateToNotifications={() => navigate('/activity')} - navigateToProfileSettings={() => navigate('/profile')} - navigateToManageAccount={onManageAccountPressed} - navigateToAppInfo={onAppInfoPressed} - navigateToNotificationSettings={onPushNotifPressed} - navigateToBlockedUsers={onBlockedUsersPressed} - /> - ); -} diff --git a/apps/tlon-web-new/src/controllers/PushNotificationSettingsScreenController.tsx b/apps/tlon-web-new/src/controllers/PushNotificationSettingsScreenController.tsx deleted file mode 100644 index 620fb171cb..0000000000 --- a/apps/tlon-web-new/src/controllers/PushNotificationSettingsScreenController.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { PushNotificationSettingsScreen } from '@tloncorp/app/features/settings/PushNotificationSettingsScreen'; -import { useNavigate } from 'react-router'; - -export function PushNotificationSettingsScreenController() { - const navigate = useNavigate(); - return navigate(-1)} />; -} diff --git a/apps/tlon-web-new/src/controllers/UserBugReportScreenController.tsx b/apps/tlon-web-new/src/controllers/UserBugReportScreenController.tsx deleted file mode 100644 index a445c3086c..0000000000 --- a/apps/tlon-web-new/src/controllers/UserBugReportScreenController.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { UserBugReportScreen } from '@tloncorp/app/features/settings/UserBugReportScreen'; -import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; - -export function UserBugReportScreenController() { - const navigate = useNavigate(); - const onGoBack = useCallback(() => { - navigate(-1); - }, [navigate]); - - return ; -} diff --git a/apps/tlon-web-new/src/controllers/UserProfileScreenController.tsx b/apps/tlon-web-new/src/controllers/UserProfileScreenController.tsx deleted file mode 100644 index ce173af76a..0000000000 --- a/apps/tlon-web-new/src/controllers/UserProfileScreenController.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { UserProfileScreen } from '@tloncorp/app/features/top/UserProfileScreen'; -import * as store from '@tloncorp/shared/dist/store'; -import { useCallback } from 'react'; -import { useNavigate, useParams } from 'react-router'; - -export default function UserProfileScreenController() { - const navigate = useNavigate(); - const { userId } = useParams<{ userId: string }>(); - - const handleGoToDm = useCallback( - async (participants: string[]) => { - const dmChannel = await store.upsertDmChannel({ - participants, - }); - - navigate(`/dm/${dmChannel.id}`); - }, - [navigate] - ); - - if (!userId) { - return null; - } - - return ( - navigate(-1)} - onPressGoToDm={handleGoToDm} - /> - ); -} diff --git a/apps/tlon-web-new/src/hooks.ts b/apps/tlon-web-new/src/hooks.ts deleted file mode 100644 index d60cd577b5..0000000000 --- a/apps/tlon-web-new/src/hooks.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { useSearchParams } from 'react-router-dom'; - -export function useSearchParam( - key: string -): [T | undefined, (update: T) => void] { - const [searchParams, setSearchParams] = useSearchParams(); - - const value = useMemo(() => { - const v = searchParams.get(key); - if (v) { - return JSON.parse(decodeURIComponent(v)); - } - return undefined; - }, [key, searchParams]); - - const setValue = useCallback( - (update: T) => { - searchParams.delete(key); - searchParams.append(key, JSON.stringify(update)); - setSearchParams(searchParams); - }, - [key, searchParams, setSearchParams] - ); - - return [value, setValue]; -} diff --git a/apps/tlon-web-new/src/logic/routing.ts b/apps/tlon-web-new/src/logic/routing.ts deleted file mode 100644 index c41c5d7984..0000000000 --- a/apps/tlon-web-new/src/logic/routing.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useCallback } from 'react'; -import { NavigateOptions, To, useLocation, useNavigate } from 'react-router'; - -export interface ModalLocationState { - backgroundLocation: Location; -} - -/** - * Returns an imperative method for navigating while preserving the navigation - * state underneath the overlay - */ -export function useModalNavigate() { - const navigate = useNavigate(); - const location = useLocation(); - return useCallback( - (to: To, opts?: NavigateOptions) => { - if (location.state) { - navigate(to, { ...(opts || {}), state: location.state }); - return; - } - navigate(to, opts); - }, - [navigate, location.state] - ); -} - -export function useDismissNavigate() { - const navigate = useNavigate(); - const location = useLocation(); - const state = location.state as ModalLocationState | null; - - return useCallback(() => { - if (state?.backgroundLocation) { - // we want to replace the current location with the background location - // so that the user won't navigate back to the modal if they hit the back button - navigate(state.backgroundLocation, { replace: true }); - } - }, [navigate, state]); -} diff --git a/apps/tlon-web-new/src/logic/useChannelMeta.tsx b/apps/tlon-web-new/src/logic/useChannelMeta.tsx deleted file mode 100644 index cac639bf53..0000000000 --- a/apps/tlon-web-new/src/logic/useChannelMeta.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as store from '@tloncorp/shared/dist/store'; -import { useLocation, useParams } from 'react-router'; - -const useChannelMeta = () => { - const { ship, name, chType, chShip, chName, postId } = useParams(); - const location = useLocation(); - const isDm = location.pathname.includes('/dm/'); - const channelId = isDm && chShip ? chShip : `${chType}/${chShip}/${chName}`; - const groupId = `${ship}/${name}`; - - const { data: channel } = store.useChannel({ id: channelId }); - const { data: group } = store.useGroup({ id: groupId }); - - return { channel, group, postId, isDm, channelId, groupId }; -}; - -export default useChannelMeta; diff --git a/apps/tlon-web-new/src/logic/useGroupIdFromRoute.tsx b/apps/tlon-web-new/src/logic/useGroupIdFromRoute.tsx deleted file mode 100644 index d18111c188..0000000000 --- a/apps/tlon-web-new/src/logic/useGroupIdFromRoute.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { useParams } from 'react-router'; - -const useGroupIdFromRoute = () => { - const { ship, name } = useParams<{ ship: string; name: string }>(); - const groupId = `${ship}/${name}`; - return groupId; -}; - -export default useGroupIdFromRoute; diff --git a/apps/tlon-web-new/src/logic/utils.ts b/apps/tlon-web-new/src/logic/utils.ts index 6e43e168f9..efa38ec865 100644 --- a/apps/tlon-web-new/src/logic/utils.ts +++ b/apps/tlon-web-new/src/logic/utils.ts @@ -43,7 +43,6 @@ import { differenceInDays, endOfToday, format } from 'date-fns'; import emojiRegex from 'emoji-regex'; import _ from 'lodash'; import { useCallback, useMemo, useRef, useState } from 'react'; -import { useParams } from 'react-router'; import ob from 'urbit-ob'; import { useCopyToClipboard } from 'usehooks-ts'; import isURL from 'validator/es/lib/isURL'; @@ -1200,33 +1199,10 @@ export function useIsHttps() { return window.location.protocol === 'https:'; } -export function useIsInThread() { - const { idTime } = useParams<{ - idTime: string; - }>(); - - return !!idTime; -} - export function useIsDmOrMultiDm(whom: string) { return useMemo(() => whomIsDm(whom) || whomIsMultiDm(whom), [whom]); } -export function useThreadParentId(whom: string) { - const isDMorMultiDM = useIsDmOrMultiDm(whom); - - const { idShip, idTime } = useParams<{ - idShip: string; - idTime: string; - }>(); - - if (isDMorMultiDM) { - return `${idShip}/${idTime}`; - } - - return idTime; -} - export function cacheIdToString(id: CacheId) { return `${id.author}/${id.sent}`; } diff --git a/apps/tlon-web-new/src/main.tsx b/apps/tlon-web-new/src/main.tsx index 8044abf286..56a1707cf9 100644 --- a/apps/tlon-web-new/src/main.tsx +++ b/apps/tlon-web-new/src/main.tsx @@ -5,6 +5,7 @@ /* eslint-disable */ import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'; import { EditorView } from '@tiptap/pm/view'; +import { setupDb } from '@tloncorp/app/lib/webDb'; import { PostHogProvider } from 'posthog-js/react'; import React from 'react'; import { createRoot } from 'react-dom/client'; @@ -12,7 +13,6 @@ import { createRoot } from 'react-dom/client'; import _api from './api'; import App from './app'; import indexedDBPersistor from './indexedDBPersistor'; -import { setupDb } from './lib/webDb'; import { analyticsClient, captureError } from './logic/analytics'; import queryClient from './queryClient'; import './styles/index.css'; diff --git a/apps/tlon-web-new/vite.config.mts b/apps/tlon-web-new/vite.config.mts index f6f5c343bd..4cb17ca05b 100644 --- a/apps/tlon-web-new/vite.config.mts +++ b/apps/tlon-web-new/vite.config.mts @@ -116,8 +116,6 @@ export default ({ mode }: { mode: string }) => { : [ '@urbit/sigil-js/dist/core', 'react-native-device-info', - '@react-navigation/bottom-tabs', - '@react-navigation/native-stack', ], output: { hashCharacters: 'base36' as any, diff --git a/packages/app/features/channels/ChannelMembersScreen.tsx b/packages/app/features/channels/ChannelMembersScreen.tsx index 6428e74496..fcb6167844 100644 --- a/packages/app/features/channels/ChannelMembersScreen.tsx +++ b/packages/app/features/channels/ChannelMembersScreen.tsx @@ -1,13 +1,13 @@ +import { NativeStackScreenProps } from '@react-navigation/native-stack'; import * as store from '@tloncorp/shared/dist/store'; import { ChannelMembersScreenView } from '@tloncorp/ui'; -export function ChannelMembersScreen({ - channelId, - onGoBack, -}: { - channelId: string; - onGoBack: () => void; -}) { +import { RootStackParamList } from '../../navigation/types'; + +type Props = NativeStackScreenProps; + +export function ChannelMembersScreen(props: Props) { + const { channelId } = props.route.params; const channelQuery = store.useChannelWithRelations({ id: channelId, }); @@ -15,7 +15,7 @@ export function ChannelMembersScreen({ return ( props.navigation.goBack()} /> ); } diff --git a/packages/app/features/channels/ChannelMetaScreen.tsx b/packages/app/features/channels/ChannelMetaScreen.tsx index 09b7c31dc5..a4fde4abd5 100644 --- a/packages/app/features/channels/ChannelMetaScreen.tsx +++ b/packages/app/features/channels/ChannelMetaScreen.tsx @@ -1,25 +1,25 @@ +import { NativeStackScreenProps } from '@react-navigation/native-stack'; import * as db from '@tloncorp/shared/dist/db'; import * as store from '@tloncorp/shared/dist/store'; import { uploadAsset, useCanUpload } from '@tloncorp/shared/dist/store'; import { AttachmentProvider, MetaEditorScreenView } from '@tloncorp/ui'; import { useCallback } from 'react'; -export function ChannelMetaScreen({ - channelId, - onGoBack, -}: { - channelId: string; - onGoBack: () => void; -}) { +import { RootStackParamList } from '../../navigation/types'; + +type Props = NativeStackScreenProps; + +export function ChannelMetaScreen(props: Props) { + const { channelId } = props.route.params; const channelQuery = store.useChannel({ id: channelId }); const canUpload = useCanUpload(); const handleSubmit = useCallback( (meta: db.ClientMeta) => { store.updateDMMeta(channelId, meta); - onGoBack(); + () => props.navigation.goBack(); }, - [channelId, onGoBack] + [channelId, props.navigation] ); return ( @@ -27,7 +27,7 @@ export function ChannelMetaScreen({ props.navigation.goBack()} title={'Edit chat info'} /> diff --git a/packages/app/features/groups/EditChannelScreen.tsx b/packages/app/features/groups/EditChannelScreen.tsx index e746d48f7d..aef376d1cf 100644 --- a/packages/app/features/groups/EditChannelScreen.tsx +++ b/packages/app/features/groups/EditChannelScreen.tsx @@ -1,18 +1,15 @@ +import { NativeStackScreenProps } from '@react-navigation/native-stack'; import * as store from '@tloncorp/shared/dist/store'; import { EditChannelScreenView } from '@tloncorp/ui'; import { useCallback } from 'react'; import { useGroupContext } from '../../hooks/useGroupContext'; +import { GroupSettingsStackParamList } from '../../navigation/types'; -export function EditChannelScreen({ - groupId, - channelId, - onGoBack, -}: { - groupId: string; - channelId: string; - onGoBack: () => void; -}) { +type Props = NativeStackScreenProps; + +export function EditChannelScreen(props: Props) { + const { groupId, channelId } = props.route.params; const { updateChannel, deleteChannel } = useGroupContext({ groupId, }); @@ -24,9 +21,9 @@ export function EditChannelScreen({ const prevChannel = data; if (prevChannel) { deleteChannel(prevChannel.id); - onGoBack(); + props.navigation.goBack(); } - }, [data, deleteChannel, onGoBack]); + }, [data, deleteChannel, props.navigation]); const handleSubmit = useCallback( (title: string, description?: string) => { @@ -37,15 +34,15 @@ export function EditChannelScreen({ title, description, }); - onGoBack(); + props.navigation.goBack(); } }, - [data, updateChannel, onGoBack] + [data, updateChannel, props.navigation] ); return ( void; -}) { + +type Props = NativeStackScreenProps< + GroupSettingsStackParamList, + 'GroupMembers' +>; + +export function GroupMembersScreen(props: Props) { + const { groupId } = props.route.params; const { groupMembers, groupRoles, @@ -29,7 +32,7 @@ export function GroupMembersScreen({ return ( void; -}) { +type Props = NativeStackScreenProps; + +export function GroupMetaScreen(props: Props) { + const { groupId } = props.route.params; const { group, setGroupMetadata, deleteGroup } = useGroupContext({ groupId, }); @@ -33,7 +32,7 @@ export function GroupMetaScreen({ const handleSubmit = useCallback( (data: db.ClientMeta) => { setGroupMetadata(data); - onGoBack(); + props.navigation.goBack(); if (enabled) { describe({ title: data.title ?? '', @@ -43,7 +42,7 @@ export function GroupMetaScreen({ }); } }, - [setGroupMetadata, onGoBack, enabled, describe] + [setGroupMetadata, props.navigation, enabled, describe] ); const handlePressDelete = useCallback(() => { @@ -55,7 +54,7 @@ export function GroupMetaScreen({ diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx index 46a5b58a7c..f846237a8c 100644 --- a/packages/app/features/top/ChatListScreen.tsx +++ b/packages/app/features/top/ChatListScreen.tsx @@ -1,3 +1,4 @@ +import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import * as db from '@tloncorp/shared/dist/db'; import * as logic from '@tloncorp/shared/dist/logic'; import * as store from '@tloncorp/shared/dist/store'; @@ -25,9 +26,12 @@ import { useChatSettingsNavigation } from '../../hooks/useChatSettingsNavigation import { useCurrentUserId } from '../../hooks/useCurrentUser'; import { useIsFocused } from '../../hooks/useIsFocused'; import { useFeatureFlag } from '../../lib/featureFlags'; +import type { RootStackParamList } from '../../navigation/types'; import { identifyTlonEmployee } from '../../utils/posthog'; import { isSplashDismissed, setSplashDismissed } from '../../utils/splash'; +type Props = NativeStackScreenProps; + const ShowFiltersButton = ({ onPress }: { onPress: () => void }) => { return ( ); }; From c704707d5d7961303055d7863fac5c1ca1ebe3dd Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 26 Sep 2024 11:11:30 -0700 Subject: [PATCH 143/157] expose: better non-backlink styling --- desk/app/expose/style/page.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index 6ff8c6ccae..ecde5cd769 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -66,7 +66,8 @@ color: var(--text-muted); } -.author a { +.author, +.author > a { display: flex; align-items: center; color: var(--text-muted); @@ -80,6 +81,7 @@ overflow: hidden; width: 1.5625em; height: 1.5625em; + border-radius: 0.25em !important; } .author .avatar img { From 159be9e424a39adf134c04c66001b46dcecbb2fe Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 26 Sep 2024 13:16:11 -0700 Subject: [PATCH 144/157] expose: fix sizing/positioning of avatar and author row --- desk/app/expose/style/page.css | 19 +++++++++++++------ desk/app/expose/style/widget.css | 8 ++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index ecde5cd769..52437f13fa 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -51,15 +51,22 @@ .author-row { width: 100%; font-size: 0.8em; - margin-left: -2.5rem; + margin-left: -2rem; display: flex; align-items: center; justify-content: space-between; - padding: 0 2.5em 2em; + padding: 0 2rem 2rem; border-bottom: 0.125em solid var(--border); gap: 2em; } +@media screen and (width >= 40rem) { + .author-row { + margin-left: -2.5em; + padding: 0 2.5em 2em; + } +} + .author-row time { padding: 0; background: transparent; @@ -79,14 +86,14 @@ .author .avatar { display: inline-block; overflow: hidden; - width: 1.5625em; - height: 1.5625em; + width: 1.5625rem; + height: 1.5625rem; border-radius: 0.25em !important; } .author .avatar img { border-radius: 0.25em !important; - width: 1.5625em; - height: 1.5625em; + width: 1.5625rem; + height: 1.5625rem; object-fit: cover; } diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index 36d1e21124..e13fc6a476 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -88,13 +88,13 @@ display: inline-block; overflow: hidden; border-radius: 0.25em; - width: 1.5625em; - height: 1.5625em; + width: 1.5625rem; + height: 1.5625rem; } .exposed .author .avatar img { border-radius: 0.25em !important; - width: 1.5625em; - height: 1.5625em; + width: 1.5625rem; + height: 1.5625rem; object-fit: cover; } From d5b503183fa0d74fd44ec6927e7fac057ccf5ccb Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 26 Sep 2024 16:19:17 -0700 Subject: [PATCH 145/157] profile, expose: slightly better tiny-responsive behavior --- desk/app/expose/style/widget.css | 11 +++++++++-- desk/app/profile/widgets.hoon | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index e13fc6a476..0ceef7b520 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -61,9 +61,16 @@ .exposed .author-row { font-size: 0.8em; display: flex; - align-items: center; - justify-content: space-between; gap: 2em; + flex-direction: column; +} + +@media screen and (min-width: 15em) { + .exposed .author-row { + flex-direction: row; + align-items: center; + justify-content: space-between; + } } .exposed:last-of-type .author-row { diff --git a/desk/app/profile/widgets.hoon b/desk/app/profile/widgets.hoon index 04adb50664..49f670f10c 100644 --- a/desk/app/profile/widgets.hoon +++ b/desk/app/profile/widgets.hoon @@ -92,12 +92,22 @@ display: flex; align-content: center; gap: 1em; + flex-direction: column; + } + @media screen and (min-width: 15em) { + .profile-headline { + flex-direction: row; + } } .profile-headline-avatar-sigil { + flex-shrink: 0; border-radius: 1em; overflow: hidden; + width: 128px; + height: 128px; } .profile-headline-avatar { + flex-shrink: 0; border-radius: 1em; object-fit: cover; width: 128px; From db24ebacb1a8ce98cbf6a92f1a5789a26fcd2594 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Thu, 26 Sep 2024 16:22:41 -0700 Subject: [PATCH 146/157] expose: avoid collapsing avatar in author row --- desk/app/expose/style/page.css | 1 + desk/app/expose/style/widget.css | 1 + 2 files changed, 2 insertions(+) diff --git a/desk/app/expose/style/page.css b/desk/app/expose/style/page.css index 52437f13fa..20f6ce21cd 100644 --- a/desk/app/expose/style/page.css +++ b/desk/app/expose/style/page.css @@ -89,6 +89,7 @@ width: 1.5625rem; height: 1.5625rem; border-radius: 0.25em !important; + flex-shrink: 0; } .author .avatar img { diff --git a/desk/app/expose/style/widget.css b/desk/app/expose/style/widget.css index 0ceef7b520..ee0f2ed819 100644 --- a/desk/app/expose/style/widget.css +++ b/desk/app/expose/style/widget.css @@ -97,6 +97,7 @@ border-radius: 0.25em; width: 1.5625rem; height: 1.5625rem; + flex-shrink: 0; } .exposed .author .avatar img { From 908c18d1aed5838ce89ae580655887001fdcd5ba Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Thu, 26 Sep 2024 17:10:52 -0700 Subject: [PATCH 147/157] add spinners, fix create branch link race condition --- packages/shared/src/logic/branch.ts | 8 +++- packages/shared/src/store/lure.ts | 42 +++++++++---------- .../components/InviteFriendsToTlonButton.tsx | 7 +++- .../ui/src/components/InviteUsersWidget.tsx | 8 +++- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/packages/shared/src/logic/branch.ts b/packages/shared/src/logic/branch.ts index 4b031721e1..626a413a28 100644 --- a/packages/shared/src/logic/branch.ts +++ b/packages/shared/src/logic/branch.ts @@ -1,8 +1,11 @@ import { isValidPatp } from '@urbit/aura'; import { getPostInfoFromWer } from '../api/harkApi'; +import { createDevLogger } from '../debug'; import * as urbit from '../urbit'; +const logger = createDevLogger('branch', true); + const fetchBranchApi = async (path: string, init?: RequestInit) => fetch(`https://api2.branch.io${path}`, init); @@ -130,7 +133,7 @@ export const createDeepLink = async ({ const isDMLure = parts.length === 2 && parts[0] === 'dm' && isValidPatp(parts[1]); if (!isDMLure && !getPostInfoFromWer(path)) { - console.log(`Invalid path: ${path}`); + logger.crumb(`Invalid path: ${path}`); return undefined; } } @@ -153,7 +156,7 @@ export const createDeepLink = async ({ } let url = await getDeepLink(alias, branchDomain, branchKey).catch(() => null); if (!url) { - console.log(`No existing deeplink for ${alias}, creating new one`); + logger.crumb(`No existing deeplink for ${alias}, creating new one`); const response = await fetchBranchApi('/v1/url', { method: 'POST', body: JSON.stringify({ @@ -167,5 +170,6 @@ export const createDeepLink = async ({ } ({ url } = (await response.json()) as { url: string }); } + logger.crumb('returning deeplink', url); return url; }; diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index 3be25fac0b..60b92fa13a 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -78,14 +78,14 @@ export const useLureState = create((set, get) => ({ }, }); - return get().fetchLure(flag, branchDomain, branchKey); + // return get().fetchLure(flag, branchDomain, branchKey); }, toggle: async (flag, meta, branchDomain, branchKey) => { const { name } = getFlagParts(flag); const lure = get().lures[flag]; const enabled = !lure?.enabled; if (!enabled) { - lureLogger.log('not enabled, poking reel-undescribe', flag); + lureLogger.crumb('not enabled, poking reel-undescribe', flag); await poke({ app: 'reel', mark: 'reel-undescribe', @@ -112,7 +112,7 @@ export const useLureState = create((set, get) => ({ json: name, }); - return get().fetchLure(flag, branchDomain, branchKey); + // return get().fetchLure(flag, branchDomain, branchKey); }, start: async () => { const bait = await scry({ @@ -129,11 +129,11 @@ export const useLureState = create((set, get) => ({ fetchLure: async (flag, branchDomain, branchKey) => { const { name } = getFlagParts(flag); const prevLure = get().lures[flag]; - lureLogger.log('fetching', flag, 'prevLure', prevLure); + lureLogger.crumb('fetching', flag, 'prevLure', prevLure); const [enabled, url] = await Promise.all([ // enabled asyncWithDefault(async () => { - lureLogger.log(performance.now(), 'fetching enabled', flag); + lureLogger.crumb(performance.now(), 'fetching enabled', flag); return subscribeOnce( { app: 'grouper', @@ -142,7 +142,7 @@ export const useLureState = create((set, get) => ({ LURE_REQUEST_TIMEOUT ) .then((en) => { - lureLogger.log(performance.now(), 'enabled fetched', flag); + lureLogger.crumb(performance.now(), 'enabled fetched', flag); return en; }) @@ -153,13 +153,13 @@ export const useLureState = create((set, get) => ({ }, prevLure?.enabled), // url (includes the token as last element of the path) asyncWithDefault(async () => { - lureLogger.log(performance.now(), 'fetching url', flag); + lureLogger.crumb(performance.now(), 'fetching url', flag); return subscribeOnce( { app: 'reel', path: `/v1/id-link/${flag}` }, LURE_REQUEST_TIMEOUT ) .then((u) => { - lureLogger.log(performance.now(), 'url fetched', u, flag); + lureLogger.crumb(performance.now(), 'url fetched', u, flag); return u; }) .catch((e) => { @@ -169,10 +169,10 @@ export const useLureState = create((set, get) => ({ }, prevLure?.url), ]); - lureLogger.log('fetched', { flag, enabled, url }); + lureLogger.crumb('fetched', { flag, enabled, url }); let deepLinkUrl: string | undefined; - lureLogger.log('enabled', enabled); + lureLogger.crumb('enabled', enabled); if (enabled && checkLureToken(url)) { const currentUserId = getCurrentUserId(); const group = await db.getGroup({ id: flag }); @@ -196,7 +196,7 @@ export const useLureState = create((set, get) => ({ branchKey, metadata, }); - lureLogger.log('deepLinkUrl created', deepLinkUrl); + lureLogger.crumb('deepLinkUrl created', deepLinkUrl); } set( @@ -231,8 +231,8 @@ export function useLure({ const fetchLure = useLureState((state) => state.fetchLure); const { bait, lure } = useLureState(selLure(flag)); - lureLogger.log('bait', bait); - lureLogger.log('lure', lure); + lureLogger.crumb('bait', bait); + lureLogger.crumb('lure', lure); const canCheckForUpdate = useMemo(() => { return Boolean(bait && !disableLoading); @@ -245,11 +245,11 @@ export function useLure({ ); }, [lure]); - lureLogger.log('lure fetcher', { canCheckForUpdate, uninitialized }); + lureLogger.crumb('lure fetcher', { canCheckForUpdate, uninitialized }); useQuery({ queryKey: ['lureFetcher', flag], queryFn: async () => { - lureLogger.log('fetching', flag, branchDomain, branchKey); + lureLogger.crumb('fetching', flag, branchDomain, branchKey); await fetchLure(flag, branchDomain, branchKey); return true; }, @@ -258,7 +258,7 @@ export function useLure({ }); const toggle = async (meta: GroupMeta) => { - lureLogger.log('toggling', flag, meta, branchDomain, branchKey); + lureLogger.crumb('toggling', flag, meta, branchDomain, branchKey); return useLureState.getState().toggle(flag, meta, branchDomain, branchKey); }; @@ -271,7 +271,7 @@ export function useLure({ [flag, branchDomain, branchKey] ); - lureLogger.log('useLure', flag, bait, lure, describe); + lureLogger.crumb('useLure', flag, bait, lure, describe); return { ...lure, @@ -297,7 +297,7 @@ export function useLureLinkChecked(url: string | undefined, enabled: boolean) { prevData.current = data; - lureLogger.log('useLureLinkChecked', url, data); + lureLogger.crumb('useLureLinkChecked', url, data); return { ...query, @@ -323,7 +323,7 @@ export function useLureLinkStatus({ }); const { good, checked } = useLureLinkChecked(url, !!enabled); - lureLogger.log('useLureLinkStatus', { + lureLogger.crumb('useLureLinkStatus', { flag, supported, fetched, @@ -348,7 +348,7 @@ export function useLureLinkStatus({ } if (!url || !checkLureToken(url) || !fetched || !checked || !deepLinkUrl) { - lureLogger.log('loading', fetched, checked, url, deepLinkUrl); + lureLogger.crumb('loading', fetched, checked, url, deepLinkUrl); return 'loading'; } @@ -359,7 +359,7 @@ export function useLureLinkStatus({ return 'ready'; }, [supported, fetched, enabled, url, checked, deepLinkUrl, good]); - lureLogger.log('url', url, 'deepLinkUrl', deepLinkUrl, 'status', status); + lureLogger.crumb('url', url, 'deepLinkUrl', deepLinkUrl, 'status', status); return { status, shareUrl: deepLinkUrl, toggle, describe }; } diff --git a/packages/ui/src/components/InviteFriendsToTlonButton.tsx b/packages/ui/src/components/InviteFriendsToTlonButton.tsx index 61a726444a..0cb50011f9 100644 --- a/packages/ui/src/components/InviteFriendsToTlonButton.tsx +++ b/packages/ui/src/components/InviteFriendsToTlonButton.tsx @@ -9,6 +9,7 @@ import { useCopy } from '../hooks/useCopy'; import { useIsAdmin } from '../utils'; import { Button } from './Button'; import { Icon } from './Icon'; +import { LoadingSpinner } from './LoadingSpinner'; export function InviteFriendsToTlonButton({ group }: { group?: db.Group }) { const userId = useCurrentUserId(); @@ -77,7 +78,11 @@ export function InviteFriendsToTlonButton({ group }: { group?: db.Group }) { > - + {status !== 'ready' || typeof shareUrl !== 'string' ? ( + + ) : ( + + )} Invite Friends to Tlon diff --git a/packages/ui/src/components/InviteUsersWidget.tsx b/packages/ui/src/components/InviteUsersWidget.tsx index 4b6cdd41b0..3cf0ddd1ab 100644 --- a/packages/ui/src/components/InviteUsersWidget.tsx +++ b/packages/ui/src/components/InviteUsersWidget.tsx @@ -9,6 +9,8 @@ import { useCopy } from '../hooks/useCopy'; import { ActionSheet } from './ActionSheet'; import { Button } from './Button'; import { ContactBook } from './ContactBook'; +import { Icon } from './Icon'; +import { LoadingSpinner } from './LoadingSpinner'; const InviteUsersWidgetComponent = ({ group, @@ -133,8 +135,12 @@ const InviteUsersWidgetComponent = ({ invitees.length === 0 && (status !== 'ready' || typeof shareUrl !== 'string') } + gap="$xl" > - {buttonText} + {buttonText} + {status !== 'ready' || typeof shareUrl !== 'string' ? ( + + ) : null} From 2c5d73c34d519e31504f59a418de73b15d85d5e7 Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Thu, 26 Sep 2024 17:12:45 -0700 Subject: [PATCH 148/157] remove local git + postinstall hooks (#3957) * remove local git + postinstall hooks * fix ci --- .github/workflows/ci.yml | 3 ++ .github/workflows/deploy-canary.yml | 2 + .github/workflows/deploy-ephemeral.yml | 61 ++++++++++++++------------ .github/workflows/deploy.yml | 2 + .github/workflows/mobile-build.yml | 2 + .github/workflows/mobile-update.yml | 14 ++++-- .husky/pre-commit | 4 -- .husky/pre-push | 2 - package.json | 3 -- pnpm-lock.yaml | 12 +---- 10 files changed, 53 insertions(+), 52 deletions(-) delete mode 100755 .husky/pre-commit delete mode 100644 .husky/pre-push diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c4535f61e..f1b095bd36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,9 @@ jobs: - recursive: true args: [--frozen-lockfile] + - name: Build packages + run: pnpm build:all + - name: Run Lint run: pnpm -r lint diff --git a/.github/workflows/deploy-canary.yml b/.github/workflows/deploy-canary.yml index 290f64d8ca..07cb4e315f 100644 --- a/.github/workflows/deploy-canary.yml +++ b/.github/workflows/deploy-canary.yml @@ -29,6 +29,8 @@ jobs: run_install: | - recursive: true args: [--frozen-lockfile] + - name: Build packages + run: pnpm build:all - working-directory: ./apps/tlon-web run: pnpm build - uses: actions/upload-artifact@v3 diff --git a/.github/workflows/deploy-ephemeral.yml b/.github/workflows/deploy-ephemeral.yml index 9f98d11ac0..be00b5998d 100644 --- a/.github/workflows/deploy-ephemeral.yml +++ b/.github/workflows/deploy-ephemeral.yml @@ -2,14 +2,14 @@ name: Deploy Groups (ephemeral) on: push: branches: - - 'do/ephemeral-moons' + - "do/ephemeral-moons" #pull_request: jobs: build-frontend: runs-on: ubuntu-latest - name: 'Build Frontend' + name: "Build Frontend" steps: - name: Checkout uses: actions/checkout@v4 @@ -26,37 +26,39 @@ jobs: - recursive: true args: [--frozen-lockfile] + - name: Build packages + run: pnpm build:all + - working-directory: ./apps/tlon-web - run: - pnpm build + run: pnpm build - uses: actions/upload-artifact@v4 with: - name: 'ui-dist' - path: apps/tlon-web/dist + name: "ui-dist" + path: apps/tlon-web/dist glob: runs-on: ubuntu-latest - name: 'Make a glob' + name: "Make a glob" needs: build-frontend steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: - name: 'ui-dist' + name: "ui-dist" path: apps/tlon-web/dist - - id: 'auth' - uses: 'google-github-actions/auth@v1' + - id: "auth" + uses: "google-github-actions/auth@v1" with: - credentials_json: '${{ secrets.GCP_SERVICE_KEY }}' - - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v1' - - name: 'glob' + credentials_json: "${{ secrets.GCP_SERVICE_KEY }}" + - name: "Set up Cloud SDK" + uses: "google-github-actions/setup-gcloud@v1" + - name: "glob" uses: ./.github/actions/glob with: - folder: 'apps/tlon-web/dist/*' - docket: 'desk/desk.docket-0' + folder: "apps/tlon-web/dist/*" + docket: "desk/desk.docket-0" - name: Commit and Push Glob run: | git config --global user.name github-actions @@ -74,12 +76,12 @@ jobs: needs: glob steps: - uses: actions/checkout@v4 - - id: 'auth' - uses: 'google-github-actions/auth@v1' + - id: "auth" + uses: "google-github-actions/auth@v1" with: - credentials_json: '${{ secrets.GCP_SERVICE_KEY }}' - - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v1' + credentials_json: "${{ secrets.GCP_SERVICE_KEY }}" + - name: "Set up Cloud SDK" + uses: "google-github-actions/setup-gcloud@v1" - id: create_moon name: Create Moon run: | @@ -88,24 +90,25 @@ jobs: SSH_SEC_KEY: ${{ secrets.GCP_SSH_SEC_KEY }} SSH_PUB_KEY: ${{ secrets.GCP_SSH_PUB_KEY }} - deploy: needs: create_moon runs-on: ubuntu-latest name: "Release to ~binnec-dozzod-marnus (canary)" steps: - uses: actions/checkout@v4 - - id: 'auth' - uses: 'google-github-actions/auth@v1' + - id: "auth" + uses: "google-github-actions/auth@v1" with: - credentials_json: '${{ secrets.GCP_SERVICE_KEY }}' - - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v1' + credentials_json: "${{ secrets.GCP_SERVICE_KEY }}" + - name: "Set up Cloud SDK" + uses: "google-github-actions/setup-gcloud@v1" - id: deploy name: Deploy run: - ./.github/helpers/deploy.sh tloncorp/tlon-apps groups ${{ needs.build_test_images.outputs.moon_name }} us-central1-a mainnet-tlon-other-2d + ./.github/helpers/deploy.sh tloncorp/tlon-apps groups ${{ + needs.build_test_images.outputs.moon_name }} us-central1-a + mainnet-tlon-other-2d env: SSH_SEC_KEY: ${{ secrets.GCP_SSH_SEC_KEY }} SSH_PUB_KEY: ${{ secrets.GCP_SSH_PUB_KEY }} - URBIT_REPO_TAG: ${{ vars.URBIT_REPO_TAG }} \ No newline at end of file + URBIT_REPO_TAG: ${{ vars.URBIT_REPO_TAG }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d2801ee227..805cc66113 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -33,6 +33,8 @@ jobs: run_install: | - recursive: true args: [--frozen-lockfile] + - name: Build packages + run: pnpm build:all - working-directory: ./apps/tlon-web run: pnpm build - uses: actions/upload-artifact@v3 diff --git a/.github/workflows/mobile-build.yml b/.github/workflows/mobile-build.yml index b9ef62a811..a6091ac4c3 100644 --- a/.github/workflows/mobile-build.yml +++ b/.github/workflows/mobile-build.yml @@ -35,6 +35,8 @@ jobs: uses: pnpm/action-setup@v3 - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Build packages + run: pnpm build:all - name: Create build vars id: vars run: | diff --git a/.github/workflows/mobile-update.yml b/.github/workflows/mobile-update.yml index 704a7add61..e48097dc37 100644 --- a/.github/workflows/mobile-update.yml +++ b/.github/workflows/mobile-update.yml @@ -33,13 +33,21 @@ jobs: token: ${{ secrets.EXPO_TOKEN }} - name: Install dependencies run: pnpm install + - name: Build packages + run: pnpm build:all - name: Create build vars id: vars run: | echo "profile=${{ inputs.profile || 'preview' }}" >> $GITHUB_OUTPUT - name: Push update for selected platforms working-directory: ./apps/tlon-mobile - run: eas update --auto --profile ${{ steps.vars.outputs.profile }} --platform ${{ inputs.platform || 'all' }} --non-interactive + run: + eas update --auto --profile ${{ steps.vars.outputs.profile }} + --platform ${{ inputs.platform || 'all' }} --non-interactive env: - NOTIFY_PROVIDER: "${{ steps.vars.outputs.profile == 'preview' && 'binnec-dozzod-marnus' || 'rivfur-livmet' }}" - NOTIFY_SERVICE: "${{ steps.vars.outputs.profile == 'preview' && 'tlon-preview-release' || 'groups-native' }}" + NOTIFY_PROVIDER: + "${{ steps.vars.outputs.profile == 'preview' && + 'binnec-dozzod-marnus' || 'rivfur-livmet' }}" + NOTIFY_SERVICE: + "${{ steps.vars.outputs.profile == 'preview' && + 'tlon-preview-release' || 'groups-native' }}" diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 3abfb0062a..0000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -git add ./apps/tlon-web/src/\*.snap - -pnpm exec lint-staged diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100644 index 6d908db9f7..0000000000 --- a/.husky/pre-push +++ /dev/null @@ -1,2 +0,0 @@ -pnpm test -pnpm -r tsc diff --git a/package.json b/package.json index 15d5da947d..6566d07a81 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,6 @@ "dev:ios": "concurrently \"pnpm run dev:shared\" \"pnpm --filter 'tlon-mobile' dev\" \"pnpm --filter 'tlon-mobile' ios\" \"pnpm --filter '@tloncorp/ui' watch\"", "dev:web": "concurrently \"pnpm run dev:shared\" \"pnpm --filter 'tlon-web' dev-no-ssl\"", "test": "pnpm run -r test run -u", - "prepare": "husky", - "postinstall": "pnpm run build:all", "lint:all": "pnpm -r lint" }, "dependencies": { @@ -31,7 +29,6 @@ "eslint": "8.57.0", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", - "husky": "^9.0.10", "lint-staged": "^15.0.0", "tsup": "^8.0.1", "vitest": "^1.2.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ab90bf07b..46cdb1febb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,9 +87,6 @@ importers: eslint-plugin-react-hooks: specifier: ^4.6.0 version: 4.6.0(eslint@8.57.0) - husky: - specifier: ^9.0.10 - version: 9.0.10 lint-staged: specifier: ^15.0.0 version: 15.2.0 @@ -3894,7 +3891,7 @@ packages: react: '>=16.x.x' react-native: '>=0.62.x' react-native-gesture-handler: '>=2.x.x' - react-native-reanimated: '>=2.x.x' + react-native-reanimated: 3.10.1 '@microsoft/applicationinsights-web-snippet@1.1.2': resolution: {integrity: sha512-qPoOk3MmEx3gS6hTc1/x8JWQG5g4BvRdH7iqZMENBsKCL927b7D7Mvl19bh3sW9Ucrg1fVrF+4hqShwQNdqLxQ==} @@ -8809,11 +8806,6 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - husky@9.0.10: - resolution: {integrity: sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==} - engines: {node: '>=18'} - hasBin: true - hyphenate-style-name@1.0.4: resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} @@ -22715,8 +22707,6 @@ snapshots: human-signals@5.0.0: {} - husky@9.0.10: {} - hyphenate-style-name@1.0.4: {} iconv-lite@0.4.24: From 3ef106f37f09fdaa8fba41ca6098e2d26e6c9632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?b=E1=B5=A3=E1=B5=A2=E2=82=90=E2=82=99?= <90741358+latter-bolden@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:33:27 -0700 Subject: [PATCH 149/157] Update packages/shared/src/store/lure.ts Co-authored-by: Patrick O'Sullivan --- packages/shared/src/store/lure.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index 60b92fa13a..44fc0554d8 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -78,7 +78,6 @@ export const useLureState = create((set, get) => ({ }, }); - // return get().fetchLure(flag, branchDomain, branchKey); }, toggle: async (flag, meta, branchDomain, branchKey) => { const { name } = getFlagParts(flag); From 4507a9404252a45f70c9a27b661318f005293343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?b=E1=B5=A3=E1=B5=A2=E2=82=90=E2=82=99?= <90741358+latter-bolden@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:33:32 -0700 Subject: [PATCH 150/157] Update packages/shared/src/store/lure.ts Co-authored-by: Patrick O'Sullivan --- packages/shared/src/store/lure.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shared/src/store/lure.ts b/packages/shared/src/store/lure.ts index 44fc0554d8..adf1bcf094 100644 --- a/packages/shared/src/store/lure.ts +++ b/packages/shared/src/store/lure.ts @@ -111,7 +111,6 @@ export const useLureState = create((set, get) => ({ json: name, }); - // return get().fetchLure(flag, branchDomain, branchKey); }, start: async () => { const bait = await scry({ From d85c026f53e9366aeb0034dbafee63658810398f Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 27 Sep 2024 00:38:38 +0000 Subject: [PATCH 151/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 634bfb966c..4d56f5536c 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0v7.bk061.vf2h1.b3jve.t2qt7.jes9n.glob' 0v7.bk061.vf2h1.b3jve.t2qt7.jes9n] + glob-http+['https://bootstrap.urbit.org/glob-0va3jd1.apd6f.3hta3.vi5n9.mupfp.glob' 0va3jd1.apd6f.3hta3.vi5n9.mupfp] base+'groups' version+[6 3 0] website+'https://tlon.io' From a51eb5259c2f84f74e4847df4169ef776ba4f501 Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Thu, 26 Sep 2024 17:56:32 -0700 Subject: [PATCH 152/157] show reconnection state when app foregrounded (#3955) * show reconnection state when app foregrounded * add reconnecting prop instead of clearing session --- .../src/components/AuthenticatedApp.tsx | 22 +++++++------- packages/app/hooks/useAppForegrounded.ts | 30 ------------------- packages/app/hooks/useAppStatusChange.ts | 24 +++++++++++++++ packages/app/lib/api.ts | 20 +++++++++++++ packages/shared/src/api/urbit.ts | 9 ++++-- packages/shared/src/store/session.ts | 19 ++++++++++-- packages/shared/src/store/sync.ts | 10 +++++++ 7 files changed, 87 insertions(+), 47 deletions(-) delete mode 100644 packages/app/hooks/useAppForegrounded.ts create mode 100644 packages/app/hooks/useAppStatusChange.ts diff --git a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx index b67ab9a827..939b6a9b91 100644 --- a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx +++ b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx @@ -1,25 +1,25 @@ import crashlytics from '@react-native-firebase/crashlytics'; import { useShip } from '@tloncorp/app/contexts/ship'; import { useSignupContext } from '@tloncorp/app/contexts/signup'; -import useAppForegrounded from '@tloncorp/app/hooks/useAppForegrounded'; +import { useAppStatusChange } from '@tloncorp/app/hooks/useAppStatusChange'; import { useCurrentUserId } from '@tloncorp/app/hooks/useCurrentUser'; import { useNavigationLogging } from '@tloncorp/app/hooks/useNavigationLogger'; import { useNetworkLogger } from '@tloncorp/app/hooks/useNetworkLogger'; import { usePostSignup } from '@tloncorp/app/hooks/usePostSignup'; -import { configureClient } from '@tloncorp/app/lib/api'; +import { cancelFetch, configureClient } from '@tloncorp/app/lib/api'; import { PlatformState } from '@tloncorp/app/lib/platformHelpers'; import { RootStack } from '@tloncorp/app/navigation/RootStack'; import { AppDataProvider } from '@tloncorp/app/provider/AppDataProvider'; import { initializeCrashReporter, sync } from '@tloncorp/shared'; import * as store from '@tloncorp/shared/dist/store'; import { ZStack } from '@tloncorp/ui'; -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; +import { AppStateStatus } from 'react-native'; import { useDeepLinkListener } from '../hooks/useDeepLinkListener'; import useNotificationListener, { type Props as NotificationListenerProps, } from '../hooks/useNotificationListener'; -import { refreshHostingAuth } from '../lib/refreshHostingAuth'; export interface AuthenticatedAppProps { notificationListenerProps: NotificationListenerProps; @@ -30,7 +30,6 @@ function AuthenticatedApp({ }: AuthenticatedAppProps) { const { ship, shipUrl } = useShip(); const currentUserId = useCurrentUserId(); - const session = store.useCurrentSession(); const signupContext = useSignupContext(); const handlePostSignup = usePostSignup(); useNotificationListener(notificationListenerProps); @@ -45,6 +44,7 @@ function AuthenticatedApp({ onReset: () => sync.syncStart(), verbose: __DEV__, onChannelReset: () => sync.handleDiscontinuity(), + onChannelStatusChange: sync.handleChannelStatusChange, }); initializeCrashReporter(crashlytics(), PlatformState); @@ -61,16 +61,16 @@ function AuthenticatedApp({ sync.syncStart(); }, [currentUserId, handlePostSignup, ship, shipUrl, signupContext.didSignup]); - useAppForegrounded(() => { - // only run these updates if we've initialized the session - // (i.e. first startSync has completed) - if (session) { + const handleAppStatusChange = useCallback((status: AppStateStatus) => { + if (status === 'active') { sync.syncUnreads({ priority: sync.SyncPriority.High }); sync.syncPinnedItems({ priority: sync.SyncPriority.High }); + } else if (status === 'background') { + cancelFetch(); } + }, []); - refreshHostingAuth(); - }); + useAppStatusChange(handleAppStatusChange); return ( diff --git a/packages/app/hooks/useAppForegrounded.ts b/packages/app/hooks/useAppForegrounded.ts deleted file mode 100644 index 43531e4e29..0000000000 --- a/packages/app/hooks/useAppForegrounded.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useCallback, useEffect, useState } from 'react'; -import { AppState, AppStateStatus } from 'react-native'; - -const useAppForegrounded = (callback: (() => void) | (() => Promise)) => { - const [appState, setAppState] = useState(AppState.currentState); - - const handleAppStateChange = useCallback( - (nextAppState: AppStateStatus) => { - if (appState.match(/inactive|background/) && nextAppState === 'active') { - // App has come to the foreground - callback(); - } - setAppState(nextAppState); - }, - [appState, callback] - ); - - useEffect(() => { - const subscription = AppState.addEventListener( - 'change', - handleAppStateChange - ); - - return () => { - subscription.remove(); - }; - }, [callback, handleAppStateChange]); -}; - -export default useAppForegrounded; diff --git a/packages/app/hooks/useAppStatusChange.ts b/packages/app/hooks/useAppStatusChange.ts new file mode 100644 index 0000000000..505578f695 --- /dev/null +++ b/packages/app/hooks/useAppStatusChange.ts @@ -0,0 +1,24 @@ +import { useEffect, useRef } from 'react'; +import { AppState, AppStateStatus } from 'react-native'; + +export const useAppStatusChange = ( + callback: (status: AppStateStatus) => void | Promise +) => { + const appStateRef = useRef(AppState.currentState); + + useEffect(() => { + const subscription = AppState.addEventListener('change', (status) => { + if (appStateRef.current === null) { + // initial app state is null + return; + } else { + appStateRef.current = status; + callback(status); + } + }); + + return () => { + subscription.remove(); + }; + }, [callback]); +}; diff --git a/packages/app/lib/api.ts b/packages/app/lib/api.ts index 94f3c42af2..57cd791937 100644 --- a/packages/app/lib/api.ts +++ b/packages/app/lib/api.ts @@ -1,4 +1,5 @@ import * as api from '@tloncorp/shared/dist/api'; +import { ChannelStatus } from '@urbit/http-api'; //@ts-expect-error no typedefs import { fetch as streamingFetch } from 'react-native-fetch-api'; //@ts-expect-error no typedefs @@ -9,7 +10,17 @@ import { polyfill as polyfillReadableStream } from 'react-native-polyfill-global polyfillReadableStream(); polyfillEncoding(); +let abortController = new AbortController(); + const apiFetch: typeof fetch = (input, { ...init } = {}) => { + // Wire our injected AbortController up to the one passed in by the client. + if (init.signal) { + init.signal.onabort = () => { + abortController.abort(); + abortController = new AbortController(); + }; + } + const headers = new Headers(init.headers); // The urbit client is inconsistent about sending cookies, sometimes causing // the server to send back a new, anonymous, cookie, which is sent on all @@ -23,6 +34,7 @@ const apiFetch: typeof fetch = (input, { ...init } = {}) => { headers, // Avoid setting credentials method for same reason as above. credentials: undefined, + signal: abortController.signal, // @ts-expect-error This is used by the SSE polyfill to determine whether // to stream the request. reactNative: { textStreaming: true }, @@ -30,17 +42,24 @@ const apiFetch: typeof fetch = (input, { ...init } = {}) => { return streamingFetch(input, newInit); }; +export const cancelFetch = () => { + abortController.abort(); + abortController = new AbortController(); +}; + export function configureClient({ shipName, shipUrl, onReset, onChannelReset, + onChannelStatusChange, verbose, }: { shipName: string; shipUrl: string; onReset?: () => void; onChannelReset?: () => void; + onChannelStatusChange?: (status: ChannelStatus) => void; verbose?: boolean; }) { api.configureClient({ @@ -49,6 +68,7 @@ export function configureClient({ fetchFn: apiFetch, onReset, onChannelReset, + onChannelStatusChange, verbose, }); } diff --git a/packages/shared/src/api/urbit.ts b/packages/shared/src/api/urbit.ts index 8754ed226d..37f5df9f10 100644 --- a/packages/shared/src/api/urbit.ts +++ b/packages/shared/src/api/urbit.ts @@ -1,5 +1,5 @@ import { deSig, preSig } from '@urbit/aura'; -import { Urbit } from '@urbit/http-api'; +import { ChannelStatus, Urbit } from '@urbit/http-api'; import _ from 'lodash'; import { createDevLogger, escapeLog, runIfDev } from '../debug'; @@ -58,6 +58,7 @@ export function configureClient({ verbose, onReset, onChannelReset, + onChannelStatusChange, }: { shipName: string; shipUrl: string; @@ -65,14 +66,16 @@ export function configureClient({ verbose?: boolean; onReset?: () => void; onChannelReset?: () => void; + onChannelStatusChange?: (status: ChannelStatus) => void; }) { logger.log('configuring client', shipName, shipUrl); clientInstance = new Urbit(shipUrl, undefined, undefined, fetchFn); clientInstance.ship = deSig(shipName); clientInstance.our = preSig(shipName); clientInstance.verbose = verbose; - clientInstance.on('status-update', (status) => { - logger.log('status-update', status); + clientInstance.on('status-update', (event) => { + logger.log('status-update', event); + onChannelStatusChange?.(event.status); }); clientInstance.on('fact', (fact) => { diff --git a/packages/shared/src/store/session.ts b/packages/shared/src/store/session.ts index 368330a35d..2f92282739 100644 --- a/packages/shared/src/store/session.ts +++ b/packages/shared/src/store/session.ts @@ -1,6 +1,6 @@ import { useSyncExternalStore } from 'react'; -export type Session = { startTime: number }; +export type Session = { startTime?: number; isReconnecting?: boolean }; // Session — time when subscriptions were first initialized after which we can assume // all new events will be heard @@ -12,11 +12,20 @@ export function getSession() { return session; } -export function updateSession(newSession: Session | null) { - session = newSession; +export function triggerSessionListeners() { sessionListeners.forEach((listener) => listener(session)); } +export function updateSession(newSession: Partial | null) { + session = newSession ? { ...session, ...newSession } : null; + triggerSessionListeners(); +} + +export function setSession(newSession: Session) { + session = newSession; + triggerSessionListeners(); +} + function subscribeToSession(listener: SessionListener) { sessionListeners.push(listener); return () => { @@ -61,6 +70,10 @@ export function useConnectionStatus() { return 'Connecting'; } + if (currentSession.isReconnecting) { + return 'Reconnecting'; + } + if (syncing) { return 'Syncing'; } diff --git a/packages/shared/src/store/sync.ts b/packages/shared/src/store/sync.ts index 9f403e0533..700880b861 100644 --- a/packages/shared/src/store/sync.ts +++ b/packages/shared/src/store/sync.ts @@ -1,3 +1,4 @@ +import { ChannelStatus } from '@urbit/http-api'; import { backOff } from 'exponential-backoff'; import _ from 'lodash'; @@ -1012,6 +1013,15 @@ export const handleDiscontinuity = async () => { await syncStart(true); }; +export const handleChannelStatusChange = async (status: ChannelStatus) => { + if (status === 'reconnecting') { + updateSession({ isReconnecting: true }); + } else if (status === 'reconnected') + updateSession({ + isReconnecting: false, + }); +}; + export const syncStart = async (alreadySubscribed?: boolean) => { updateIsSyncing(true); const reporter = new ErrorReporter('sync start', logger); From ec83b2e2322543a2f70f681aa61d2a9d5abeb47c Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 27 Sep 2024 01:01:40 +0000 Subject: [PATCH 153/157] update glob: [skip actions] --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 4d56f5536c..72123dc02c 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -2,7 +2,7 @@ info+'Start, host, and cultivate communities. Own your communications, organize your resources, and share documents. Tlon is a decentralized platform that offers a full, communal suite of tools for messaging, writing and sharing media with others.' color+0xde.dede image+'https://bootstrap.urbit.org/tlon.svg?v=1' - glob-http+['https://bootstrap.urbit.org/glob-0va3jd1.apd6f.3hta3.vi5n9.mupfp.glob' 0va3jd1.apd6f.3hta3.vi5n9.mupfp] + glob-http+['https://bootstrap.urbit.org/glob-0vgsiup.8b5ag.nf309.99824.uuril.glob' 0vgsiup.8b5ag.nf309.99824.uuril] base+'groups' version+[6 3 0] website+'https://tlon.io' From 85de789b06c38cea241b09b9247a673e55533799 Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Mon, 30 Sep 2024 10:56:25 -0500 Subject: [PATCH 154/157] Only show lure invite button if we're an admin in a new empty group --- packages/ui/src/components/Channel/EmptyChannelNotice.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx index 2ca5846ca4..57ef0847fa 100644 --- a/packages/ui/src/components/Channel/EmptyChannelNotice.tsx +++ b/packages/ui/src/components/Channel/EmptyChannelNotice.tsx @@ -37,7 +37,9 @@ export function EmptyChannelNotice({ {noticeText} - + {isGroupAdmin && isFirstVisit && isWelcomeChannel && ( + + )} ); } From 5e7929c23f8264bbe956aaf7b7f656765713ca2c Mon Sep 17 00:00:00 2001 From: Dan Brewster Date: Mon, 30 Sep 2024 16:18:02 -0400 Subject: [PATCH 155/157] fix expo build (#3964) * fix build failure * fix type error * move eas postinstall to mobile package * remove unnecessary build steps * add install step back (need dotenv installed) * fix pnpm syntax --- .github/workflows/mobile-build.yml | 2 -- apps/tlon-mobile/package.json | 3 ++- packages/app/features/top/ChannelScreen.tsx | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/mobile-build.yml b/.github/workflows/mobile-build.yml index a6091ac4c3..b9ef62a811 100644 --- a/.github/workflows/mobile-build.yml +++ b/.github/workflows/mobile-build.yml @@ -35,8 +35,6 @@ jobs: uses: pnpm/action-setup@v3 - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build packages - run: pnpm build:all - name: Create build vars id: vars run: | diff --git a/apps/tlon-mobile/package.json b/apps/tlon-mobile/package.json index d1ac49f981..5387c70d96 100644 --- a/apps/tlon-mobile/package.json +++ b/apps/tlon-mobile/package.json @@ -24,7 +24,8 @@ "test-ui": "jest", "tsc": "tsc --noEmit", "build": "pnpm generate", - "cosmos": "cosmos-native" + "cosmos": "cosmos-native", + "eas-build-post-install": "pnpm -w run build:all" }, "lint-staged": { "*.{js,jsx,ts,tsx,json,md,html}": [ diff --git a/packages/app/features/top/ChannelScreen.tsx b/packages/app/features/top/ChannelScreen.tsx index 51b3d9217e..96220b81f5 100644 --- a/packages/app/features/top/ChannelScreen.tsx +++ b/packages/app/features/top/ChannelScreen.tsx @@ -1,3 +1,5 @@ +import { useFocusEffect } from '@react-navigation/native'; +import { useIsFocused } from '@react-navigation/native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { createDevLogger } from '@tloncorp/shared/dist'; import * as db from '@tloncorp/shared/dist/db'; @@ -22,9 +24,7 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import { useChannelContext } from '../../hooks/useChannelContext'; import { useChannelNavigation } from '../../hooks/useChannelNavigation'; import { useChatSettingsNavigation } from '../../hooks/useChatSettingsNavigation'; -import { useFocusEffect } from '@react-navigation/native'; import { useGroupActions } from '../../hooks/useGroupActions'; -import { useIsFocused } from '@react-navigation/native'; import type { RootStackParamList } from '../../navigation/types'; const logger = createDevLogger('ChannelScreen', false); @@ -119,7 +119,7 @@ export default function ChannelScreen(props: Props) { // // ------------------------| syncedAt // session.startTime |--------------- - if (syncedAt >= session.startTime) { + if (syncedAt >= (session.startTime ?? 0)) { return true; } From 823f14db66c538069fc0ac92183c89b2d8a6ae5e Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Mon, 30 Sep 2024 15:58:05 -0500 Subject: [PATCH 156/157] ops: version bump --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index 72123dc02c..b67975f173 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -4,7 +4,7 @@ image+'https://bootstrap.urbit.org/tlon.svg?v=1' glob-http+['https://bootstrap.urbit.org/glob-0vgsiup.8b5ag.nf309.99824.uuril.glob' 0vgsiup.8b5ag.nf309.99824.uuril] base+'groups' - version+[6 3 0] + version+[6 3 1] website+'https://tlon.io' license+'MIT' == From 3060f0578bd8940bc77ec995ea2d5d4f0e36666f Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Mon, 30 Sep 2024 15:59:37 -0500 Subject: [PATCH 157/157] ops: correct version --- desk/desk.docket-0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 index b67975f173..21c18fb17e 100644 --- a/desk/desk.docket-0 +++ b/desk/desk.docket-0 @@ -4,7 +4,7 @@ image+'https://bootstrap.urbit.org/tlon.svg?v=1' glob-http+['https://bootstrap.urbit.org/glob-0vgsiup.8b5ag.nf309.99824.uuril.glob' 0vgsiup.8b5ag.nf309.99824.uuril] base+'groups' - version+[6 3 1] + version+[6 4 0] website+'https://tlon.io' license+'MIT' ==