Skip to content

Commit

Permalink
feat: improved bookmark management
Browse files Browse the repository at this point in the history
  • Loading branch information
modelrailroader committed Jul 3, 2024
1 parent 436562d commit 42763da
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 64 deletions.
62 changes: 30 additions & 32 deletions phpmyfaq/assets/src/api/bookmarks.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,36 @@
* @since 2023-09-19
*/

export const handleBookmarks = () => {
const bookmarkTrashIcons = document.querySelectorAll('.pmf-delete-bookmark');

if (bookmarkTrashIcons) {
bookmarkTrashIcons.forEach((element) => {
element.addEventListener('click', async (event) => {
event.preventDefault();
const bookmarkId = event.target.getAttribute('data-pmf-bookmark-id');

try {
const response = await fetch(`api/bookmark/${bookmarkId}`, {
method: 'DELETE',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
});

if (!response.ok) {
throw new Error('Network response was not ok');
}
export const addBookmark = async (faqId) => {
try {
const response = await fetch(`api/bookmark/add/${faqId}`, {
method: 'POST',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
});
return await response.json();
} catch (error) {
console.error('Error adding bookmark:', error);
}
}

const responseData = await response.json();
const bookmarkToDelete = document.getElementById(`delete-bookmark-${bookmarkId}`);
bookmarkToDelete.remove();
} catch (error) {
// Handle error here
console.error('Error deleting bookmark:', error);
}
});
export const removeBookmark = async (faqId) => {
try {
const response = await fetch(`api/bookmark/remove/${faqId}`, {
method: 'DELETE',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
});
return await response.json();
} catch (error) {
console.error('Error removing bookmark:', error);
}
};
}
36 changes: 35 additions & 1 deletion phpmyfaq/assets/src/faq/faq.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { addElement } from '../utils';
import { createFaq } from '../api';
import { addBookmark, createFaq, handleBookmarks, removeBookmark } from '../api';
import { pushErrorNotification, pushNotification } from '../../../admin/assets/src/utils';

export const handleAddFaq = () => {
const addFaqSubmit = document.getElementById('pmf-submit-faq');
Expand Down Expand Up @@ -40,3 +41,36 @@ export const handleAddFaq = () => {
});
}
};

export const handleShowFaq = async () => {
const bookmarkToggle = document.getElementById('pmf-bookmark-toggle');
if (bookmarkToggle) {
bookmarkToggle.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
if (bookmarkToggle.getAttribute('data-pmf-action') === 'remove') {
const response = await removeBookmark(bookmarkToggle.getAttribute('data-pmf-id'));
if (response.success) {
pushNotification(response.success);
document.getElementById('pmf-bookmark-icon').classList.remove('bi-bookmark-fill');
document.getElementById('pmf-bookmark-icon').classList.add('bi-bookmark');
bookmarkToggle.innerText = response.linkText;
bookmarkToggle.setAttribute('data-pmf-action', 'add');
} else {
pushErrorNotification(response.error);
}
} else {
const response = await addBookmark(bookmarkToggle.getAttribute('data-pmf-id'));
if (response.success) {
pushNotification(response.success);
document.getElementById('pmf-bookmark-icon').classList.remove('bi-bookmark');
document.getElementById('pmf-bookmark-icon').classList.add('bi-bookmark-fill');
bookmarkToggle.innerText = response.linkText;
bookmarkToggle.setAttribute('data-pmf-action', 'remove');
} else {
pushErrorNotification(response.error);
}
}
});
}
};
17 changes: 14 additions & 3 deletions phpmyfaq/assets/src/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@
import 'bootstrap';
import Masonry from 'masonry-layout';

