Skip to content

Commit

Permalink
fix copy move selection
Browse files Browse the repository at this point in the history
  • Loading branch information
ildyria committed Nov 28, 2024
1 parent f77198f commit f65e8f0
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 34 deletions.
17 changes: 11 additions & 6 deletions app/Actions/Album/ListAlbums.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,26 @@ class ListAlbums extends Action
private const SHORTEN_BY = 80;

/**
* @param int|null $lft
* @param int|null $rgt
* @param string|null $parent_id
* @param Collection<int,Album> $albumsFiltering
* @param string|null $parent_id
*
* @return TAlbumSaved[]
*/
public function do(?int $lft, ?int $rgt, ?string $parent_id): array
public function do(Collection $albumsFiltering, ?string $parent_id): array
{
$albumQueryPolicy = resolve(AlbumQueryPolicy::class);
$unfiltered = $albumQueryPolicy->applyReachabilityFilter(
// We remove all sub albums
// Otherwise it would create cyclic dependency
Album::query()
->when($lft !== null,
fn ($q) => $q->where('_lft', '<', $lft)->orWhere('_rgt', '>', $rgt))
->when($albumsFiltering->count() > 0,
function ($q) use ($albumsFiltering) {
$albumsFiltering->each(
fn ($a) => $q->whereNot(fn ($q1) => $q1->where('_lft', '>=', $a->_lft)->where('_rgt', '<=', $a->_rgt))
);

return $q;
})
);
$sorting = AlbumSortingCriterion::createDefault();
$query = (new SortingDecorator($unfiltered))
Expand Down
10 changes: 3 additions & 7 deletions app/Http/Controllers/Gallery/AlbumController.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,10 @@ public function delete(DeleteAlbumsRequest $request, Delete $delete): void
*/
public function getTargetListAlbums(TargetListAlbumRequest $request, ListAlbums $listAlbums)
{
if ($request->album() instanceof Album) {
/** @var Album $album */
$album = $request->album();
$albums = $request->albums();
$parent_id = $albums->count() > 0 ? $albums->first()->parent_id : null;

return TargetAlbumResource::collect($listAlbums->do($album->_lft, $album->_rgt, $album->parent_id));
}

return TargetAlbumResource::collect($listAlbums->do(null, null, null));
return TargetAlbumResource::collect($listAlbums->do($albums, $parent_id));
}

/**
Expand Down
21 changes: 14 additions & 7 deletions app/Http/Requests/Album/TargetListAlbumRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@

namespace App\Http\Requests\Album;

use App\Contracts\Http\Requests\HasAbstractAlbum;
use App\Contracts\Http\Requests\HasAlbums;
use App\Contracts\Http\Requests\RequestAttribute;
use App\Contracts\Models\AbstractAlbum;
use App\Exceptions\PasswordRequiredException;
use App\Http\Requests\BaseApiRequest;
use App\Http\Requests\Traits\HasAbstractAlbumTrait;
use App\Http\Requests\Traits\HasAlbumsTrait;
use App\Models\Album;
use App\Models\Extensions\BaseAlbum;
use App\Policies\AlbumPolicy;
use App\Rules\AlbumIDRule;
use Illuminate\Support\Facades\Gate;

class TargetListAlbumRequest extends BaseApiRequest implements HasAbstractAlbum
/**
* @implements HasAlbums<Album>
*/
class TargetListAlbumRequest extends BaseApiRequest implements HasAlbums
{
use HasAbstractAlbumTrait;
/** @phpstan-use HasAlbumsTrait<Album> */
use HasAlbumsTrait;

/**
* {@inheritDoc}
Expand Down Expand Up @@ -45,7 +50,8 @@ public function authorize(): bool
public function rules(): array
{
return [
RequestAttribute::ALBUM_ID_ATTRIBUTE => ['sometimes', new AlbumIDRule(false)],
RequestAttribute::ALBUM_IDS_ATTRIBUTE => 'sometimes|array|min:1',
RequestAttribute::ALBUM_IDS_ATTRIBUTE . '.*' => ['required', new AlbumIDRule(false)],
];
}

Expand All @@ -54,7 +60,8 @@ public function rules(): array
*/
protected function processValidatedValues(array $values, array $files): void
{
$album_id = $values[RequestAttribute::ALBUM_ID_ATTRIBUTE] ?? null;
$this->album = $this->albumFactory->findNullalbleAbstractAlbumOrFail($album_id);
$album_ids = $values[RequestAttribute::ALBUM_IDS_ATTRIBUTE] ?? [];
/** @phpstan-ignore-next-line */
$this->albums = $this->albumFactory->findAbstractAlbumsOrFail($album_ids);
}
}
2 changes: 1 addition & 1 deletion resources/js/components/forms/album/AlbumMove.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</div>
<div v-else-if="error_no_target === false">
<span class="font-bold">{{ "Move to" }}</span>
<SearchTargetAlbum :album-id="props.album.id" @selected="selected" @no-target="error_no_target = true" />
<SearchTargetAlbum :album-ids="[props.album.id]" @selected="selected" @no-target="error_no_target = true" />
</div>
<div v-else>
<p class="text-center text-muted-color">{{ "No album to move to" }}</p>
Expand Down
10 changes: 5 additions & 5 deletions resources/js/components/forms/album/SearchTargetAlbum.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import AlbumService from "@/services/album-service";
import Select from "primevue/select";
const props = defineProps<{
albumId: string | undefined;
albumIds: string[] | undefined;
}>();
const albumId = ref<string | null>(props.albumId ?? null);
const albumIds = ref<string[] | null>(props.albumIds ?? null);
const emits = defineEmits<{
selected: [target: App.Http.Resources.Models.TargetAlbumResource];
"no-target": [];
Expand All @@ -43,7 +43,7 @@ const options = ref<App.Http.Resources.Models.TargetAlbumResource[] | undefined>
const selectedTarget = ref<App.Http.Resources.Models.TargetAlbumResource | undefined>(undefined);
function load() {
AlbumService.getTargetListAlbums(albumId.value).then((response) => {
AlbumService.getTargetListAlbums(albumIds.value).then((response) => {
options.value = response.data;
if (options.value.length === 0) {
emits("no-target");
Expand All @@ -62,9 +62,9 @@ function selected() {
}
watch(
() => props.albumId,
() => props.albumIds,
(newAlbumId, _oldAlbumId) => {
albumId.value = newAlbumId ?? null;
albumIds.value = newAlbumId ?? null;
load();
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<span v-else class="font-bold">
{{ sprintf("Merge %d albums to:", props.albumIds?.length) }}
</span>
<SearchTargetAlbum :album-id="parentId" @selected="selected" @no-target="error_no_target = true" />
<SearchTargetAlbum :album-ids="albumIds" @selected="selected" @no-target="error_no_target = true" />
</div>
<div v-else class="p-9">
<p class="text-center text-muted-color">{{ "No album to merge to." }}</p>
Expand Down Expand Up @@ -58,6 +58,7 @@ const toast = useToast();
const titleMovedTo = ref<string | undefined>(undefined);
const destination_id = ref<string | undefined | null>(undefined);
const error_no_target = ref(false);

const confirmation = computed(() => {
if (props.album) {
return sprintf(trans("lychee.ALBUM_MERGE"), props.album.title, titleMovedTo.value);
Expand Down
10 changes: 9 additions & 1 deletion resources/js/components/forms/gallery-dialogs/MoveDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<span class="font-bold">
{{ question }}
</span>
<SearchTargetAlbum :album-id="parentId" @selected="selected" @no-target="error_no_target = true" />
<SearchTargetAlbum :album-ids="headIds" @selected="selected" @no-target="error_no_target = true" />
</div>
<div v-else class="p-9">
<p class="text-center text-muted-color">{{ "No album to move to." }}</p>
Expand Down Expand Up @@ -70,6 +70,14 @@ function close() {
visible.value = false;
}
const headIds = computed(() => {
if (props.photo || (props.photoIds && props.photoIds?.length > 0)) {
return undefined;
}
return props.albumIds;
});
const question = computed(() => {
if (props.photoIds && props.photoIds?.length > 1) {
return sprintf("Move %d photos to:", props.photoIds?.length);
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/forms/photo/PhotoCopyDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<span class="font-bold">
{{ question }}
</span>
<SearchTargetAlbum :album-id="undefined" @selected="selected" @no-target="error_no_target = true" />
<SearchTargetAlbum :album-ids="undefined" @selected="selected" @no-target="error_no_target = true" />
</div>
<div v-else class="p-9">
<p class="text-center text-muted-color">{{ "No album to copy to." }}</p>
Expand Down
4 changes: 2 additions & 2 deletions resources/js/services/album-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ const AlbumService = {
return axios.delete(`${Constants.getApiUrl()}Album`, { data: { album_ids: album_ids } });
},

getTargetListAlbums(album_id: string | null): Promise<AxiosResponse<App.Http.Resources.Models.TargetAlbumResource[]>> {
return axios.get(`${Constants.getApiUrl()}Album::getTargetListAlbums`, { params: { album_id: album_id }, data: {} });
getTargetListAlbums(album_ids: string[] | null): Promise<AxiosResponse<App.Http.Resources.Models.TargetAlbumResource[]>> {
return axios.get(`${Constants.getApiUrl()}Album::getTargetListAlbums`, { params: { album_ids: album_ids }, data: {} });
},

move(dest: string | null, album_ids: string[]): Promise<AxiosResponse> {
Expand Down
6 changes: 3 additions & 3 deletions tests/Feature_v2/Album/AlbumListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ class AlbumListTest extends BaseApiV2Test
{
public function testListTargetAlbumUnauthorizedForbidden(): void
{
$response = $this->getJson('Album::getTargetListAlbums?album_id=' . $this->album1->id);
$response = $this->getJson('Album::getTargetListAlbums?album_ids=' . $this->album1->id);
$this->assertUnauthorized($response);

$response = $this->actingAs($this->userLocked)->getJson('Album::getTargetListAlbums?album_id=' . $this->album1->id);
$response = $this->actingAs($this->userLocked)->getJson('Album::getTargetListAlbums?album_ids=' . $this->album1->id);
$this->assertForbidden($response);
}

public function testListTargetAlbumAuthorizedOwner(): void
{
$response = $this->actingAs($this->admin)->getJson('Album::getTargetListAlbums?album_id=' . $this->album1->id);
$response = $this->actingAs($this->admin)->getJson('Album::getTargetListAlbums?album_ids=' . $this->album1->id);
$this->assertOk($response);
$response->assertDontSee($this->subAlbum1->id);
}
Expand Down

0 comments on commit f65e8f0

Please sign in to comment.