The crop tool now exposes a landscape/portrait toggle next to the device-name label, and the canvas crop frame snaps to the chosen aspect when toggled. Choosing an orientation that does not match the target frame's current orientation surfaces a yellow informational chip — purely informational, no action required, clears as soon as the user toggles back to the matching orientation (or changes the frame in Settings). The chosen orientation rides along on the upload/reprocess request as a new cropOrientation form field and is persisted on the Image entity, so the library view and rotation logic can later surface the same mismatch state for already-uploaded photos. Existing photos without a stored orientation get null and are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,11 @@ class ImageApiController extends AbstractController
|
||||
if ($request->request->has('stickerState')) {
|
||||
$image->setStickerState($request->request->get('stickerState'));
|
||||
}
|
||||
if ($request->request->has('cropOrientation')) {
|
||||
$image->setCropOrientation(
|
||||
Orientation::tryFrom((string) $request->request->get('cropOrientation'))
|
||||
);
|
||||
}
|
||||
|
||||
// Generate thumbnail from composited if available, otherwise from original
|
||||
$thumbSrc = file_exists($storageDir . '/composited.jpg')
|
||||
@@ -208,6 +213,11 @@ class ImageApiController extends AbstractController
|
||||
if ($request->request->has('stickerState')) {
|
||||
$image->setStickerState($request->request->get('stickerState'));
|
||||
}
|
||||
if ($request->request->has('cropOrientation')) {
|
||||
$image->setCropOrientation(
|
||||
Orientation::tryFrom((string) $request->request->get('cropOrientation'))
|
||||
);
|
||||
}
|
||||
|
||||
// Reset all rendered assets so they re-render from the new composited
|
||||
foreach ($image->getRenderedAssets() as $asset) {
|
||||
@@ -321,6 +331,7 @@ class ImageApiController extends AbstractController
|
||||
'approvedDeviceIds' => array_values($image->getApprovedDevices()->map(fn($d) => $d->getId())->toArray()),
|
||||
'cropParams' => $image->getCropParams() ? json_decode($image->getCropParams(), true) : null,
|
||||
'stickerState' => $image->getStickerState() ? json_decode($image->getStickerState(), true) : null,
|
||||
'cropOrientation' => $image->getCropOrientation()?->value,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Enum\Orientation;
|
||||
use App\Repository\ImageRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
@@ -36,6 +37,15 @@ class Image
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $stickerState = null;
|
||||
|
||||
/**
|
||||
* Orientation the user picked in the crop editor. Drives the warning
|
||||
* indicator on the library and the crop page when it doesn't match an
|
||||
* approved device's current orientation. Null on legacy uploads predating
|
||||
* the toggle.
|
||||
*/
|
||||
#[ORM\Column(nullable: true, enumType: Orientation::class)]
|
||||
private ?Orientation $cropOrientation = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private \DateTimeImmutable $uploadedAt;
|
||||
|
||||
@@ -82,6 +92,9 @@ class Image
|
||||
public function getStickerState(): ?string { return $this->stickerState; }
|
||||
public function setStickerState(?string $s): static { $this->stickerState = $s; return $this; }
|
||||
|
||||
public function getCropOrientation(): ?Orientation { return $this->cropOrientation; }
|
||||
public function setCropOrientation(?Orientation $o): static { $this->cropOrientation = $o; return $this; }
|
||||
|
||||
/** @return Collection<int, Device> */
|
||||
public function getApprovedDevices(): Collection { return $this->approvedDevices; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user