import { handleBookmarks } from './api';
import { handleContactForm } from './contact';
import { handleAddFaq, handleComments, handleSaveComment, handleShare, handleUserVoting } from './faq';
import { handleAddFaq, handleComments, handleSaveComment, handleShare, handleShowFaq, handleUserVoting } from './faq';
import { handleAutoComplete, handleQuestion } from './search';
import { handleRegister, handleRequestRemoval, handleUserControlPanel, handleUserPassword } from './user';
import {
handleBookmarks,
handleRegister,
handleRequestRemoval,
handleUserControlPanel,
handleUserPassword,
} from './user';
import { calculateReadingTime, handlePasswordStrength, handlePasswordToggle, handleReloadCaptcha } from './utils';
import './utils/tooltip';
import { initializeTooltips } from '../../admin/assets/src/utils';

//
// Reload Captchas
Expand Down Expand Up @@ -67,6 +73,11 @@ handleShare();
//
handleAddFaq();

//
// Handle show FAQ
//
handleShowFaq();

//
// Handle Add a Question
//
Expand Down
38 changes: 38 additions & 0 deletions phpmyfaq/assets/src/user/bookmarks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Handle bookmarks page
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*
* @package phpMyFAQ
* @author Thorsten Rinne <thorsten@phpmyfaq.de>
* @author Jan Harms <modelrailroader@gmx-topmail.de>
* @copyright 2023-2024 phpMyFAQ Team
* @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
* @link https://www.phpmyfaq.de
* @since 2024-07-03
*/

import { removeBookmark } from '../api';
import { pushErrorNotification, pushNotification } from '../../../admin/assets/src/utils';

export const handleBookmarks = () => {
const bookmarkTrashIcons = document.querySelectorAll('.pmf-delete-bookmark');
if (bookmarkTrashIcons) {
bookmarkTrashIcons.forEach((element) => {
element.addEventListener('click', async (event) => {
event.preventDefault();
const bookmarkId = event.target.getAttribute('data-pmf-bookmark-id');
const bookmarkToDelete = document.getElementById(`delete-bookmark-${bookmarkId}`);
bookmarkToDelete.remove();
const response = await removeBookmark(bookmarkId);
if (response.success) {
pushNotification(response.success);
} else {
pushErrorNotification(response.error);
}
});
});
}
};
1 change: 1 addition & 0 deletions phpmyfaq/assets/src/user/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './password';
export * from './register';
export * from './request-removal';
export * from './ucp';
export * from './bookmarks';
52 changes: 48 additions & 4 deletions phpmyfaq/assets/templates/bookmarks.twig
Original file line number Diff line number Diff line change
@@ -1,15 +1,59 @@
<!-- @todo Toasts -> have to be moved to header and footer.twig --->
<div class="toast-container position-fixed top-0 start-50 translate-middle-x mt-5 p-3">
<div id="pmf-notification" class="toast align-items-center text-bg-primary shadow border-0" role="alert"
aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body" id="pmf-notification-message">
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"
aria-label="Close">
</button>
</div>
</div>
<div id="pmf-notification-error" class="toast align-items-center text-bg-danger shadow border-0"
role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body" id="pmf-notification-error-message">
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"
aria-label="Close">
</button>
</div>
</div>
</div>
<div class="row g-5">
<div class="col-md-12">
<h2 class="pb-4 mb-4 border-bottom">{{ msgMyBookmarks }}</h2>
<div class="list-group mb-4">
<div class="accordion" id="bookmarkAccordion">
{% set count = 1 %}
{% for bookmark in bookmarksList %}
<a href="{{ bookmark['url'] }}" class="list-group-item list-group-item-action" id="delete-bookmark-{{ bookmark['id'] }}">
<!--<a href="{{ bookmark['url'] }}" class="list-group-item list-group-item-action" id="delete-bookmark-{{ bookmark['id'] }}">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ bookmark['title'] }}</h5>
<i class="bi bi-trash-fill text-danger m-1 pmf-delete-bookmark" data-pmf-bookmark-id="{{ bookmark['id'] }}"></i>
<i class="bi bi-trash text-danger m-1 pmf-delete-bookmark" title="{{ removeBookmark }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-pmf-bookmark-id="{{ bookmark['id'] }}"></i>
</div>
</a>-->
<div class="accordion-item shadow-sm rounded mb-3 border-1" id="delete-bookmark-{{ bookmark['id'] }}">
<h2 class="accordion-header" id="heading{{ count }}">
<button class="accordion-button d-flex collapsed justify-content-between" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ count }}" aria-expanded="true" aria-controls="collapse{{ count }}">
<span class="flex-grow-1 text-start">{{ count }}. {{ bookmark['title'] }}</span>
<i class="bi bi-trash text-danger m-1 pmf-delete-bookmark" title="{{ removeBookmark }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-pmf-bookmark-id="{{ bookmark['id'] }}">
</i>&nbsp;
</button>
</h2>
<div id="collapse{{ count }}" class="accordion-collapse collapse" aria-labelledby="heading{{ count }}" data-bs-parent="#bookmarkAccordion">
<div class="accordion-body">
{{ bookmark['answer']|raw }}
<a href="{{ bookmark['url'] }}">{{ msgLinkToFAQ }}</a>
</div>
</div>
</a>
</div>
{% set count = count + 1 %}
{% endfor %}
</div>

