Skip to content

Commit

Permalink
add sharing dialog to hub (#2987)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmachughes authored Feb 27, 2025
1 parent b9ece41 commit b3e16ea
Show file tree
Hide file tree
Showing 22 changed files with 222 additions and 99 deletions.
1 change: 1 addition & 0 deletions sourcecode/hub/app/DataObjects/ContentDisplayItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function __construct(
public string|null $useUrl,
public string|null $editUrl,
public string|null $shareUrl,
public string|null $shareDialogUrl,
public string|null $copyUrl,
public string|null $deleteUrl,
) {}
Expand Down
11 changes: 11 additions & 0 deletions sourcecode/hub/app/Http/Controllers/ContentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ public function share(Content $content, Request $request): View
]);
}

public function shareDialog(Content $content, Request $request): View
{
if (!$request->header('HX-Request')) {
abort(400);
}

return view('content.hx-share-dialog', [
'content' => $content,
]);
}

public function embed(Content $content, ContentVersion|null $version = null): View
{
$version ??= $content->latestPublishedVersion()->firstOrFail();
Expand Down
1 change: 1 addition & 0 deletions sourcecode/hub/app/Http/Requests/ContentFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ private function attachModel(array $hits, bool $forUser, bool $showDrafts): Coll
useUrl: $canUse ? route('content.use', [$content, $version]) : null,
editUrl: $canEdit ? route('content.edit', [$content, $version]) : null,
shareUrl: $canView ? route('content.share', [$content, SessionScope::TOKEN_PARAM => null]) : null,
shareDialogUrl: $canView ? route('content.share-dialog', [$content]) : null,
copyUrl: $canCopy ? route('content.copy', [$content]) : null,
deleteUrl: $canDelete ? route('content.delete', [$content]) : null,
);
Expand Down
1 change: 1 addition & 0 deletions sourcecode/hub/resources/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'bootstrap';
import 'htmx.org';
import { findIframeByWindow } from './helpers';
import './clipboard';
import './modal.js';
import './resize';
import './xApi';
import './dateHelper';
Expand Down
8 changes: 4 additions & 4 deletions sourcecode/hub/resources/js/clipboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ document.addEventListener('click', async (event) => {
return;
}

const element = event.target.closest('.share-button');
const element = event.target.closest('.copy-to-clipboard');

if (element) {
event.preventDefault();
event.stopImmediatePropagation();

const shareUrl = element.href;
const value = element.getAttribute('data-share-value');
const successMessage = element.getAttribute('data-share-success-message');
const failureMessage = element.getAttribute('data-share-failure-message');

// TODO: use a bootstrap toast or something nicer instead
try {
await navigator.clipboard.writeText(shareUrl);
await navigator.clipboard.writeText(value);

alert(successMessage);
} catch (e) {
console.error('An error occurred while copying the URL', e);
console.error('An error occurred while copying to clipboard', e);

alert(failureMessage)
}
Expand Down
29 changes: 29 additions & 0 deletions sourcecode/hub/resources/js/modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Launch a Bootstrap modal after the content is loaded via htmx and the request
* has been settled. This solves issues with multiple modals and needing a
* skeleton modal.
*
* For this to work, links should look like this:
*
* <a hx-get="..."
* hx-target="#modal-container"
* hx-swap="beforeend"
* data-modal="true">...</a>
*/

import { Modal } from 'bootstrap';

document.body.addEventListener('htmx:beforeRequest', (event) => {
if (event.detail.elt.hasAttribute('data-modal')) {
event.detail.requestConfig.modalRequest = true;
}
});

document.body.addEventListener('htmx:afterSettle', (event) => {
if (event.detail.requestConfig.modalRequest) {
new Modal(event.detail.target.querySelector('.modal:last-child')).show({
focus: true,
keyboard: true,
});
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@endif
<x-content.action-buttons.menu
:shareUrl="$content->shareUrl"
:shareDialogUrl="$content->shareDialogUrl"
:detailsUrl="$content->detailsUrl"
:copyUrl="$content->editUrl ? $content->copyUrl : null"
:deleteUrl="$content->deleteUrl"
Expand All @@ -22,10 +23,11 @@
@endif
<x-content.action-buttons.menu
:shareUrl="$content->shareUrl"
:shareDialogUrl="$content->shareDialogUrl"
:copyUrl="$content->editUrl ? $content->copyUrl : null"
:deleteUrl="$content->deleteUrl"
/>
@else
<x-content.action-buttons.share :url="$content->shareUrl" />
<x-content.action-buttons.share :$content />
<x-content.action-buttons.details :url="$content->detailsUrl" />
@endif
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ class="btn btn-sm btn-secondary border-1 dropdown-toggle action-menu-toggle"
<a
href="{{ $shareUrl }}"
class="dropdown-item share-button"
data-share-success-message="{{ trans('messages.share-copied-url-success') }}"
data-share-failure-message="{{ trans('messages.share-copied-url-failed') }}"
role="button"
target="_blank"
hx-get="{{ $shareDialogUrl }}"
hx-target="#modal-container"
hx-swap="beforeend"
data-modal="true"
>
<x-icon name="share" class="me-2" />
{{ trans('messages.share') }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<a
href="{{ $url }}"
class="btn btn-secondary btn-sm me-1 content-share-button"
data-share-success-message="{{ trans('messages.share-copied-url-success') }}"
data-share-failure-message="{{ trans('messages.share-copied-url-failed') }}"
role="button"
href="{{ $content->shareUrl }}"
class="btn btn-secondary btn-sm me-1"
target="_blank"
hx-get="{{ $content->shareDialogUrl }}"
hx-target="#modal-container"
hx-swap="beforeend"
data-modal="true"
>
{{ trans('messages.share') }}
</a>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<x-form action="{{ $url }}" method="POST">
<button class="btn btn-primary btn-sm me-1 content-use-button">
<button class="btn btn-primary btn-sm me-1 use-button">
{{ trans('messages.use-content') }}
</button>
</x-form>
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
href="{{ $content->detailsUrl }}"
class="text-decoration-none link-body-emphasis"
hx-get="{{ $content->previewUrl }}"
hx-target="#previewModal"
data-bs-toggle="modal"
data-bs-target="#previewModal"
hx-target="#modal-container"
hx-swap="beforeend"
data-modal="true"
>
<div class="content-card-header-updated text-truncate d-none d-md-block fw-normal">
{{ trans('messages.edited') }}:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ class="btn btn-secondary d-flex gap-2 text-nowrap"
class="btn btn-secondary d-flex gap-2 text-nowrap share-button"
href="{{ route('content.share', [$content, SessionScope::TOKEN_PARAM => null]) }}"
target="_blank"
data-share-success-message="{{ trans('messages.share-copied-url-success') }}"
data-share-failure-message="{{ trans('messages.share-copied-url-failed') }}"
hx-get="{{ route('content.share-dialog', [$content]) }}"
hx-target="#modal-container"
hx-swap="beforeend"
data-modal="true"
>
<x-icon name="share" />
<span class="flex-grow-1">{{ trans('messages.share') }}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
href="{{ $content->detailsUrl }}"
class="col text-decoration-none link-body-emphasis"
hx-get="{{ $content->previewUrl }}"
hx-target="#previewModal"
data-bs-toggle="modal"
data-bs-target="#previewModal"
hx-target="#modal-container"
hx-swap="beforeend"
data-modal="true"
>
<h5 class="text-line-clamp clamp-3-lines fw-bold" aria-label="{{ trans('messages.title') }}">
{{ $content->title }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<a {{ $attributes->merge([
'href' => $detailsUrl ?: route('content.version-details', [$content, $version]),
'hx-get' => $previewUrl ?: route('content.preview', [$content, $version]),
'hx-target' => '#previewModal',
'data-bs-toggle' => 'modal',
'data-bs-target' => '#previewModal',
'hx-target' => '#modal-container',
'hx-swap' => 'beforeend',
'data-modal' => 'true',
]) }}>{{ $slot }}</a>
13 changes: 5 additions & 8 deletions sourcecode/hub/resources/views/components/layout.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,12 @@ class="edlib-logo edlib-logo-footer"
{{-- Replacement confirmation modal for htmx:confirm event --}}
<x-htmx-confirm-modal />

{{-- Skeleton for preview modal --}}
<div class="modal" id="previewModal" tabindex="-1" aria-labelledby="previewModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-fullscreen-lg-down modal-lg">
</div>
</div>
<div id="modal-container"></div>

<script nonce="{{ \Illuminate\Support\Facades\Vite::cspNonce() }}">
const previewModal = document.querySelector('#previewModal');
previewModal.addEventListener('hidden.bs.modal', () => {
previewModal.firstChild.remove();
const modalContainer = document.querySelector('#modal-container');
modalContainer.addEventListener('hidden.bs.modal', () => {
modalContainer.firstChild.remove();
});
</script>
</body>
Expand Down
49 changes: 49 additions & 0 deletions sourcecode/hub/resources/views/components/modal.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<div {{ $attributes->except([
'sm',
'md',
'lg',
'xl',
'xxl',
'fullscreen-sm-down',
'fullscreen-md-down',
'fullscreen-lg-down',
'fullscreen-xl-down',
'fullscreen-xxl-down',
])->class('modal') }}>
<div @class([
'modal-dialog',
'modal-sm' => $sm ?? false,
'modal-md' => $md ?? false,
'modal-lg' => $lg ?? false,
'modal-xl' => $xl ?? false,
'modal-xxl' => $xxl ?? false,
'modal-fullscreen-sm-down' => $fullscreenSm_down ?? false,
'modal-fullscreen-md-down' => $fullscreenMdDown ?? false,
'modal-fullscreen-lg-down' => $fullscreenLgDown ?? false,
'modal-fullscreen-xl-down' => $fullscreenXlDown ?? false,
'modal-fullscreen-xxl-down' => $fullscreenXxlDown ?? false,
])>
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ $title }}</h4>

<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="{{ trans('messages.close') }}"
></button>
</div>

<div class="modal-body">
{{ $slot }}
</div>

@isset($footer)
<div class="modal-footer">
{{ $footer }}
</div>
@endif
</div>
</div>
</div>
98 changes: 50 additions & 48 deletions sourcecode/hub/resources/views/content/hx-preview.blade.php
Original file line number Diff line number Diff line change
@@ -1,57 +1,59 @@
<div class="modal-dialog modal-dialog-centered modal-fullscreen-lg-down modal-lg">
<div class="modal-content">
<div class="modal-header border-0">
<h4 class="modal-title">{{ $version->getTitle() }}</h4>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="{{trans('messages.close')}}"
></button>
</div>
<div class="modal preview-modal">
<div class="modal-dialog modal-dialog-centered modal-fullscreen-lg-down modal-lg">
<div class="modal-content">
<div class="modal-header border-0">
<h4 class="modal-title">{{ $version->getTitle() }}</h4>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="{{trans('messages.close')}}"
></button>
</div>

<div class="modal-body">
<x-lti-launch :launch="$launch" class="w-100 border" />
</div>
<div class="modal-body">
<x-lti-launch :launch="$launch" class="w-100 border" />
</div>

<div class="modal-footer border-0">
<div class="flex-fill">
<div>
<strong>{{ trans('messages.edited') }}:</strong>
<time
datetime="{{ $version->created_at->toIso8601String() }}"
data-dh-relative="true"
></time>
<div class="modal-footer border-0">
<div class="flex-fill">
<div>
<strong>{{ trans('messages.edited') }}:</strong>
<time
datetime="{{ $version->created_at->toIso8601String() }}"
data-dh-relative="true"
></time>
</div>
</div>
</div>

@if ($version->published)
<a
href="{{ route('content.share', [$content]) }}"
class="btn btn-secondary d-flex gap-2 share-button"
role="button"
data-share-success-message="{{ trans('messages.share-copied-url-success') }}"
data-share-failure-message="{{ trans('messages.share-copied-url-failed') }}"
target="_blank"
>
<x-icon name="share" />
{{ trans('messages.share') }}
</a>
@endif
@if ($version->published)
<a
href="{{ route('content.share', [$content]) }}"
class="btn btn-secondary d-flex gap-2"
hx-get="{{ route('content.share-dialog', [$content]) }}"
hx-target="#modal-container"
hx-swap="beforeend"
data-modal="true"
>
<x-icon name="share" />
{{ trans('messages.share') }}
</a>
@endif

@can('edit', [$content])
<a href="{{ route('content.edit', [$content, $version]) }}" class="btn btn-secondary" role="button">
{{ trans('messages.edit-content') }}
</a>
@endcan
@can('edit', [$content])
<a href="{{ route('content.edit', [$content, $version]) }}" class="btn btn-secondary" role="button">
{{ trans('messages.edit-content') }}
</a>
@endcan

@can('use', [$content, $version])
<x-form action="{{ $version->getUseUrl() }}" method="POST">
<button class="btn btn-primary use-button" role="button">
{{ trans('messages.use-content') }}
</button>
</x-form>
@endcan
@can('use', [$content, $version])
<x-form action="{{ $version->getUseUrl() }}" method="POST">
<button class="btn btn-primary use-button" role="button">
{{ trans('messages.use-content') }}
</button>
</x-form>
@endcan
</div>
</div>
</div>
</div>
Loading

0 comments on commit b3e16ea

Please sign in to comment.