diff --git a/public/images/icons/bluesky.svg b/public/images/icons/bluesky.svg new file mode 100644 index 00000000..87d94074 --- /dev/null +++ b/public/images/icons/bluesky.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/icons/social/bluesky.svg b/public/images/icons/social/bluesky.svg new file mode 100644 index 00000000..6a3c21d4 --- /dev/null +++ b/public/images/icons/social/bluesky.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/icons/facebook-blue.svg b/public/images/icons/social/facebook.svg similarity index 96% rename from public/images/icons/facebook-blue.svg rename to public/images/icons/social/facebook.svg index 0a71adae..5f4f4d26 100644 --- a/public/images/icons/facebook-blue.svg +++ b/public/images/icons/social/facebook.svg @@ -1,3 +1,3 @@ - + diff --git a/public/images/icons/social/threads.svg b/public/images/icons/social/threads.svg new file mode 100644 index 00000000..809c12d8 --- /dev/null +++ b/public/images/icons/social/threads.svg @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/public/images/icons/twitter-blue.svg b/public/images/icons/social/x.svg similarity index 83% rename from public/images/icons/twitter-blue.svg rename to public/images/icons/social/x.svg index ac854c94..96db8608 100644 --- a/public/images/icons/twitter-blue.svg +++ b/public/images/icons/social/x.svg @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/public/images/icons/threads.svg b/public/images/icons/threads.svg new file mode 100644 index 00000000..fa2aaa6b --- /dev/null +++ b/public/images/icons/threads.svg @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/public/images/icons/tiktok.svg b/public/images/icons/tiktok.svg new file mode 100644 index 00000000..e4598089 --- /dev/null +++ b/public/images/icons/tiktok.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/icons/twitter.svg b/public/images/icons/x.svg similarity index 100% rename from public/images/icons/twitter.svg rename to public/images/icons/x.svg diff --git a/src/components.d.ts b/src/components.d.ts index 78512444..7f569424 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -114,6 +114,11 @@ export namespace Components { } interface UiScrollToTopButton { } + interface UiShareLinks { + "additionalLinks": HTMLLinkElement; + "shareText": string; + "shareUrl": string; + } interface UiSingleInput { "buttonLabel": string; "getCurrentValue": () => Promise; @@ -318,6 +323,12 @@ declare global { prototype: HTMLUiScrollToTopButtonElement; new (): HTMLUiScrollToTopButtonElement; }; + interface HTMLUiShareLinksElement extends Components.UiShareLinks, HTMLStencilElement { + } + var HTMLUiShareLinksElement: { + prototype: HTMLUiShareLinksElement; + new (): HTMLUiShareLinksElement; + }; interface HTMLUiSingleInputElement extends Components.UiSingleInput, HTMLStencilElement { } var HTMLUiSingleInputElement: { @@ -357,6 +368,7 @@ declare global { "ui-modal": HTMLUiModalElement; "ui-pizza-list": HTMLUiPizzaListElement; "ui-scroll-to-top-button": HTMLUiScrollToTopButtonElement; + "ui-share-links": HTMLUiShareLinksElement; "ui-single-input": HTMLUiSingleInputElement; } } @@ -475,6 +487,11 @@ declare namespace LocalJSX { } interface UiScrollToTopButton { } + interface UiShareLinks { + "additionalLinks"?: HTMLLinkElement; + "shareText"?: string; + "shareUrl"?: string; + } interface UiSingleInput { "buttonLabel"?: string; "label"?: string; @@ -516,6 +533,7 @@ declare namespace LocalJSX { "ui-modal": UiModal; "ui-pizza-list": UiPizzaList; "ui-scroll-to-top-button": UiScrollToTopButton; + "ui-share-links": UiShareLinks; "ui-single-input": UiSingleInput; } } @@ -555,6 +573,7 @@ declare module "@stencil/core" { "ui-modal": LocalJSX.UiModal & JSXBase.HTMLAttributes; "ui-pizza-list": LocalJSX.UiPizzaList & JSXBase.HTMLAttributes; "ui-scroll-to-top-button": LocalJSX.UiScrollToTopButton & JSXBase.HTMLAttributes; + "ui-share-links": LocalJSX.UiShareLinks & JSXBase.HTMLAttributes; "ui-single-input": LocalJSX.UiSingleInput & JSXBase.HTMLAttributes; } } diff --git a/src/components/app-root/app-root.tsx b/src/components/app-root/app-root.tsx index df7ba166..f3c4593b 100644 --- a/src/components/app-root/app-root.tsx +++ b/src/components/app-root/app-root.tsx @@ -131,8 +131,8 @@ export class AppRoot {
diff --git a/src/components/form-donate/form-donate.scss b/src/components/form-donate/form-donate.scss index 06587c3b..971411a5 100644 --- a/src/components/form-donate/form-donate.scss +++ b/src/components/form-donate/form-donate.scss @@ -78,42 +78,6 @@ form-donate { } } - .share-donation-link-container { - &.can-native-share { - // Hide on mobile if native sharing is supported - display: none; - // Show if not on mobile regardless - @media ($tablet) { - display: block; - } - } - } - - .share-donation-links { - padding: 0; - - li { - display: block; - margin: 0 auto 10px; - - @media ($tablet) { - display: inline-block; - margin: 0 10px 0 0; - } - - .share-donation-link { - img, - span { - pointer-events: none; // important - } - - img { - margin-right: 8px; - } - } - } - } - #donation-error { margin-bottom: 1rem; h2 { diff --git a/src/components/form-donate/form-donate.tsx b/src/components/form-donate/form-donate.tsx index d73a5ed5..8a527388 100644 --- a/src/components/form-donate/form-donate.tsx +++ b/src/components/form-donate/form-donate.tsx @@ -1,4 +1,4 @@ -import { Build, Component, h, Host, Prop, State } from "@stencil/core"; +import { Component, h, Host, Prop, State } from "@stencil/core"; import { PizzaApi } from "../../api"; @@ -18,7 +18,6 @@ export class FormDonate { @State() private donationType: string = "donation"; @State() private amount?: number | null; @State() private enteredOther?: boolean = false; - @State() private canNativeShare: boolean = false; @State() private error?: string | null; public componentWillLoad() { @@ -51,11 +50,6 @@ export class FormDonate { } public render() { - if (Build.isBrowser) { - // Determine if `navigator.share` is supported in browser (native device sharing) - this.canNativeShare = navigator && navigator.share ? true : false; - } - const activateCustomAmountRadio = () => { this.enteredOther = true; @@ -120,48 +114,6 @@ export class FormDonate { const shareText = "I just donated" + shareAmount + " to Pizza to the Polls to help keep Democracy Delicious this year - you should too! #democracyisdelicious"; const shareUrl = "https://polls.pizza/donate"; // add URL tracking parameters here, if desired - // Native sharing on device via `navigator.share` - supported on mobile, tablets, and some browsers - const nativeShare = async () => { - if (!navigator || !navigator.share) { - this.canNativeShare = false; - return; - } - - try { - await navigator.share({ - title: shareText, - text: shareText, - url: shareUrl, - }); - } catch (error) { - console.log("Sharing failed", error); - } - }; - - // Fallback if native sharing is not available - let metaDescription = document.querySelector("meta[name='description']"); - let shareDescription = ""; - if (metaDescription) { - shareDescription = metaDescription.getAttribute("content") || ""; - } - - const shareTwitterLink = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${shareUrl}&via=PizzaToThePolls`; - - const shareFacebookLink = `https://www.facebook.com/sharer/sharer.php?u=${shareUrl}&title=${encodeURIComponent(shareText)}&description=${encodeURIComponent( - shareDescription, - )}"e=${encodeURIComponent(shareText)}`; - - const openSharePopup = (e: Event) => { - e.preventDefault(); - const linkTarget = e.target as HTMLLinkElement; - const targetURL = linkTarget.getAttribute("href"); - if (!targetURL) { - return; - } - window.open(targetURL, "popup", "width=600,height=600"); - return; - }; - return ( {!this.showConfirmation && ( @@ -233,54 +185,21 @@ export class FormDonate { )} {this.showConfirmation && ( - + )} ); diff --git a/src/components/page-crustclub/page-crustclub.tsx b/src/components/page-crustclub/page-crustclub.tsx index a3678949..76f00660 100644 --- a/src/components/page-crustclub/page-crustclub.tsx +++ b/src/components/page-crustclub/page-crustclub.tsx @@ -1,4 +1,4 @@ -import { Build, Component, h, Host, Prop, State } from "@stencil/core"; +import { Component, h, Host, Prop, State } from "@stencil/core"; import { RouterHistory } from "@stencil/router"; import { PizzaApi } from "../../api"; @@ -12,7 +12,6 @@ const AMOUNTS = [5, 10, 20]; export class PageCrustclub { @State() private amount?: number | null; @State() private showConfirmation: boolean = false; - @State() private canNativeShare: boolean = false; @State() private referral?: string | null; @State() private error?: string | null; @Prop() public history?: RouterHistory; @@ -48,11 +47,6 @@ export class PageCrustclub { } public render() { - if (Build.isBrowser) { - // Determine if `navigator.share` is supported in browser (native device sharing) - this.canNativeShare = navigator && navigator.share ? true : false; - } - const getAmount = (): number | null => { const checked = document.querySelector("input[name=level]:checked") as HTMLInputElement; return checked ? Number(checked.value) : null; @@ -84,48 +78,6 @@ export class PageCrustclub { " to Pizza to the Polls each month to help keep Democracy Delicious this year - you should too! #democracyisdelicious"; const shareUrl = "https://polls.pizza/crustclub"; // add URL tracking parameters here, if desired - // Native sharing on device via `navigator.share` - supported on mobile, tablets, and some browsers - const nativeShare = async () => { - if (!navigator || !navigator.share) { - this.canNativeShare = false; - return; - } - - try { - await navigator.share({ - title: shareText, - text: shareText, - url: shareUrl, - }); - } catch (error) { - console.log("Sharing failed", error); - } - }; - - // Fallback if native sharing is not available - let metaDescription = document.querySelector("meta[name='description']"); - let shareDescription = ""; - if (metaDescription) { - shareDescription = metaDescription.getAttribute("content") || ""; - } - - const shareTwitterLink = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${shareUrl}&via=PizzaToThePolls`; - - const shareFacebookLink = `https://www.facebook.com/sharer/sharer.php?u=${shareUrl}&title=${encodeURIComponent(shareText)}&description=${encodeURIComponent( - shareDescription, - )}"e=${encodeURIComponent(shareText)}`; - - const openSharePopup = (e: Event) => { - e.preventDefault(); - const linkTarget = e.target as HTMLLinkElement; - const targetURL = linkTarget.getAttribute("href"); - if (!targetURL) { - return; - } - window.open(targetURL, "popup", "width=600,height=600"); - return; - }; - return ( @@ -187,47 +139,11 @@ export class PageCrustclub { )} {this.showConfirmation && ( - + + } + > +

Thanks for helping to make the pizza magic happen!

+

Thanks for joining Crust Club. You'll receive a receipt in your email soon.

+ +

Help spread the word by sharing your membership!

+ )}
diff --git a/src/components/page-gift/page-gift.tsx b/src/components/page-gift/page-gift.tsx index d129d6ed..bd18c700 100644 --- a/src/components/page-gift/page-gift.tsx +++ b/src/components/page-gift/page-gift.tsx @@ -1,4 +1,4 @@ -import { Build, Component, h, Host, Prop, State } from "@stencil/core"; +import { Component, h, Host, Prop, State } from "@stencil/core"; import { RouterHistory } from "@stencil/router"; import { PizzaApi } from "../../api"; @@ -15,7 +15,6 @@ export class PageGift { @State() private giftName?: string | null; @State() private giftEmail?: string | null; @State() private showConfirmation: boolean = false; - @State() private canNativeShare: boolean = false; @State() private referral?: string | null; @State() private error?: string | null; @Prop() public history?: RouterHistory; @@ -58,11 +57,6 @@ export class PageGift { } public render() { - if (Build.isBrowser) { - // Determine if `navigator.share` is supported in browser (native device sharing) - this.canNativeShare = navigator && navigator.share ? true : false; - } - const activateCustomAmountRadio = () => { this.enteredOther = true; @@ -128,48 +122,6 @@ export class PageGift { const shareText = "I just gave" + shareAmount + " worth of Pizza to the Polls as a gift to help keep Democracy Delicious this year - you should too! #democracyisdelicious"; const shareUrl = "https://polls.pizza/gift"; // add URL tracking parameters here, if desired - // Native sharing on device via `navigator.share` - supported on mobile, tablets, and some browsers - const nativeShare = async () => { - if (!navigator || !navigator.share) { - this.canNativeShare = false; - return; - } - - try { - await navigator.share({ - title: shareText, - text: shareText, - url: shareUrl, - }); - } catch (error) { - console.log("Sharing failed", error); - } - }; - - // Fallback if native sharing is not available - let metaDescription = document.querySelector("meta[name='description']"); - let shareDescription = ""; - if (metaDescription) { - shareDescription = metaDescription.getAttribute("content") || ""; - } - - const shareTwitterLink = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${shareUrl}&via=PizzaToThePolls`; - - const shareFacebookLink = `https://www.facebook.com/sharer/sharer.php?u=${shareUrl}&title=${encodeURIComponent(shareText)}&description=${encodeURIComponent( - shareDescription, - )}"e=${encodeURIComponent(shareText)}`; - - const openSharePopup = (e: Event) => { - e.preventDefault(); - const linkTarget = e.target as HTMLLinkElement; - const targetURL = linkTarget.getAttribute("href"); - if (!targetURL) { - return; - } - window.open(targetURL, "popup", "width=600,height=600"); - return; - }; - const resetDonationForm = (e: Event) => { this.showConfirmation = false; this.amount = null; @@ -257,7 +209,17 @@ export class PageGift { )} {this.showConfirmation && ( - + )} diff --git a/src/components/ui-share-links/ui-share-links.scss b/src/components/ui-share-links/ui-share-links.scss new file mode 100644 index 00000000..02ef1339 --- /dev/null +++ b/src/components/ui-share-links/ui-share-links.scss @@ -0,0 +1,43 @@ +ui-share-links { + &.can-native-share { + // Hide on mobile if native sharing is supported + display: none; + // Show if not on mobile regardless + @media ($tablet) { + display: block; + } + } +} + +.share-links { + padding: 0; + + li { + display: block; + margin: 0 auto 10px; + + @media ($tablet) { + display: inline-block; + margin: 0 10px 10px 0; + } + + .share-donation-link { + img, + span { + pointer-events: none; // important + } + + img { + margin-right: 8px; + } + } + + stencil-route-link a { + &, + &:active { + color: white; + text-decoration: none; + } + } + } +} diff --git a/src/components/ui-share-links/ui-share-links.tsx b/src/components/ui-share-links/ui-share-links.tsx new file mode 100644 index 00000000..6a15e74d --- /dev/null +++ b/src/components/ui-share-links/ui-share-links.tsx @@ -0,0 +1,144 @@ +import { Build, Component, h, Host, Prop, State } from "@stencil/core"; + +@Component({ + tag: "ui-share-links", + styleUrl: "ui-share-links.scss", + shadow: false, +}) +export class UiShareLinks { + @Prop() public shareText: string; + @Prop() public shareUrl: string; + @Prop() public additionalLinks: HTMLLinkElement; + + @State() private canNativeShare: boolean = false; + + constructor() { + this.shareText = ""; + this.shareUrl = ""; + this.additionalLinks =
; + + if (Build.isBrowser) { + // Determine if `navigator.share` is supported in browser (native device sharing) + this.canNativeShare = navigator && navigator.share ? true : false; + } + } + + public render() { + // Native sharing on device via `navigator.share` - supported on mobile, tablets, and some browsers + const nativeShare = async () => { + if (!navigator || !navigator.share) { + this.canNativeShare = false; + return; + } + + try { + await navigator.share({ + title: this.shareText, + text: this.shareText, + url: this.shareUrl, + }); + } catch (error) { + console.log("Sharing failed", error); + } + }; + + // Fallback if native sharing is not available + let metaDescription = document.querySelector("meta[name='description']"); + let shareDescription = ""; + if (metaDescription) { + shareDescription = metaDescription.getAttribute("content") || ""; + } + + const shareXLink = `https://twitter.com/intent/tweet?text=${encodeURIComponent(this.shareText)}&url=${this.shareUrl}&via=PizzaToThePolls`; + const shareBlueskyLink = `https://bsky.app/intent/compose?text=${encodeURIComponent(`${this.shareText} ${this.shareUrl}`)}`; + + const shareFacebookLink = `https://www.facebook.com/sharer/sharer.php?u=${this.shareUrl}&title=${encodeURIComponent(this.shareText)}&description=${encodeURIComponent( + shareDescription, + )}"e=${encodeURIComponent(this.shareText)}`; + const shareThreadsLink = `https://www.threads.net/intent/post?url=${this.shareUrl}&text=${this.shareText}`; + + const openSharePopup = (e: Event) => { + e.preventDefault(); + const linkTarget = e.target as HTMLLinkElement; + const targetURL = linkTarget.getAttribute("href"); + if (!targetURL) { + return; + } + window.open(targetURL, "popup", "width=600,height=600"); + return; + }; + + return ( + + + + ); + } +} diff --git a/styles/include/_vars.scss b/styles/include/_vars.scss index 96fc3aca..f35130f3 100644 --- a/styles/include/_vars.scss +++ b/styles/include/_vars.scss @@ -14,8 +14,10 @@ $black: #1e1e1e; $tan: #fff6e4; // External brand colors -$twitter: #1da1f2; +$x: #000000; $facebook: #4267b2; +$bluesky: #0886fe; +$threads: #000000; // class name (is-teal), text color, background color $text-colors: ( @@ -61,14 +63,22 @@ $button-colors: ( $blue, $white, ), - "twitter": ( + "x": ( $white, - $twitter, + $x, ), "facebook": ( $white, $facebook, ), + "bluesky": ( + $white, + $bluesky, + ), + "threads": ( + $white, + $threads, + ), ); // Media queries