Expand Down
32 changes: 29 additions & 3 deletions phpmyfaq/assets/templates/faq.twig
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
<!-- @todo Toasts -> have to be moved to header and footer.twig --->
<div class="toast-container position-fixed top-0 start-50 translate-middle-x mt-5 p-3">
<div id="pmf-notification" class="toast align-items-center text-bg-primary shadow border-0" role="alert"
aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body" id="pmf-notification-message">
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"
aria-label="Close">
</button>
</div>
</div>
<div id="pmf-notification-error" class="toast align-items-center text-bg-danger shadow border-0"
role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body" id="pmf-notification-error-message">
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"
aria-label="Close">
</button>
</div>
</div>
</div>
<div class="row g-5">
<div class="col-md-8">
<h2 class="pb-4 mb-4 border-bottom">{{ question }}</h2>
Expand Down Expand Up @@ -38,15 +63,16 @@
{% endif %}
{% if userId != -1 %}
<li class="list-group-item bg-transparent">
<i aria-hidden="true" class="{{ bookmarkIcon }}"></i>
<a target="_self" id="bookmark-link" href="{{ bookmarkLink }}" rel="noopener" title="{{ msgAddBookmark }}" class="text-decoration-none">
<i aria-hidden="true" id="pmf-bookmark-icon" class="{{ bookmarkIcon }}"></i>
<a id="pmf-bookmark-toggle" href="#" rel="noopener" class="text-decoration-none"
data-pmf-action="{% if isFaqBookmark == true %}remove{% else %}add{% endif %}" data-pmf-id="{{ id }}">
{{ msgAddBookmark }}
</a>
</li>
{% endif %}
<li class="list-group-item bg-transparent">
<i aria-hidden="true" class="bi bi-file-pdf"></i>
<a target="_blank" href="{{ linkToPdf }}" rel="noopener" title="{{ msgPdf }}" class="text-decoration-none">
<a target="_blank" href="{{ linkToPdf }}" rel="noopener" class="text-decoration-none">
{{ msgPdf }}
</a>
</li>
Expand Down
2 changes: 2 additions & 0 deletions phpmyfaq/bookmarks.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
$templateVars = [
'msgMyBookmarks' => Translation::get('msgMyBookmarks'),
'bookmarksList' => $bookmark->getBookmarkList(),
'removeBookmark' => Translation::get('removeBookmark'),
'msgLinkToFAQ' => Translation::get('msgLinkToFAQ')
];

