Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/Data/BskyImageData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Data;

class BskyImageData
{
public function __construct(
public string $imageFile,
public string $imageTitle,
public ?string $imageDescription = null,
public ?string $imageUri = null
) {
}
}
14 changes: 14 additions & 0 deletions app/Facades/Bluesky.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Facades;

use App\Services\Bluesky as ServicesBluesky;
use Illuminate\Support\Facades\Facade;

class Bluesky extends Facade
{
protected static function getFacadeAccessor()
{
return ServicesBluesky::class;
}
}
44 changes: 42 additions & 2 deletions app/Filament/Resources/MeetupResource/Pages/EditMeetup.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,68 @@
namespace App\Filament\Resources\MeetupResource\Pages;

use App\Actions\GenerateOpenGraphImage;
use App\Data\BskyImageData;
use App\Facades\Bluesky;
use App\Filament\Resources\MeetupResource;
use App\Models\Meetup;
use Filament\Actions;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\TagsInput;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
use Filament\Support\Enums\IconPosition;
use Illuminate\Support\HtmlString;

class EditMeetup extends EditRecord
{
protected static string $resource = MeetupResource::class;

protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
Actions\Action::make('bsky')->label('Announce on Bluesky')
->visible($this->record->group->isBskyConnected())
->form($this->getBskyFormFields())->action(function($form) {
$state = $form->getState();
if ($state['include_image']) {
$img = new BskyImageData(
$this->record->open_graph_image_file,
$this->record->group->name,
"Meetup @ {$this->record->location} on {$this->record->range()}",
$this->record->rsvp_url
);
} else {
$img = null;
}
Bluesky::post($this->record->group, $state['post'], $state['tags'], $img);
Notification::make()->title('Posted to bsky')->success()->send();
}),
Actions\Action::make('view')
->icon('heroicon-o-arrow-top-right-on-square')
->iconPosition(IconPosition::After)
->url(fn(Meetup $record) => $record->rsvp_url)
->openUrlInNewTab(),
];
}


protected function getBskyFormFields()
{
$viewUrl = route('meetup.show-rsvp', $this->record);
$formFields = [
MarkdownEditor::make('post')->default("📆 Meetup @ [{$this->record->location}]($viewUrl)\n\n{$this->record->range()}"),
];
if ($ogImage = $this->record->open_graph_image_url) {
$formFields[] = Checkbox::make('include_image')->default(true)->live();
$formFields[] = Placeholder::make('image')->content(new HtmlString('<img src="'.$ogImage.'">'))->visible(fn($get) => $get('include_image'))->live();
}

$formFields[] = TagsInput::make('tags')->default(['#Meetup', '#PHP', '#Laravel']);
return $formFields;
}

protected function afterSave()
{
GenerateOpenGraphImage::run($this->getRecord());
Expand Down
55 changes: 30 additions & 25 deletions app/Models/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Group extends Model
use HasSnowflakes;
use HasDomain;
use HasGroupMembership;

protected $visible = [
'id',
'domain',
Expand All @@ -45,69 +45,74 @@ class Group extends Model
'longitude',
'created_at',
];

protected $appends = [
'label',
];

protected static function booted()
{
static::saved(function(Group $group) {
Cache::forget('phpx-network');
Cache::forget("group:{$group->domain}");
});
}


public function isBskyConnected(): bool
{
return isset($this->bsky_did, $this->bsky_app_password);
}

public function isActive(): bool
{
return GroupStatus::Active === $this->status;
}

public function isPlanned(): bool
{
return GroupStatus::Planned === $this->status;
}

public function isProspective(): bool
{
return GroupStatus::Prospective === $this->status;
}

public function isDisbanded(): bool
{
return GroupStatus::Disbanded === $this->status;
}

public function mailcoach(): ?Mailcoach
{
if (! isset($this->mailcoach_token, $this->mailcoach_list, $this->mailcoach_endpoint)) {
return null;
}

return new Mailcoach($this->mailcoach_token, $this->mailcoach_endpoint);
}

public function bsky(): Factory|Bluesky|null
{
if (! isset($this->bsky_did, $this->bsky_app_password)) {
if (! $this->isBskyConnected()) {
return null;
}
return Bluesky::login($this->bsky_did, $this->bsky_app_password);

return Bluesky::login(identifier: $this->bsky_did, password: $this->bsky_app_password);
}

public function url(string $path, array $parameters = [], bool $secure = true): string
{
$generator = app(UrlGenerator::class);

try {
$generator->forceRootUrl('https://'.$this->domain);
return $generator->to($path, $parameters, $secure);
} finally {
$generator->forceRootUrl(null);
}
}

public function users(): BelongsToMany
{
return $this->belongsToMany(User::class, 'group_memberships')
Expand All @@ -116,17 +121,17 @@ public function users(): BelongsToMany
->withTimestamps()
->using(GroupMembership::class);
}

public function meetups(): HasMany
{
return $this->hasMany(Meetup::class);
}

public function mailcoach_transactional_emails(): HasMany
{
return $this->hasMany(MailcoachTransactionalEmail::class);
}

protected function casts(): array
{
return [
Expand All @@ -139,33 +144,33 @@ protected function casts(): array
'longitude' => 'float',
];
}

protected function label(): Attribute
{
return Attribute::get(fn() => $this->region ?? str($this->name)->afterLast('×')->trim()->toString());
}

protected function airportCode(): Attribute
{
return Attribute::get(
fn(): Stringable => str($this->name)->afterLast('×')->trim()->upper(),
);
}

protected function openGraphImageUrl(): Attribute
{
return Attribute::get(function() {
$filename = $this->airport_code->lower()->finish('.png');
$path = public_path("og/{$filename}");

if (file_exists($path)) {
return asset("og/{$filename}").'?t='.filemtime($path);
}

return null;
});
}

protected function meetupUrlArray(): Attribute
{
return Attribute::get(fn() => str($this->meetup_url)
Expand Down
14 changes: 7 additions & 7 deletions app/Models/Meetup.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class Meetup extends Model implements Htmlable
Expand Down Expand Up @@ -135,20 +136,19 @@ protected function openGraphImageFile(): Attribute
{
return Attribute::get(function() {
$filename = "og/meetups/{$this->getKey()}.png";
$path = storage_path("app/public/{$filename}");
$path = "public/{$filename}";

return file_exists($path) ? $path : null;
return Storage::exists($path) ? $path : null;
});
}

protected function openGraphImageUrl(): Attribute
{
return Attribute::get(function() {
$filename = "og/meetups/{$this->getKey()}.png";
$path = storage_path("app/public/{$filename}");

if (file_exists($path)) {
return asset("storage/{$filename}").'?t='.filemtime($path);
if ($this->openGraphImageFile) {
$microtime = Storage::lastModified($this->openGraphImageFile);
$filename = "og/meetups/{$this->getKey()}.png";
return asset("storage/{$filename}").'?t='.$microtime;
}

return null;
Expand Down
Loading