From 8d919157642a4e9ae23b8ee8533d59ba127bc916 Mon Sep 17 00:00:00 2001 From: Ivan Akimov Date: Sun, 24 Mar 2024 10:51:12 -0500 Subject: [PATCH] wip --- CHANGELOG.md | 16 +++- README.md | 91 +++++++++++++++++++- examples/{confirm_email.php => feedback.php} | 23 +++-- examples/qr_code.php | 73 ++++++++++++++++ examples/simple.php | 14 ++- examples/verification.php | 64 ++++++++++++++ src/Collection.php | 28 +++++- src/Components/ComponentId.php | 3 + src/Components/Cryptocurrency.php | 9 ++ src/Components/QrCode.php | 58 +++++++++++++ src/Components/Service.php | 2 + src/Components/Signature.php | 30 +++++++ src/Components/SignatureFont.php | 12 +++ src/Components/SocialItem.php | 2 +- src/Components/StoreBadge.php | 10 +++ src/Components/StoreBadgeItem.php | 25 ++++++ src/Components/StoreBadges.php | 27 ++++++ src/Content.php | 27 ++++++ 18 files changed, 489 insertions(+), 25 deletions(-) rename examples/{confirm_email.php => feedback.php} (59%) create mode 100644 examples/qr_code.php create mode 100644 examples/verification.php create mode 100644 src/Components/Cryptocurrency.php create mode 100644 src/Components/QrCode.php create mode 100644 src/Components/Signature.php create mode 100644 src/Components/SignatureFont.php create mode 100644 src/Components/StoreBadge.php create mode 100644 src/Components/StoreBadgeItem.php create mode 100644 src/Components/StoreBadges.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 943134c..5a5f8bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,23 @@ # CHANGELOG -**v0.1.0-alpha.3:** +## v0.1.0-alpha.4: + +- New social icons: `Service::MASTODON` and `Service::RSS` +- New store badges component +- New QR code component +- New signature component +- New [examples](examples) + +## v0.1.0-alpha.3: - `README.md`: notice about test mode - Support for test mode logging -**v0.1.0-alpha.2:** +## v0.1.0-alpha.2: - `README.md` cleanup (listing of components) - `Image` component now requires only `src`; the other params are optional - `ViewInBrowser` component has changed: text is optional -**v0.1.0-alpha.1:** +## v0.1.0-alpha.1: - Introduced `CHANGELOG.md` - Introduced new services as social icons: - `Service::PHONE` (converts into a link with `tel:` prefix) @@ -24,5 +32,5 @@ - `Service::THREADS` - `Service::TELEGRAM` -**v0.1.0-alpha.0:** +## v0.1.0-alpha.0: - Initial implementation \ No newline at end of file diff --git a/README.md b/README.md index 21e3e88..639f64f 100644 --- a/README.md +++ b/README.md @@ -162,8 +162,7 @@ Link component adds an anchor tag. This is the same as a text component with the ```php Content::builder() - ->link("Confirm Email", "https://example.com/confirm?token=XYZ") // or... - ->text("[Confirm Email](https://example.com/confirm?token=XYZ)") + ->link("Confirm Email", "https://example.com/confirm?token=XYZ") ->build(); ``` @@ -252,6 +251,8 @@ Content::builder() new SocialItem(Service::SNAPCHAT, "Username"), new SocialItem(Service::THREADS, "Username"), new SocialItem(Service::TELEGRAM, "Username"), + new SocialItem(Service::MASTODON, "@Username@example.com"), + new SocialItem(Service::RSS, "https://example.com/blog"), ]) ->build(); ``` @@ -271,6 +272,92 @@ Content::builder() ->build(); ``` + +
Store Badges + +Link to your mobile apps via store badges: + +```php +Content::builder() + ->store_badges([ + new StoreBadgeItem(StoreBadge::APP_STORE, "https://apps.apple.com/us/app/example/id1234567890"), + new StoreBadgeItem(StoreBadge::GOOGLE_PLAY, "https://play.google.com/store/apps/details?id=com.example"), + new StoreBadgeItem(StoreBadge::MICROSOFT_STORE, "https://apps.microsoft.com/detail/example"), + ]) + ->build(); +``` + +
+
QR Code + +You can also generate QR codes on the fly. They will be shown as images inside the email. + +Here are all the supported data types: + +```php +// url +Content::builder() + ->qr_code("https://example.com") + ->build(); + +// email +Content::builder() + ->component(QrCode::email("user@example.com")) + ->build(); + +// phone +Content::builder() + ->component(QrCode::phone("123-456-7890")) + ->build(); + +// sms / text message +Content::builder() + ->component(QrCode::sms("123-456-7890")) + ->build(); + +// geo coordinates +Content::builder() + ->component(QrCode::coordinates(37.773972, -122.431297)) + ->build(); + +// crypto address (for now only Bitcoin and Ethereum are supported) +Content::builder() + ->component(QrCode::cryptocurrencyAddress(Cryptocurrency::BITCOIN, "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")) + ->build(); + +// you can also encode any binary data +Content::builder() + ->component(new QrCode(implode(array_map(function($byte) { return chr($byte); }, [1, 2, 3])))) + ->build(); +``` + +
+
Signature + +Generated signatures can be added to your emails to give a bit of a personal touch. This will embed an image with your custom text using one of several available fonts: + +```php +// signature with a default font +Content::builder() + ->signature("John Smith") + ->build(); + +// signature with a custom font +Content::builder() + ->component("John Smith", SignatureFont::REENIE_BEANIE) + ->build(); +``` + +These are the available fonts: + +- `SignatureFont::REENIE_BEANIE` [preview →](https://fonts.google.com/specimen/Reenie+Beanie) +- `SignatureFont::MEOW_SCRIPT` [preview →](https://fonts.google.com/specimen/Meow+Script) +- `SignatureFont::CAVEAT` [preview →](https://fonts.google.com/specimen/Caveat) +- `SignatureFont::ZEYADA` [preview →](https://fonts.google.com/specimen/Zeyada) +- `SignatureFont::PETEMOSS` [preview →](https://fonts.google.com/specimen/Petemoss) + +Signature should not exceed 64 characters. Only alphanumeric characters and most common symbols are allowed. +
--- diff --git a/examples/confirm_email.php b/examples/feedback.php similarity index 59% rename from examples/confirm_email.php rename to examples/feedback.php index 9bdf133..2237c0b 100644 --- a/examples/confirm_email.php +++ b/examples/feedback.php @@ -7,6 +7,7 @@ use Templateless\EmailAddress; use Templateless\Templateless; use Templateless\Collection; +use Templateless\Theme; use Templateless\Components\SocialItem; use Templateless\Components\Service; @@ -24,33 +25,37 @@ } $header = Collection::builder() - ->text('# ExampleApp') + ->image('https://templateless.net/myco.webp', null, 100, null, 'MyCo') ->build(); $footer = Collection::builder() ->socials([ - new SocialItem(Service::TWITTER, "ExampleApp"), - new SocialItem(Service::GITHUB, "ExampleApp"), + new SocialItem(Service::TWITTER, 'MyCo'), + new SocialItem(Service::GITHUB, 'MyCo'), ]) - ->link('Unsubscribe', 'https://example.com/unsubscribe?id=123') ->build(); $content = Content::builder() + ->theme(Theme::SIMPLE) ->header($header) - ->text('Hello world') + ->text("Hey Alex,") + ->text("I'm Jamie, founder of **MyCo**.") + ->text("Could you spare a moment to reply to this email with your thoughts on our service? Your feedback is invaluable and directly influences our improvements.") + ->text("When you hit reply, your email will go directly to me, and I read each and every one.") + ->text("Thanks for your support,") + ->signature("Jamie Parker") + ->text("Jamie Parker\n\nFounder @ [MyCo](https://example.com)") ->footer($footer) ->build(); $email = Email::builder() ->to(new EmailAddress($email_address)) - ->subject("Confirm your email") + ->subject("Thoughts on service?") ->content($content) ->build(); $templateless = new Templateless($api_key); - $result = $templateless->send($email); - - var_dump($result); + $templateless->send($email); } catch (\Exception $e) { echo $e; } diff --git a/examples/qr_code.php b/examples/qr_code.php new file mode 100644 index 0000000..486307e --- /dev/null +++ b/examples/qr_code.php @@ -0,0 +1,73 @@ +image('https://templateless.net/myco.webp', null, 100, null, 'MyCo') + ->build(); + + $app_store_link = "https://apps.apple.com/us/app/example/id1234567890"; + $google_play_link = "https://play.google.com/store/apps/details?id=com.example"; + + $footer = Collection::builder() + ->store_badges([ + new StoreBadgeItem(StoreBadge::APP_STORE, $app_store_link), + new StoreBadgeItem(StoreBadge::GOOGLE_PLAY, $google_play_link), + ]) + ->socials([ + new SocialItem(Service::TWITTER, 'MyCo'), + new SocialItem(Service::GITHUB, 'MyCo'), + ]) + ->build(); + + $content = Content::builder() + ->theme(Theme::SIMPLE) + ->header($header) + ->text("Hey Alex,") + ->text("Thank you for choosing MyCo! To get started with our mobile experience, simply pair your account with our mobile app.") + ->text("Here's how to do it:") + ->text(implode("\n", [ + "1. Download the MyCo app from the [App Store]($app_store_link) or [Google Play]($google_play_link).", + "1. Open the app and select _Pair Device_.", + "1. Scan the QR code below with your mobile device:", + ])) + ->qr_code("https://example.com/qr-code-link") + ->text("Enjoy your seamless experience across devices!") + ->footer($footer) + ->build(); + + $email = Email::builder() + ->to(new EmailAddress($email_address)) + ->subject("How to Pair Device") + ->content($content) + ->build(); + + $templateless = new Templateless($api_key); + $templateless->send($email); +} catch (\Exception $e) { + echo $e; +} diff --git a/examples/simple.php b/examples/simple.php index 0b8477e..27efa7a 100644 --- a/examples/simple.php +++ b/examples/simple.php @@ -20,20 +20,18 @@ exit; } - $content = Content::builder() - ->text("Hello world") - ->build(); - $email = Email::builder() ->to(new EmailAddress($email_address)) ->subject("Hello") - ->content($content) + ->content( + Content::builder() + ->text("Hello world") + ->build() + ) ->build(); $templateless = new Templateless($api_key); - $result = $templateless->send($email); - - var_dump($result); + $templateless->send($email); } catch (\Exception $e) { echo $e; } diff --git a/examples/verification.php b/examples/verification.php new file mode 100644 index 0000000..fd69424 --- /dev/null +++ b/examples/verification.php @@ -0,0 +1,64 @@ +image('https://templateless.net/myco.webp', null, 100, null, 'MyCo') + ->build(); + + $footer = Collection::builder() + ->text("If you did not sign up for a MyCo account, please ignore this email.\nThis link will expire in 24 hours.") + ->socials([ + new SocialItem(Service::TWITTER, 'MyCo'), + new SocialItem(Service::GITHUB, 'MyCo'), + ]) + ->link('Unsubscribe', 'https://example.com') + ->build(); + + $verify_email_link = 'https://example.com/verify?token=ABC'; + + $content = Content::builder() + ->theme(Theme::SIMPLE) + ->header($header) + ->text("Hi there,") + ->text("Welcome to **MyCo**! We're excited to have you on board. Before we get started, we need to verify your email address.") + ->text("Please confirm your email by clicking the button below:") + ->button("Verify Email", $verify_email_link) + ->text("Or use the link below:") + ->link($verify_email_link, $verify_email_link) + ->footer($footer) + ->build(); + + $email = Email::builder() + ->to(new EmailAddress($email_address)) + ->subject("Confirm your email") + ->content($content) + ->build(); + + $templateless = new Templateless($api_key); + $templateless->send($email); +} catch (\Exception $e) { + echo $e; +} diff --git a/src/Collection.php b/src/Collection.php index 8320150..4da4a22 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -10,7 +10,9 @@ use Templateless\Components\Socials; use Templateless\Components\Text; use Templateless\Components\ViewInBrowser; -use Templateless\Components\SocialItem; +use Templateless\Components\QrCode; +use Templateless\Components\StoreBadges; +use Templateless\Components\Signature; class Collection { @@ -68,6 +70,30 @@ public function view_in_browser($text = '') return $this; } + public function qr_code($url) + { + $this->push(QrCode::url($url)); + return $this; + } + + public function store_badges($data) + { + $this->push(new StoreBadges($data)); + return $this; + } + + public function signature($text, $font = null) + { + $this->push(new Signature($text, $font)); + return $this; + } + + public function component($c) + { + $this->push($c); + return $this; + } + public function build() { return $this; diff --git a/src/Components/ComponentId.php b/src/Components/ComponentId.php index df3ce91..c72f8b6 100644 --- a/src/Components/ComponentId.php +++ b/src/Components/ComponentId.php @@ -12,4 +12,7 @@ enum ComponentId: string case SOCIALS = 'SOCIALS'; case TEXT = 'TEXT'; case VIEW_IN_BROWSER = 'VIEW_IN_BROWSER'; + case STORE_BADGES = 'STORE_BADGES'; + case QR_CODE = 'QR_CODE'; + case SIGNATURE = 'SIGNATURE'; } diff --git a/src/Components/Cryptocurrency.php b/src/Components/Cryptocurrency.php new file mode 100644 index 0000000..d8da196 --- /dev/null +++ b/src/Components/Cryptocurrency.php @@ -0,0 +1,9 @@ +id = ComponentId::QR_CODE; + $this->data = base64_encode($data); + } + + public static function email($email) + { + return new self("mailto:{$email}"); + } + + public static function url($url) + { + return new self($url); + } + + public static function phone($phone) + { + return new self("tel:{$phone}"); + } + + public static function sms($text) + { + return new self("smsto:{$text}"); + } + + public static function coordinates($lat, $lng) + { + return new self("geo:{$lat},{$lng}"); + } + + public static function cryptocurrencyAddress($cryptocurrency, $address) + { + return new self("{$cryptocurrency}:{$address}"); + } + + public function jsonSerialize(): mixed + { + return [ + 'id' => $this->id, + 'data' => $this->data + ]; + } +} diff --git a/src/Components/Service.php b/src/Components/Service.php index b20b12a..0f3818b 100644 --- a/src/Components/Service.php +++ b/src/Components/Service.php @@ -20,4 +20,6 @@ enum Service: string case SNAPCHAT = 'SNAPCHAT'; case THREADS = 'THREADS'; case TELEGRAM = 'TELEGRAM'; + case MASTODON = 'MASTODON'; + case RSS = 'RSS'; } diff --git a/src/Components/Signature.php b/src/Components/Signature.php new file mode 100644 index 0000000..318b7a7 --- /dev/null +++ b/src/Components/Signature.php @@ -0,0 +1,30 @@ +id = ComponentId::SIGNATURE; + $this->text = $text; + $this->font = $font; + } + + public function jsonSerialize(): mixed + { + return [ + 'id' => $this->id, + 'text' => $this->text, + 'font' => $this->font + ]; + } +} diff --git a/src/Components/SignatureFont.php b/src/Components/SignatureFont.php new file mode 100644 index 0000000..7865b5b --- /dev/null +++ b/src/Components/SignatureFont.php @@ -0,0 +1,12 @@ + $this->service, + 'key' => $this->service, 'value' => $this->value ]; } diff --git a/src/Components/StoreBadge.php b/src/Components/StoreBadge.php new file mode 100644 index 0000000..da7e2db --- /dev/null +++ b/src/Components/StoreBadge.php @@ -0,0 +1,10 @@ +key = $key; + $this->value = $value; + } + + public function jsonSerialize(): mixed + { + return [ + 'key' => $this->key, + 'value' => $this->value + ]; + } +} diff --git a/src/Components/StoreBadges.php b/src/Components/StoreBadges.php new file mode 100644 index 0000000..fa20900 --- /dev/null +++ b/src/Components/StoreBadges.php @@ -0,0 +1,27 @@ +id = ComponentId::STORE_BADGES; + $this->data = $data; + } + + public function jsonSerialize(): mixed + { + return [ + 'id' => $this->id, + 'data' => $this->data + ]; + } +} diff --git a/src/Content.php b/src/Content.php index d13917b..9bd9d1a 100644 --- a/src/Content.php +++ b/src/Content.php @@ -10,6 +10,9 @@ use Templateless\Components\Socials; use Templateless\Components\Text; use Templateless\Components\ViewInBrowser; +use Templateless\Components\QrCode; +use Templateless\Components\StoreBadges; +use Templateless\Components\Signature; class Content { @@ -93,6 +96,30 @@ public function view_in_browser($text = '') return $this; } + public function qr_code($url) + { + $this->push(QrCode::url($url)); + return $this; + } + + public function store_badges($data) + { + $this->push(new StoreBadges($data)); + return $this; + } + + public function signature($text, $font = null) + { + $this->push(new Signature($text, $font)); + return $this; + } + + public function component($c) + { + $this->push($c); + return $this; + } + public function build() { return $this;