$template->addRenderedTwigOutput(
Expand Down
16 changes: 7 additions & 9 deletions phpmyfaq/faq.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
// Handle bookmarks
$bookmark = new Bookmark($faqConfig, $user);
if ($bookmarkAction === 'add' && isset($faqId)) {
$bookmark->saveFaqAsBookmarkById($faqId);
$bookmark->add($faqId);
}

if ($bookmarkAction === 'remove' && isset($faqId)) {
Expand Down Expand Up @@ -221,14 +221,9 @@
$templateVars = [
...$templateVars,
'bookmarkIcon' => $bookmark->isFaqBookmark($faqId) ? 'bi bi-bookmark-fill' : 'bi bi-bookmark',
'bookmarkLink' =>
$bookmark->isFaqBookmark($faqId)
?
sprintf('index.php?action=faq&bookmark_action=remove&id=%d', $faqId)
:
sprintf('index.php?action=faq&bookmark_action=add&id=%d', $faqId),
'msgAddBookmark' =>
$bookmark->isFaqBookmark($faqId) ? Translation::get('removeBookmark') : Translation::get('msgAddBookmark')
$bookmark->isFaqBookmark($faqId) ? Translation::get('removeBookmark') : Translation::get('msgAddBookmark'),
'isFaqBookmark' => $bookmark->isFaqBookmark($faqId)
];
}

Expand Down Expand Up @@ -307,7 +302,7 @@
'msgPdf' => Translation::get('msgPDF'),
'msgPrintFaq' => Translation::get('msgPrintArticle'),
'suggestLink' => $faqServices->getSuggestLink(),
'enableSendToFriend' => $this->configuration->get('main.enableSendToFriend'),
'enableSendToFriend' => $faqConfig->get('main.enableSendToFriend'),
'msgShareViaWhatsappText' => Translation::get('msgShareViaWhatsappText'),
'msgShareViaWhatsapp' => Translation::get('msgShareViaWhatsapp'),
'msgSend2Friend' => Translation::get('msgSend2Friend'),
Expand Down Expand Up @@ -344,6 +339,9 @@
'permissionEditFaq' => $user->perm->hasPermission($user->getUserId(), PermissionType::FAQ_EDIT->value),
'ad_entry_edit_1' => Translation::get('ad_entry_edit_1'),
'ad_entry_edit_2' => Translation::get('ad_entry_edit_2'),
'bookmarkAction' => $bookmarkAction ?? '',
'msgBookmarkAdded' => Translation::get('msgBookmarkAdded'),
'msgBookmarkRemoved' => Translation::get('msgBookmarkRemoved')
];

$twig = new TwigWrapper(PMF_ROOT_DIR . '/assets/templates');
Expand Down
11 changes: 8 additions & 3 deletions phpmyfaq/src/api-routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,16 @@
'controller' => [AutoCompleteController::class, 'search'],
'methods' => 'GET'
],
'api.private.bookmark' => [
'path' => 'bookmark/{bookmarkId}',
'controller' => [BookmarkController::class, 'delete'],
'api.private.bookmark.remove' => [
'path' => 'bookmark/remove/{bookmarkId}',
'controller' => [BookmarkController::class, 'remove'],
'methods' => 'DELETE'
],
'api.private.bookmark.add' => [
'path' => 'bookmark/add/{bookmarkId}',
'controller' => [BookmarkController::class, 'add'],
'methods' => 'POST'
],
'api.private.captcha' => [
'path' => 'captcha',
'controller' => [CaptchaController::class, 'renderImage'],
Expand Down
3 changes: 2 additions & 1 deletion phpmyfaq/src/phpMyFAQ/Bookmark.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function isFaqBookmark(int $faqId): bool
*
* @param int $faqId ID of the Faq
*/
public function saveFaqAsBookmarkById(int $faqId): bool
public function add(int $faqId): bool
{
$query = sprintf(
"INSERT INTO %sfaqbookmarks(userid, faqid) VALUES (%d, %d)",
Expand Down Expand Up @@ -130,6 +130,7 @@ public function getBookmarkList(): array
'url' => $link->toString(),
'title' => htmlspecialchars_decode((string) $faqData['title']),
'id' => $faqData['id'],
'answer' => $faqData['content']
];
}

Expand Down
Loading

0 comments on commit 42763da

Please sign in to comment.