From 107ab1438055a205aba18034ccf2c4ab29556ea9 Mon Sep 17 00:00:00 2001 From: puklipo Date: Mon, 25 Nov 2024 10:53:55 +0900 Subject: [PATCH] TextBuilder: Make the second parameter optional --- README.md | 6 +- docs/basic-client.md | 14 ++--- docs/notification.md | 8 +-- src/RichText/TextBuilder.php | 68 ++++++++++++++++++---- tests/Feature/RichText/TextBuilderTest.php | 40 +++++++++++++ 5 files changed, 111 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index fb7d6e67..cdb8ad50 100644 --- a/README.md +++ b/README.md @@ -101,11 +101,11 @@ use Revolution\Bluesky\RichText\TextBuilder; Route::get('text-builder', function () { $post = Post::build(function (TextBuilder $builder) { - $builder->text(text: 'Hello Bluesky') + $builder->text('Hello Bluesky') ->newLine(count: 2) - ->link(text: 'https://bsky.app/', uri: 'https://bsky.app/') + ->link('https://bsky.app/') ->newLine() - ->tag(text: '#Bluesky', tag: 'Bluesky'); + ->tag('#Bluesky'); }); $response = Bluesky::login(identifier: config('bluesky.identifier'), password: config('bluesky.password')) diff --git a/docs/basic-client.md b/docs/basic-client.md index 5cfa89be..0af70870 100644 --- a/docs/basic-client.md +++ b/docs/basic-client.md @@ -136,9 +136,9 @@ use Revolution\Bluesky\RichText\TextBuilder; $builder = TextBuilder::make(text: 'test') ->newLine() - ->link(text: 'https://', uri: 'https://') + ->link('https://') ->newLine() - ->tag(text: '#Laravel', tag: 'Laravel'); + ->tag('#Laravel'); $post = Post::create(text: $builder->text, facets: $builder->facets); @@ -157,9 +157,9 @@ use Revolution\Bluesky\RichText\TextBuilder; $post = TextBuilder::make(text: 'test') ->newLine() - ->link(text: 'https://', uri: 'https://') + ->link('https://') ->newLine() - ->tag(text: '#Laravel', tag: 'Laravel') + ->tag('#Laravel') ->toPost(); /** @var \Illuminate\Http\Client\Response $response */ @@ -175,11 +175,11 @@ use Revolution\Bluesky\Record\Post; use Revolution\Bluesky\RichText\TextBuilder; $post = Post::build(function (TextBuilder $builder) { - $builder->text(text: 'test') + $builder->text('test') ->newLine() - ->link(text: 'https://', uri: 'https://') + ->link('https://') ->newLine() - ->tag(text: '#Laravel', tag: 'Laravel') + ->tag('#Laravel') }); ``` diff --git a/docs/notification.md b/docs/notification.md index 6935ba96..956d990a 100644 --- a/docs/notification.md +++ b/docs/notification.md @@ -31,9 +31,9 @@ class TestNotification extends Notification $external = External::create(title: 'Title', description: 'test', uri: 'https://'); return Post::build(function (TextBuilder $builder) { - $builder->text(text: 'test') + $builder->text('test') ->newLine() - ->tag(text: '#Laravel', tag: 'Laravel'); + ->tag('#Laravel'); })->embed($external); } } @@ -68,9 +68,9 @@ class TestNotification extends Notification $quote = QuoteRecord::create(StrongRef::to(uri: 'at://', cid: 'cid')); return BlueskyPrivateMessage::build(function (TextBuilder $builder) { - $builder->text(text: 'test') + $builder->text('test') ->newLine() - ->tag(text: '#Laravel', tag: 'Laravel'); + ->tag('#Laravel'); })->embed($quote); } } diff --git a/src/RichText/TextBuilder.php b/src/RichText/TextBuilder.php index 59445674..51857083 100644 --- a/src/RichText/TextBuilder.php +++ b/src/RichText/TextBuilder.php @@ -10,6 +10,7 @@ use Revolution\AtProto\Lexicon\Attributes\Format; use Revolution\AtProto\Lexicon\Attributes\Ref; use Revolution\AtProto\Lexicon\Enum\Facet; +use Revolution\Bluesky\Facades\Bluesky; use Revolution\Bluesky\Record\Post; final class TextBuilder implements Arrayable @@ -83,18 +84,35 @@ public function newLine(int $count = 1): self /** * Add mention facets. + * + * ``` + * $builder->mention(text: '@***.bsky.social', did: 'did:plc:***'); + * ``` + * If did is not passed, did will be automatically resolved from handle. + * ``` + * $builder->mention('@***.bsky.social'); + * ``` */ - public function mention(string $text, #[Format('did')] string $did): self + public function mention(string $text, #[Format('did')] ?string $did = null): self { - $this->facets[] = [ - 'index' => $this->buildFacetIndex($text), - 'features' => [ - [ - '$type' => Facet::Mention->value, - 'did' => $did, + $text = Str::rtrim($text); + + if (is_null($did) && Str::startsWith($text, '@')) { + $handle = Str::of($text)->after('@')->trim()->toString(); + $did = Bluesky::resolveHandle($handle)->json('did'); + } + + if (filled($did)) { + $this->facets[] = [ + 'index' => $this->buildFacetIndex($text), + 'features' => [ + [ + '$type' => Facet::Mention->value, + 'did' => $did, + ], ], - ], - ]; + ]; + } $this->text .= $text; @@ -103,9 +121,23 @@ public function mention(string $text, #[Format('did')] string $did): self /** * Add link facets. + * + * ``` + * $builder->link(text: 'https://example.com', uri: 'https://example.com'); + * ``` + * If uri is not passed, input text will be used as the uri. + * ``` + * $builder->link('https://example.com'); + * ``` */ - public function link(string $text, string $uri): self + public function link(string $text, ?string $uri = null): self { + $text = Str::rtrim($text); + + if (is_null($uri)) { + $uri = $text; + } + $this->facets[] = [ 'index' => $this->buildFacetIndex($text), 'features' => [ @@ -123,9 +155,23 @@ public function link(string $text, string $uri): self /** * Add tag facets. + * + * ``` + * $builder->tag(text: '#alice', tag: 'alice'); + * ``` + * If tag is not passed, tag will be automatically set from input text. + * ``` + * $builder->tag('#alice'); + * ``` */ - public function tag(string $text, string $tag): self + public function tag(string $text, ?string $tag = null): self { + $text = Str::rtrim($text); + + if (is_null($tag) && Str::startsWith($text, '#')) { + $tag = Str::of($text)->after('#')->trim()->toString(); + } + $this->facets[] = [ 'index' => $this->buildFacetIndex($text), 'features' => [ diff --git a/tests/Feature/RichText/TextBuilderTest.php b/tests/Feature/RichText/TextBuilderTest.php index a862f0f8..4f1e195a 100644 --- a/tests/Feature/RichText/TextBuilderTest.php +++ b/tests/Feature/RichText/TextBuilderTest.php @@ -9,6 +9,13 @@ class TextBuilderTest extends TestCase { + protected function setUp(): void + { + parent::setUp(); + + Http::preventStrayRequests(); + } + public function test_detect_facets_mention() { Http::fakeSequence() @@ -104,4 +111,37 @@ public function test_detect_facets_post_build() $this->assertSame('https://localhost', collect($facets)->dot()->get('1.features.0.uri')); $this->assertSame('alice', collect($facets)->dot()->get('2.features.0.tag')); } + + public function test_resolve_mention() + { + Http::fakeSequence() + ->push(['did' => 'did:plc:alice']); + + $builder = TextBuilder::make('test') + ->mention('@alice.test '); + + $this->assertSame(4, data_get($builder->facets, '0.index.byteStart')); + $this->assertSame(15, data_get($builder->facets, '0.index.byteEnd')); + $this->assertSame('did:plc:alice', collect($builder->facets)->dot()->get('0.features.0.did')); + } + + public function test_resolve_link() + { + $builder = TextBuilder::make('test') + ->link('https://localhost '); + + $this->assertSame(4, data_get($builder->facets, '0.index.byteStart')); + $this->assertSame(21, data_get($builder->facets, '0.index.byteEnd')); + $this->assertSame('https://localhost', collect($builder->facets)->dot()->get('0.features.0.uri')); + } + + public function test_resolve_tag() + { + $builder = TextBuilder::make('test') + ->tag('#alice '); + + $this->assertSame(4, data_get($builder->facets, '0.index.byteStart')); + $this->assertSame(10, data_get($builder->facets, '0.index.byteEnd')); + $this->assertSame('alice', collect($builder->facets)->dot()->get('0.features.0.tag')); + } }