| 820 |
820
|
|
"""
|
| 821 |
821
|
|
end
|
| 822 |
822
|
|
|
|
823
|
+ |
@doc """
|
|
824
|
+ |
The GitHub sign-in control.
|
|
825
|
+ |
|
|
826
|
+ |
A real form POST, not a link: signing in starts an OAuth round-trip, and a
|
|
827
|
+ |
control labelled "log in" that navigates somewhere else instead is lying
|
|
828
|
+ |
about what it does.
|
|
829
|
+ |
|
|
830
|
+ |
The round-trip leaves the page, so there is a window where the button looks
|
|
831
|
+ |
idle and clickable while a redirect is already in flight. Submitting swaps
|
|
832
|
+ |
the mark for a spinner and disables the control, which both reports that
|
|
833
|
+ |
something is happening and stops a second submission creating a second OAuth
|
|
834
|
+ |
attempt.
|
|
835
|
+ |
|
|
836
|
+ |
The pending state is applied by a hook, but it is also expressed for
|
|
837
|
+ |
`:disabled` alone, so a browser that re-enables the button on back-navigation
|
|
838
|
+ |
or runs no script still shows the right thing.
|
|
839
|
+ |
"""
|
|
840
|
+ |
attr :id, :string, required: true
|
|
841
|
+ |
attr :label, :string, default: "Log in with GitHub"
|
|
842
|
+ |
attr :variant, :atom, default: :primary
|
|
843
|
+ |
attr :size, :atom, default: :md
|
|
844
|
+ |
|
|
845
|
+ |
attr :action, :string,
|
|
846
|
+ |
default: "/auth/github?github_tools=enabled",
|
|
847
|
+ |
doc: "where the sign-in posts; the caller owns the route"
|
|
848
|
+ |
|
|
849
|
+ |
attr :class, :any, default: nil
|
|
850
|
+ |
attr :rest, :global
|
|
851
|
+ |
|
|
852
|
+ |
def github_login(assigns) do
|
|
853
|
+ |
~H"""
|
|
854
|
+ |
<.form
|
|
855
|
+ |
for={%{}}
|
|
856
|
+ |
as={:auth}
|
|
857
|
+ |
id={"#{@id}-form"}
|
|
858
|
+ |
action={@action}
|
|
859
|
+ |
method="post"
|
|
860
|
+ |
class="login-form"
|
|
861
|
+ |
phx-hook=".LoginPending"
|
|
862
|
+ |
{@rest}
|
|
863
|
+ |
>
|
|
864
|
+ |
<.button id={@id} type="submit" variant={@variant} size={@size} class={["login-button", @class]}>
|
|
865
|
+ |
<.icon name="brand-github" class="login-button__mark" />
|
|
866
|
+ |
<.icon name="circle-dashed" class="login-button__spinner" />
|
|
867
|
+ |
{@label}
|
|
868
|
+ |
</.button>
|
|
869
|
+ |
</.form>
|
|
870
|
+ |
<script :type={Phoenix.LiveView.ColocatedHook} name=".LoginPending">
|
|
871
|
+ |
export default {
|
|
872
|
+ |
mounted() {
|
|
873
|
+ |
this.button = this.el.querySelector("button[type=submit]")
|
|
874
|
+ |
this.onSubmit = () => {
|
|
875
|
+ |
if (!this.button) return
|
|
876
|
+ |
this.button.dataset.pending = "true"
|
|
877
|
+ |
// Disabled after the event, not during it: disabling a submit
|
|
878
|
+ |
// button inside its own submit handler cancels the submission in
|
|
879
|
+ |
// some browsers.
|
|
880
|
+ |
window.setTimeout(() => { this.button.disabled = true }, 0)
|
|
881
|
+ |
}
|
|
882
|
+ |
this.el.addEventListener("submit", this.onSubmit)
|
|
883
|
+ |
},
|
|
884
|
+ |
destroyed() {
|
|
885
|
+ |
this.el.removeEventListener("submit", this.onSubmit)
|
|
886
|
+ |
},
|
|
887
|
+ |
}
|
|
888
|
+ |
</script>
|
|
889
|
+ |
"""
|
|
890
|
+ |
end
|
|
891
|
+ |
|
| 823 |
892
|
|
@doc """
|
| 824 |
893
|
|
A control that copies text to the clipboard and reports that it did.
|
| 825 |
894
|
|
|