From 70a41ad548484dbfc12cebe863c39a2d1cc40e82 Mon Sep 17 00:00:00 2001 From: Mayel de Borniol Date: Thu, 3 Oct 2024 12:53:29 +0100 Subject: [PATCH] https://github.com/bonfire-networks/bonfire-app/issues/1031 --- assets/static/images/icons/icons.css | 2 +- .../icons/vscode-icons/file-type-volt.svg | 1 + lib/components/links/link_live.ex | 74 +++++++++++++++++++ .../modals/open_external_link_live.ex | 10 +++ .../modals/open_external_link_live.hooks.js | 34 +++++++++ .../modals/open_external_link_live.sface | 3 + lib/components/modals/open_modal_live.ex | 60 ++------------- lib/components/modals/open_preview_live.ex | 12 +-- .../modals/preview_content_live.sface | 23 +++--- lib/components/modals/reusable_modal_live.ex | 66 +++++++++++++++++ .../modals/reusable_modal_live.sface | 27 ++++++- .../maybe_static_generator_plug.ex | 22 +++++- lib/web.ex | 7 +- 13 files changed, 261 insertions(+), 80 deletions(-) create mode 100644 assets/static/images/icons/vscode-icons/file-type-volt.svg create mode 100644 lib/components/modals/open_external_link_live.ex create mode 100644 lib/components/modals/open_external_link_live.hooks.js create mode 100644 lib/components/modals/open_external_link_live.sface diff --git a/assets/static/images/icons/icons.css b/assets/static/images/icons/icons.css index 2f483517..7e124055 100644 --- a/assets/static/images/icons/icons.css +++ b/assets/static/images/icons/icons.css @@ -69,6 +69,7 @@ [iconify="clarity:tree-view-solid"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Crect width='6' height='6' x='10' y='26' fill='currentColor' class='clr-i-solid clr-i-solid-path-1' rx='1' ry='1'/%3E%3Cpath fill='currentColor' d='M15 16h-4a1 1 0 0 0-1 1v1.2H5.8V12H7a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h1.2v17.8H11a.8.8 0 1 0 0-1.6H5.8v-8.4H10V21a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1' class='clr-i-solid clr-i-solid-path-2'/%3E%3Cpath fill='currentColor' d='M33 8H10v2h23a1 1 0 0 0 0-2' class='clr-i-solid clr-i-solid-path-3'/%3E%3Cpath fill='currentColor' d='M33 18H18v2h15a1 1 0 0 0 0-2' class='clr-i-solid clr-i-solid-path-4'/%3E%3Cpath fill='currentColor' d='M33 28H18v2h15a1 1 0 0 0 0-2' class='clr-i-solid clr-i-solid-path-5'/%3E%3Cpath fill='none' d='M0 0h36v36H0z'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } [iconify="dashicons:text-page"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='currentColor' d='M3 1v18h14V1zm9 13H6v-1h6zm2-3H6v-1h8zm0-3H6V7h8zm0-3H6V4h8z'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } [iconify="el:network"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 1200'%3E%3Cpath fill='currentColor' d='M1.422 630.365C-.4 620.378.049 611.896.049 601.507v-2.748c163.322-14.011 341.241-55.15 473.665-149.787c37.996 17.409 75.363 15.034 111.208-2.748c75.104 75.855 148.807 128.574 247.13 159.405c10.067 25.652 26.086 45.35 48.054 59.091c-26.543 65.961-63.612 124.136-111.209 174.521c-70.346-50.674-163.23-13.979-194.957 59.091c-220.012-2.384-441.761-98.642-572.518-267.967m571.143 354.54c-112.313 58.005-230.856 89.276-351.474 82.451C127.796 989.072 60.567 886.74 26.135 780.151c151.522 130.23 352.912 204.549 546.43 204.754m248.503-16.49c127.807-26.659 245.244-78.05 340.488-156.657c-125.012 325.938-501.479 474.94-810.035 336.676c100.162-14.432 194.251-49.025 274.588-94.817c80.286 46.004 175.832-2.388 194.959-85.202m236.146-335.302c49.196-3.631 97.167-7.251 142.786-15.116c-.089 12.283-1.357 24.374-1.373 35.729c-85.771 109.767-214.696 184.762-343.235 219.87c47.966-58.233 83.545-122.923 108.462-188.264c39.174-5.082 71.173-23.077 93.36-52.219m21.968-87.948c-5.416-40.734-25.791-73.796-57.664-94.819c10.072-93.269 11.733-184.275 4.119-272.089c96.156 99.264 154.383 225.964 170.244 351.792c-34.781 7.329-73.682 12.368-116.699 15.116M410.559 387.133C289.275 463.55 147.263 500.671 6.914 512.185C41.964 293.143 191.16 112.112 391.337 38.09c5.438 71.134 21.91 139.81 48.054 199.257c-41.973 42.622-51.941 97.264-28.832 149.786m236.145-101.69c63.215-78.489 115.77-158.695 145.532-252.851C843.492 50 889.715 72.444 930.903 99.928c14.386 113.183 16.386 225.917 5.491 331.18c-49.729 8.487-88.823 38.744-105.717 82.45c-73.416-26.576-133.514-76.068-186.72-129.174c13.364-34.477 13.869-66.794 2.747-98.941m-127.683-81.077c-25.545-63.148-42.218-124.34-42.562-191.012c76.599-17.623 159.296-17.036 232.027-2.748c-27.786 77.786-71.688 149.88-118.073 208.876c-16.321-6.971-56.075-22.499-71.392-15.116'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } +[iconify="entypo:emoji-happy"]{--Iy:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='currentColor' d='M10 .4A9.6 9.6 0 0 0 .4 10a9.6 9.6 0 1 0 19.2-.001C19.6 4.698 15.301.4 10 .4m0 17.199A7.6 7.6 0 1 1 10 2.4a7.6 7.6 0 1 1 0 15.199M7.501 9.75C8.329 9.75 9 8.967 9 8s-.672-1.75-1.5-1.75S6 7.033 6 8s.672 1.75 1.501 1.75m4.999 0c.829 0 1.5-.783 1.5-1.75s-.672-1.75-1.5-1.75S11 7.034 11 8s.672 1.75 1.5 1.75m1.841 1.586a.756.756 0 0 0-1.008.32c-.034.066-.869 1.593-3.332 1.593c-2.451 0-3.291-1.513-3.333-1.592a.75.75 0 0 0-1.339.678c.05.099 1.248 2.414 4.672 2.414c3.425 0 4.621-2.316 4.67-2.415a.745.745 0 0 0-.33-.998'/%3E%3C/svg%3E");-webkit-mask-image:var(--Iy);mask-image:var(--Iy)} [iconify="eos-icons:network"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='M12 4a8 8 0 1 0 8 8a8.003 8.003 0 0 0-8-8m-6 8a6 6 0 0 1 .105-1.095L9.6 14.4v.8a1.605 1.605 0 0 0 1.6 1.6v1.14A6.004 6.004 0 0 1 6 12m9.2 3.2h-.8v-2.4a.8.8 0 0 0-.8-.8H8.8v-1.6h1.6a.8.8 0 0 0 .8-.8V8h1.6a1.6 1.6 0 0 0 1.59-1.5a5.985 5.985 0 0 1 2.137 9.426A1.57 1.57 0 0 0 15.2 15.2M13 1a1 1 0 1 1-1-1a1 1 0 0 1 1 1m5 1a1 1 0 1 1-1-1a1 1 0 0 1 1 1m4 4a1 1 0 1 1-1-1a1 1 0 0 1 1 1m2 6a1 1 0 1 1-1-1a1 1 0 0 1 1 1m-2 6a1 1 0 1 1-1-1a1 1 0 0 1 1 1m-4 4a1 1 0 1 1-1-1a1 1 0 0 1 1 1m-5 1a1 1 0 1 1-1-1a1 1 0 0 1 1 1m-5-1a1 1 0 1 1-1-1a1 1 0 0 1 1 1m-4-4a1 1 0 1 1-1-1a1 1 0 0 1 1 1m-2-6a1 1 0 1 1-1-1a1 1 0 0 1 1 1m2-6a1 1 0 1 1-1-1a1 1 0 0 1 1 1m4-4a1 1 0 1 1-1-1a1 1 0 0 1 1 1'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } [iconify="eos-icons:pin"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='M18.27 9.81h-2.82L9.77 4.13l.71-.71l-1.42-1.41l-7.07 7.07l1.42 1.41l.71-.71l5.67 5.68h-.01v2.83l1.42 1.42l3.54-3.55l4.77 4.77l1.41-1.41l-4.77-4.77l3.53-3.53z'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } [iconify="fa-solid:list"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M80 368H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16m0-320H16A16 16 0 0 0 0 64v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16m0 160H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16m416 176H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16m0-320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16m0 160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } @@ -307,4 +308,3 @@ [iconify="teenyicons:reddit-solid"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 15'%3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M7.621 1.015A.5.5 0 0 0 7 1.5v3.515A8.16 8.16 0 0 0 3.455 6.06c-.388-.341-.911-.515-1.433-.515A2.022 2.022 0 0 0 0 7.566c0 .747.406 1.394 1.007 1.742A2.725 2.725 0 0 0 1 9.5c0 1.243.852 2.376 2.021 3.17C4.206 13.475 5.804 14 7.5 14c1.696 0 3.294-.525 4.479-1.33C13.148 11.876 14 10.743 14 9.5c0-.064-.002-.128-.007-.192A2.008 2.008 0 0 0 15 7.566a2.022 2.022 0 0 0-2.022-2.022c-.522 0-1.045.174-1.433.515A8.16 8.16 0 0 0 8 5.015V2.14l3.055.764a1.5 1.5 0 1 0 .074-1.012zM12.5 3a.5.5 0 0 1-.5-.492v-.016a.5.5 0 1 1 .5.508M5 9h1V8H5zm2.5 2c-.987 0-1.617-.17-2.306-.46l-.388.92c.796.336 1.558.54 2.694.54s1.898-.204 2.694-.54l-.388-.92c-.69.29-1.32.46-2.306.46M10 9H9V8h1z' clip-rule='evenodd'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } [iconify="typcn:info-large"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='M13.839 17.525c-.006.002-.559.186-1.039.186c-.265 0-.372-.055-.406-.079c-.168-.117-.48-.336.054-1.4l1-1.994c.593-1.184.681-2.329.245-3.225c-.356-.733-1.039-1.236-1.92-1.416a4.776 4.776 0 0 0-.958-.097c-1.849 0-3.094 1.08-3.146 1.126a.5.5 0 0 0 .493.848c.005-.002.559-.187 1.039-.187c.263 0 .369.055.402.078c.169.118.482.34-.051 1.402l-1 1.995c-.594 1.185-.681 2.33-.245 3.225c.356.733 1.038 1.236 1.921 1.416c.314.063.636.097.954.097c1.85 0 3.096-1.08 3.148-1.126a.5.5 0 0 0-.491-.849'/%3E%3Ccircle cx='13' cy='6.001' r='2.5' fill='currentColor'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } [iconify="uil:exit"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='M4 12a1 1 0 0 0 1 1h7.59l-2.3 2.29a1 1 0 0 0 0 1.42a1 1 0 0 0 1.42 0l4-4a1 1 0 0 0 .21-.33a1 1 0 0 0 0-.76a1 1 0 0 0-.21-.33l-4-4a1 1 0 1 0-1.42 1.42l2.3 2.29H5a1 1 0 0 0-1 1M17 2H7a3 3 0 0 0-3 3v3a1 1 0 0 0 2 0V5a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-3a1 1 0 0 0-2 0v3a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } -[iconify="vscode-icons:file-type-volt"] { --Iy: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='gray' d='M12.068 2h6.866l-2.73 8.421l7.322-1.331L13.115 30l3.037-14.505L8.474 17z'/%3E%3C/svg%3E"); -webkit-mask-image: var(--Iy); mask-image: var(--Iy); } diff --git a/assets/static/images/icons/vscode-icons/file-type-volt.svg b/assets/static/images/icons/vscode-icons/file-type-volt.svg new file mode 100644 index 00000000..8b7874ef --- /dev/null +++ b/assets/static/images/icons/vscode-icons/file-type-volt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/components/links/link_live.ex b/lib/components/links/link_live.ex index c4605266..c493a0c1 100644 --- a/lib/components/links/link_live.ex +++ b/lib/components/links/link_live.ex @@ -33,6 +33,8 @@ defmodule Bonfire.UI.Common.LinkLive do @doc "What browser window/frame to target, eg. `_blank`" prop target, :string, default: nil + prop external_link_warnings, :boolean, default: false + @doc """ Additional attributes to add onto the generated element """ @@ -71,6 +73,78 @@ defmodule Bonfire.UI.Common.LinkLive do end end + def render(%{to: "http" <> _, external_link_warnings: true} = assigns) do + ~F""" +

This is an external link, please check where it leads before following it. If you have concerns it may be malicious you can check one of the URL reputation services below, or copy and paste the URL into a tool of your choice.

+ + + <#Icon iconify="ri:file-copy-line" class="w-4 h-4 shrink-0" /> + {l("Copy")} + + + Keyword.merge("aria-label": @label, target: @target)} + > + <#slot>{@label} + + +

+ {#case URI.encode_www_form(@to)} + {#match url_encoded} + {!-- TODO: make these configurable --} + Keyword.merge("aria-label": @label, target: @target)} + class="btn btn-xs" + >{l("Google Safe Browsing")} + + Keyword.merge("aria-label": @label, target: @target)} + class="btn btn-xs" + >{l("URLvoid")} + + Keyword.merge("aria-label": @label, target: @target)} + class="btn btn-xs" + >{l("URLscan")} + + Keyword.merge("aria-label": @label, target: @target)} + class="btn btn-xs" + >{l("VirusTotal")} + + Keyword.merge("aria-label": @label, target: @target)} + class="btn btn-xs" + >{l("Sucuri")} + + Keyword.merge("aria-label": @label, target: @target)} + class="btn btn-xs" + >{l("Norton")} + + Keyword.merge("aria-label": @label, target: @target)} + class="btn btn-xs" + >{l("AbuseIPDB")} + {/case} +

+ """ + end + def render(%{to: "http" <> _} = assigns) do ~F""" { + const link = e.target.closest('a'); + if (!link) return; + + e.preventDefault(); + const url = window.prompt("Confirm or edit URL to follow:", link.href); + + if (url) { + window.location.href = url; + } + }); + } +}; + +let LinksDangerModal = { + mounted() { + this.el.addEventListener("click", e => { + const link = e.target.closest('a'); + if (!link) return; + + const url = link.href; + + if (url && url.indexOf("/") != 0) { + e.preventDefault(); + this.pushEvent("Bonfire.UI.Common.ReusableModalLive:prompt_external_link", { url: url }) + } + }); + } +}; + +export { LinksClickPrompt, LinksDangerModal }; diff --git a/lib/components/modals/open_external_link_live.sface b/lib/components/modals/open_external_link_live.sface new file mode 100644 index 00000000..38817ee7 --- /dev/null +++ b/lib/components/modals/open_external_link_live.sface @@ -0,0 +1,3 @@ +
+ <#slot /> +
\ No newline at end of file diff --git a/lib/components/modals/open_modal_live.ex b/lib/components/modals/open_modal_live.ex index 79fae0b1..4606ce8b 100644 --- a/lib/components/modals/open_modal_live.ex +++ b/lib/components/modals/open_modal_live.ex @@ -106,51 +106,15 @@ defmodule Bonfire.UI.Common.OpenModalLive do def open(reusable_modal_id \\ nil) do debug("open!") - set([show: true], reusable_modal_id || @default_modal_id) + ReusableModalLive.set([show: true], reusable_modal_id) end def close(reusable_modal_id \\ nil) do debug("close!") - set( + ReusableModalLive.set( [show: false] ++ ReusableModalLive.default_assigns(), - reusable_modal_id || @default_modal_id - ) - end - - def set(assigns, reusable_modal_id \\ nil) do - maybe_set_assigns( - e( - assigns, - :reusable_modal_component, - ReusableModalLive - ), - reusable_modal_id || e(assigns, :reusable_modal_id, nil) || - if(e(assigns, :__context__, :sticky, nil), - do: "persistent_modal", - else: @default_modal_id - ), - assigns - ) - - # case assigns[:root_assigns] do - # root_assigns when is_list(root_assigns) and root_assigns !=[] -> - # send_self(assigns[:root_assigns]) - - # _ -> nil - # end - end - - def maybe_set_assigns(_component, "media_player_modal", assigns) do - # TODO: detect if we're already in the sticky view - Bonfire.UI.Common.PersistentLive.maybe_send(assigns, {:media_player, assigns}) - end - - def maybe_set_assigns(component, reusable_modal_id, assigns) do - maybe_send_update( - component, - reusable_modal_id, - assigns + reusable_modal_id ) end @@ -165,31 +129,19 @@ defmodule Bonfire.UI.Common.OpenModalLive do ) # copy all of this component's assigns to the reusable modal (including slots!) - set(assigns(socket)) + ReusableModalLive.set(assigns(socket)) {:noreply, socket} end def handle_event("close", _, socket) do - close( - e(assigns(socket), :reusable_modal_id, nil) || - if(e(assigns(socket), :__context__, :sticky, nil), - do: "persistent_modal", - else: @default_modal_id - ) - ) + close(ReusableModalLive.modal_id(assigns(socket))) {:noreply, socket} end def handle_event("set_value", %{"value" => value}, socket) do - close( - e(assigns(socket), :reusable_modal_id, nil) || - if(e(assigns(socket), :__context__, :sticky, nil), - do: "persistent_modal", - else: @default_modal_id - ) - ) + close(ReusableModalLive.modal_id(assigns(socket))) {:noreply, socket |> assign(:value, value)} end diff --git a/lib/components/modals/open_preview_live.ex b/lib/components/modals/open_preview_live.ex index 294bfedf..e4d1e1d6 100644 --- a/lib/components/modals/open_preview_live.ex +++ b/lib/components/modals/open_preview_live.ex @@ -32,10 +32,10 @@ defmodule Bonfire.UI.Common.OpenPreviewLive do """ slot open_btn - def handle_event("close", _, socket) do - close() - {:noreply, assign(socket, show: false)} - end + # def handle_event("close", _, socket) do + # close() + # {:noreply, assign(socket, show: false)} + # end # def handle_event("open", _, socket) do # open() @@ -45,7 +45,7 @@ defmodule Bonfire.UI.Common.OpenPreviewLive do # def open() do # debug("open!") - # Bonfire.UI.Common.OpenModalLive.set( + # Bonfire.UI.Common.ReusableModalLive.set( # show: true, # reusable_modal_component: Bonfire.UI.Common.PreviewContentLive, # reusable_modal_id: "preview_content" @@ -55,7 +55,7 @@ defmodule Bonfire.UI.Common.OpenPreviewLive do def close() do debug("close!") - Bonfire.UI.Common.OpenModalLive.set( + Bonfire.UI.Common.ReusableModalLive.set( show: false, reusable_modal_component: Bonfire.UI.Common.PreviewContentLive, reusable_modal_id: "preview_content" diff --git a/lib/components/modals/preview_content_live.sface b/lib/components/modals/preview_content_live.sface index 1e40d038..e3f77de9 100644 --- a/lib/components/modals/preview_content_live.sface +++ b/lib/components/modals/preview_content_live.sface @@ -40,24 +40,25 @@ data-id="modal-contents" > <#slot> - {#if e(@modal_assigns, :preview_component_stateful?, nil) == true and - module_enabled?(@modal_assigns[:preview_component], @__context__)} + {!-- TODO: consolidate with ReusableModal and make modal_component & modal_view seperate props --} + {#if e(@modal_assigns, :modal_component_stateful?, nil) == true and + module_enabled?(@modal_assigns[:modal_component], @__context__)} - {#elseif not is_nil(e(@modal_assigns, :preview_component, nil)) and - module_enabled?(@modal_assigns[:preview_component], @__context__)} + {#elseif not is_nil(e(@modal_assigns, :modal_component, nil)) and + module_enabled?(@modal_assigns[:modal_component], @__context__)} - {#elseif not is_nil(e(@modal_assigns, :preview_view, nil)) and - module_enabled?(@modal_assigns[:preview_view], @__context__)} - {live_render(@socket, @modal_assigns[:preview_view], - id: "preview_view", + {#elseif not is_nil(e(@modal_assigns, :modal_view, nil)) and + module_enabled?(@modal_assigns[:modal_view], @__context__)} + {live_render(@socket, @modal_assigns[:modal_view], + id: "modal_view", session: %{"params" => @modal_assigns} )} {#else} diff --git a/lib/components/modals/reusable_modal_live.ex b/lib/components/modals/reusable_modal_live.ex index 82f3b9a2..db7693b6 100644 --- a/lib/components/modals/reusable_modal_live.ex +++ b/lib/components/modals/reusable_modal_live.ex @@ -1,10 +1,13 @@ defmodule Bonfire.UI.Common.ReusableModalLive do use Bonfire.UI.Common.Web, :stateful_component + alias Bonfire.UI.Common.ReusableModalLive @moduledoc """ The classic **modal** """ + @default_modal_id "modal" + # make sure to keep these and the Surface props in sync @default_assigns [ title_text: nil, @@ -63,6 +66,11 @@ defmodule Bonfire.UI.Common.ReusableModalLive do prop no_backdrop, :boolean, default: false + @doc """ + Additional assigns to pass on to the optional modal sub-component + """ + prop modal_assigns, :any, default: [] + @doc """ Additional attributes to add onto the modal wrapper """ @@ -93,6 +101,64 @@ defmodule Bonfire.UI.Common.ReusableModalLive do @default_assigns end + def modal_id(assigns) do + e(assigns, :reusable_modal_id, nil) || + if(e(assigns, :__context__, :sticky, nil) || e(assigns, :sticky, nil), + do: "persistent_modal", + else: @default_modal_id + ) + end + + def set(assigns, reusable_modal_id \\ nil) do + maybe_set_assigns( + e( + assigns, + :reusable_modal_component, + ReusableModalLive + ), + reusable_modal_id || modal_id(assigns), + assigns + ) + + # case assigns[:root_assigns] do + # root_assigns when is_list(root_assigns) and root_assigns !=[] -> + # send_self(assigns[:root_assigns]) + + # _ -> nil + # end + end + + defp maybe_set_assigns(_component, "media_player_modal", assigns) do + # TODO: detect if we're already in the sticky view + # debug(assigns, "try sending to media player") + Bonfire.UI.Common.PersistentLive.maybe_send(assigns, {:media_player, assigns}) + end + + defp maybe_set_assigns(component, reusable_modal_id, assigns) do + # debug(assigns, "try sending to reusable modal") + maybe_send_update( + component, + reusable_modal_id, + assigns + ) + end + + def handle_event("prompt_external_link", %{"url" => url}, socket) do + set( + show: true, + modal_assigns: [ + modal_component: LinkLive, + to: url, + label: url, + external_link_warnings: true, + class: "link font-mono" + ], + sticky: e(assigns(socket), :__context__, :sticky, nil) + ) + + {:noreply, socket} + end + def handle_event("close-key", %{"key" => "Escape"} = _attrs, socket) do handle_event("close", %{}, socket) end diff --git a/lib/components/modals/reusable_modal_live.sface b/lib/components/modals/reusable_modal_live.sface index 521c6b08..c69d88be 100644 --- a/lib/components/modals/reusable_modal_live.sface +++ b/lib/components/modals/reusable_modal_live.sface @@ -92,7 +92,32 @@ } data-id="modal-contents" > - <#slot {@default, autocomplete: @autocomplete, value: @value} /> + <#slot {@default, autocomplete: @autocomplete, value: @value}> + {!-- TODO: consolidate with PreviewContent and make modal_component & modal_view seperate props --} + {#if e(@modal_assigns, :modal_component_stateful?, nil) == true and + module_enabled?(@modal_assigns[:modal_component], @__context__)} + + {#elseif not is_nil(e(@modal_assigns, :modal_component, nil)) and + module_enabled?(@modal_assigns[:modal_component], @__context__)} + + {#elseif not is_nil(e(@modal_assigns, :modal_view, nil)) and + module_enabled?(@modal_assigns[:modal_view], @__context__)} + {live_render(@socket, @modal_assigns[:modal_view], + id: "modal_view", + session: %{"params" => @modal_assigns} + )} + {#else} + {!-- empty --} + {/if} +