Release v1.0.0
Some checks failed
CI / Checks (push) Failing after 13m2s

This commit is contained in:
2025-08-20 21:00:50 +02:00
commit b4338e2769
401 changed files with 23576 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
{% extends "ui/base.html" %}
{% load crispy_forms_tags i18n ui %}
{% block title %}{% translate 'Log in' %}{% endblock %}
{% block body_class %}d-flex justify-content-center flex-column{% endblock %}
{% block body %}
<div class="container">
<div class="row">
<div class="col col-12 col-md-6 offset-md-3">
<div class="card">
<div class="card-header text-center">
<p class="fs-3 mb-0">{{ SITE_TITLE }}</p>
</div>
<div class="card-body">
{% if not MODEL_AUTH_IS_DISABLED %}
{% crispy form %}
{% endif %}
{% if not MODEL_AUTH_IS_DISABLED and HOTPOCKET_OIDC_IS_ENABLED %}
<hr>
{% endif %}
{% if HOTPOCKET_OIDC_IS_ENABLED %}
<a
class="btn btn-primary d-block"
href="{% url 'social:begin' 'hotpocket_oidc' %}"
>
{% blocktranslate %}Log in with {{ HOTPOCKET_OIDC_DISPLAY_NAME }}{% endblocktranslate %}
</a>
{% endif %}
</div>
</div>
{% include "ui/ui/partials/uname.html" %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,31 @@
{% extends "ui/base.html" %}
{% load crispy_forms_tags i18n ui %}
{% block body_class %}d-flex justify-content-center flex-column{% endblock %}
{% block body %}
<div class="container">
<div class="row">
<div class="col col-12 col-md-6 offset-md-3">
<div class="card">
<div class="card-header text-center">
<p class="fs-3 mb-0">{{ SITE_TITLE }}</p>
</div>
<div class="card-body">
<p class="lead">{% translate "See you later!" %}</p>
<p class="mb-0">
<a
class="btn btn-primary"
href="{% url 'ui.accounts.login' %}"
>
{% translate 'Log in' %}
</a>
</p>
</div>
</div>
{% include "ui/ui/partials/uname.html" %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,31 @@
{% load i18n static ui %}
<ul class="nav nav-tabs my-3">
<li class="nav-item">
{% if active_tab == 'profile' %}
<a class="nav-link active" aria-current="page" href="#">
{% else %}
<a class="nav-link" aria-current="page" href="{% url 'ui.accounts.settings.profile' %}">
{% endif %}
{% translate 'Profile' %}
</a>
</li>
<li class="nav-item">
{% if active_tab == 'password' %}
<a class="nav-link active" aria-current="page" href="#">
{% else %}
<a class="nav-link" aria-current="page" href="{% url 'ui.accounts.settings.password' %}">
{% endif %}
{% translate 'Password' %}
</a>
</li>
<li class="nav-item">
{% if active_tab == 'settings' %}
<a class="nav-link active" aria-current="page" href="#">
{% else %}
<a class="nav-link" aria-current="page" href="{% url 'ui.accounts.settings.settings' %}">
{% endif %}
{% translate 'Settings' %}
</a>
</li>
</ul>

View File

@@ -0,0 +1,27 @@
{% extends "ui/page.html" %}
{% load crispy_forms_tags i18n static ui %}
{% block title %}{{ title }} | {% translate 'Account' %}{% endblock %}
{% block page_body %}
<div class="container">
<p class="display-6 my-3">{% translate 'Account' %}</p>
{% include 'ui/accounts/partials/nav.html' with active_tab=active_tab %}
{% if is_federated %}
<div class="alert alert-info my-3" role="alert">
<h4 class="alert-heading">{% translate 'Heads up!' %}</h4>
<p class="lead mb-0">
{% blocktranslate %}
Your account is federated from an external provider. You can update
your details in your Identity Provider.
{% endblocktranslate %}
</p>
</div>
{% endif %}
{% crispy form %}
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends "ui/page.html" %}
{% load crispy_forms_tags i18n static ui %}
{% block title %}{% translate 'Archive a save?' %} | {% translate 'My Saves' %}{% endblock %}
{% block page_body %}
<div class="container">
<p class="display-6 my-3">{% translate 'Archive a save?' %}</p>
{% include 'ui/associations/partials/archive_confirmation.html' %}
{% crispy form %}
</div>
{% endblock %}

View File

@@ -0,0 +1,161 @@
{% extends "ui/page.html" %}
{% load i18n static ui %}
{% block title %}{% translate 'My Saves' %}{% endblock %}
{% block top_nav_title %}{{ title }}{% endblock %}
{% block page_body %}
<div id="BrowseSavesView" class="container">
<div class="row mt-3">
<div class="col col-12 d-md-flex align-items-center mb-1 mb-md-3">
<form action="{% url 'ui.associations.browse' %}" class="mb-2 mb-md-0" method="GET">
<input type="hidden" name="search" value="{{ params.search|default_if_none:'' }}">
<input type="hidden" name="limit" value="{{ params.limit }}">
<input type="hidden" name="mode" value="{{ params.mode.value }}">
<button
class="btn btn-secondary btn-sm"
>
{% translate 'First page' %}
</button>
</form>
<form
action="{% url 'ui.associations.browse' %}"
class="ms-auto mb-2 mb-md-0 d-md-flex"
id="BrowseSavesView-Search"
method="GET"
>
<div class="mb-2 mb-md-0 me-0 me-md-2">
<select class="form-select form-select-sm" aria-label="{% translate 'Browse mode' %}" name="mode">
<option value="" {% if params.mode.value == 'DEFAULT' %}selected{% endif %}>
{% translate 'Default' %}
</option>
<option value="STARRED" {% if params.mode.value == 'STARRED' %}selected{% endif %}>
{% translate 'Starred' %}
</option>
<option value="ARCHIVED" {% if params.mode.value == 'ARCHIVED' %}selected{% endif %}>
{% translate 'Archived' %}
</option>
</select>
</div>
<div class="mb-2 mb-md-0 me-0 me-md-2">
<input
aria-label="Search"
class="form-control form-control-sm"
name="search"
placeholder="{% translate 'Search' %}"
type="text"
value="{{ params.search|default_if_none:'' }}"
>
</div>
<div class="mb-2 mb-md-0 me-0 me-md-2">
<button class="btn btn-primary btn-sm" type="submit">
{% translate 'Apply' %}
</button>
<a
class="btn btn-link btn-sm"
href="{% url 'ui.associations.browse' %}"
>
{% translate 'Reset' %}
</a>
</div>
</form>
</div>
</div>
<div class="row ui-saves">
{% include "ui/associations/partials/associations.html" with associations=associations params=params %}
</div>
<div class="row {% if not associations and params.before is None %}d-none{% endif %}">
<div class="col col-12">
<p class="mb-3 text-center">
<button
class="btn btn-primary {% if not before %}disabled{% endif %} ui-noscript-hide ui-load-more-button"
hx-get="{{ next_url }}"
hx-push-url="true"
hx-swap="beforeend"
hx-target="#BrowseSavesView .ui-saves"
>
{% translate 'Load more' %}
</button>
<a
class="btn btn-primary {% if not before %}disabled{% endif %} ui-noscript-show"
href="{{ next_url }}"
>
{% translate 'Load more' %}
</a>
</p>
</div>
</div>
<template id="BrowseSavesView-ArchiveModal">
<div class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% translate 'Archive a save?' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Close' %}"></button>
</div>
<div class="modal-body">
{% include 'ui/associations/partials/archive_confirmation.html' with alert_class="mb-0" %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate 'Cancel' %}</button>
<button type="button" class="btn btn-danger" data-ui-modal-action="confirm">
{% translate 'Archive' %}
</button>
</div>
</div>
</div>
</div>
</template>
<template id="BrowseSavesView-RefreshModal">
<div class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% translate 'Refresh a save?' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Close' %}"></button>
</div>
<div class="modal-body">
{% include 'ui/associations/partials/refresh_confirmation.html' with alert_class="mb-0" %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate 'Cancel' %}</button>
<button type="button" class="btn btn-warning" data-ui-modal-action="confirm">
{% translate 'Refresh' %}
</button>
</div>
</div>
</div>
</div>
</template>
<template id="BrowseSavesView-DeleteModal">
<div class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% translate 'Delete a save?' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Close' %}"></button>
</div>
<div class="modal-body">
{% include 'ui/associations/partials/delete_confirmation.html' with alert_class="mb-0" %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate 'Cancel' %}</button>
<button type="button" class="btn btn-danger" data-ui-modal-action="confirm">
{% translate 'Delete' %}
</button>
</div>
</div>
</div>
</div>
</template>
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends "ui/page.html" %}
{% load crispy_forms_tags i18n static ui %}
{% block title %}{% translate 'Delete a save?' %} | {% translate 'My Saves' %}{% endblock %}
{% block page_body %}
<div class="container">
<p class="display-6 my-3">{% translate 'Delete a save?' %}</p>
{% include 'ui/associations/partials/delete_confirmation.html' %}
{% crispy form %}
</div>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends "ui/page.html" %}
{% load crispy_forms_tags i18n static ui %}
{% block title %}{% translate 'Edit a link' %}{% endblock %}
{% block page_body %}
<div class="container">
<p class="display-6 my-3">{% translate 'Edit a link' %}</p>
{% crispy form %}
</div>
{% endblock %}

View File

@@ -0,0 +1,6 @@
{% load i18n static ui %}
<div class="alert alert-danger {{ alert_class|default_if_none:'' }}">
<h4 class="alert-heading">{% translate 'Danger Zone' %}</h4>
<p class="mb-0">{% translate 'Are you sure you want to archive this save?' %}</p>
</div>

View File

@@ -0,0 +1,116 @@
{% load i18n static ui %}
<div class="card ui-save-card">
<div class="card-body">
<h5 class="card-title">
<a href="{% url 'ui.associations.view' pk=association.pk %}">
{% if association.title %}
{{ association.title }}
{% else %}
{% translate 'Untitled' %}
{% endif %}
</a>
</h5>
{% if association.description %}
<p class="card-text">{{ association.description|truncatechars:125 }}</p>
{% endif %}
</div>
<div class="card-footer d-flex align-items-center">
<a href="{{ association.target.url }}" target="_blank" rel="noopener noreferer"><small>{{ association.target.url|render_url_domain }}</small></a>
<div class="ms-auto flex-shrink-0 d-flex align-items-center">
{% if not association.archived_at %}
<div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">{% translate 'Processing' %}</span>
</div>
{% if association.is_starred %}
<i class="bi bi-star-fill ms-2"></i>
{% endif %}
<div class="btn-group dropup ms-2">
<button class="btn btn-secondary btn-sm dropdown-toggle ui-actions-menu-trigger" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
{% if association.is_starred %}
<li>
<a
class="dropdown-item"
hx-get="{% url 'ui.associations.unstar' pk=association.pk %}"
hx-indicator='[data-association="{{ association.pk }}"] .spinner-border'
hx-swap="innerHTML"
hx-target='[data-association="{{ association.pk }}"]'
href="{% url 'ui.associations.unstar' pk=association.pk %}"
>
<i class="bi bi-star"></i> {% translate 'Unstar' %}
</a>
</li>
{% else %}
<li>
<a
class="dropdown-item"
hx-get="{% url 'ui.associations.star' pk=association.pk %}"
hx-indicator='[data-association="{{ association.pk }}"] .spinner-border'
hx-swap="innerHTML"
hx-target='[data-association="{{ association.pk }}"]'
href="{% url 'ui.associations.star' pk=association.pk %}"
>
<i class="bi bi-star-fill"></i> {% translate 'Star' %}
</a>
</li>
{% endif %}
<li>
<a
class="dropdown-item"
href="{% url 'ui.associations.edit' pk=association.pk %}"
>
<i class="bi bi-pencil"> </i> {% translate 'Edit' %}
</a>
</li>
<li>
<a
class="dropdown-item text-warning"
data-ui-modal="#BrowseSavesView-RefreshModal"
hx-confirm='CANHAZCONFIRM'
hx-indicator='[data-association="{{ association.pk }}"] .spinner-border'
hx-post="{% url 'ui.associations.refresh' pk=association.pk %}"
hx-swap="none"
hx-target='[data-association="{{ association.pk }}"]'
hx-vars='{"canhazconfirm":true}'
href="{% url 'ui.associations.refresh' pk=association.pk %}"
>
<i class="bi bi-arrow-repeat"> </i> {% translate 'Refresh' %}
</a>
</li>
<li>
<a
class="dropdown-item text-danger"
data-ui-modal="#BrowseSavesView-ArchiveModal"
hx-confirm='CANHAZCONFIRM'
hx-post="{% url 'ui.associations.archive' pk=association.pk %}"
hx-swap="delete"
hx-target='[data-association="{{ association.pk }}"]'
hx-vars='{"canhazconfirm":true}'
href="{% url 'ui.associations.archive' pk=association.pk %}"
>
<i class="bi bi-archive"></i> {% translate 'Archive' %}
</a>
</li>
</ul>
</div>
{% else %}
<i class="bi bi-archive-fill"></i>
<a
class="btn btn-sm btn-danger ms-2"
data-ui-modal="#BrowseSavesView-DeleteModal"
hx-confirm='CANHAZCONFIRM'
hx-post="{% url 'ui.associations.delete' pk=association.pk %}"
hx-swap="delete"
hx-target='[data-association="{{ association.pk }}"]'
hx-vars='{"canhazconfirm":true}'
href="{% url 'ui.associations.delete' pk=association.pk %}"
>
<i class="bi bi-trash"></i>
</a>
{% endif %}
</div>
</div>
</div>

View File

@@ -0,0 +1,28 @@
{% load i18n static ui %}
{% for association in associations %}
<div class="col col-12 col-md-6 col-lg-4 col-xl-3 mb-3" data-association="{{ association.pk }}">
{% include 'ui/associations/partials/association_card.html' with association=association %}
</div>
{% empty %}
{% if not HTMX %}
<div class="col col-12 mb-4 text-center">
<p class="display-4">¯\_(ツ)_/¯</p>
{% if params.before is None %}
<p>{% translate "It's empty. Too empty..." %}</p>
{% if params.mode == 'DEFAULT' %}
<p class="mb-0">
<a
class="btn btn-primary"
href="{% url 'ui.saves.create' %}"
>
<i class="bi bi-plus"></i> {% translate 'Save a link' %}
</a>
</p>
{% endif %}
{% else %}
<p class="mb-0">{% translate "You've reached the end of the line." %}</p>
{% endif %}
</div>
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,7 @@
{% load i18n static ui %}
<div class="alert alert-danger {{ alert_class|default_if_none:'' }}">
<h4 class="alert-heading">{% translate 'Point of no return' %}</h4>
<p class="lead mb-0">{% translate 'Are you sure you want to delete this save?' %}</p>
<p class="mb-0"><strong>This operation cannot be undone.</strong></p>
</div>

View File

@@ -0,0 +1,6 @@
{% load i18n static ui %}
<div class="alert alert-warning {{ alert_class|default_if_none:'' }}">
<h4 class="alert-heading">{% translate 'Caution!' %}</h4>
<p class="mb-0">{% translate 'Are you sure you want to refresh this save?' %}</p>
</div>

View File

@@ -0,0 +1,70 @@
{% extends "ui/page.html" %}
{% load i18n static ui %}
{% block title %}{% translate 'Saved!' %}{% endblock %}
{% block button_bar_class %}d-none{% endblock %}
{% block page_body %}
<div class="container">
{% if association.target.is_netloc_banned %}
<div class="alert alert-warning mt-3" role="alert">
<h4 class="alert-heading">{% translate 'Heads up!' %}</h4>
<p class="lead mb-2">
{% translate "Your link has been saved, but..." %}
</p>
<p>
{% blocktranslate %}
The link is for a site that makes it difficult to extract metadata from. As such, sane defaults were used. This could make the save hard to identify.
{% endblocktranslate %}
<br>
{% blocktranslate %}
Click the button below to edit the save's metadata to your liking.
{% endblocktranslate %}
</p>
<hr>
<p class="mb-0">
<a
class="btn btn-primary btn-sm"
href="{% url 'ui.associations.edit' pk=association.pk %}"
role="button"
>
<i class="bi bi-pencil"></i> {% translate 'Edit' %}
</a>
<a
class="btn btn-success btn-sm"
href="{% url 'ui.associations.browse' %}"
role="button"
>
<i class="bi bi-house-heart-fill"></i> {% translate 'Home' %}
</a>
</p>
</div>
{% else %}
<div class="alert alert-success mt-3" role="alert">
<h4 class="alert-heading">{% translate 'Saved!' %}</h4>
<p class="lead">
{% translate "Your link has been saved!" %}
</p>
<hr>
<p class="mb-0">
<a
class="btn btn-primary btn-sm"
href="{% url 'ui.associations.view' pk=association.pk %}"
role="button"
>
<i class="bi bi-eye"></i> {% translate 'View the link' %}
</a>
<a
class="btn btn-success btn-sm"
href="{% url 'ui.associations.browse' %}"
role="button"
>
<i class="bi bi-house-heart-fill"></i> {% translate 'Home' %}
</a>
</p>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends "ui/page.html" %}
{% load crispy_forms_tags i18n static ui %}
{% block title %}{% translate 'Refresh a save?' %} | {% translate 'My Saves' %}{% endblock %}
{% block page_body %}
<div class="container">
<p class="display-6 my-3">{% translate 'Refresh a save?' %}</p>
{% include 'ui/associations/partials/refresh_confirmation.html' %}
{% crispy form %}
</div>
{% endblock %}

View File

@@ -0,0 +1,85 @@
{% extends "ui/page.html" %}
{% load i18n static ui %}
{% block title %}{{ association.title }}{% endblock %}
{% block button_bar_class %}d-none{% endblock %}
{% block page_body %}
<div id="ViewAssociationView" class="container">
<p class="display-3 mt-3 mb-0 text-center">
{% if association.title %}
{{ association.title }}
{% else %}
{{ association.target.url }}
{% endif %}
</p>
<p class="lead mb-3 text-center ui-width-reference">
<span class="text-muted">
{% blocktranslate with created_at=association.created_at %}Saved on {{ created_at }}{% endblocktranslate %}
</span>
<br>
<a href="{{ association.target.url }}" target="_blank" rel="noopener noreferer">
{% translate 'View original' %} <i class="bi bi-box-arrow-up-right"></i>
</a>
</p>
{% if show_controls %}
<p class="mb-3 text-center">
{% spaceless %}
<a
class="btn btn-primary btn-sm"
href="{% url 'ui.associations.edit' pk=association.pk %}"
role="button"
>
<i class="bi bi-pencil"></i> {% translate 'Edit' %}
</a>
<a
class="btn btn-secondary btn-sm ms-2 ui-noscript-hide ui-share-button"
href="#"
role="button"
>
<i class="bi bi-box-arrow-up"></i> {% translate 'Share' %}
</a>
{% endspaceless %}
</p>
{% endif %}
{% if association.description %}
<div class="row mb-3">
<div class="col-12 col-lg-6 offset-lg-3">
{{ association.description }}
</div>
</div>
{% endif %}
{% if association.target.is_youtube_video %}
<div class="ui-embed">
{% if not request.user.is_anonymous and request.user.settings.auto_load_embeds %}
{% include "ui/saves/partials/embed.html" with save=association.target %}
{% else %}
<div class="alert alert-warning mb-0 ui-noscript-hide">
<h4 class="alert-heading">{% translate 'Heads up!' %}</h4>
<p class="lead">
{% blocktranslate %}
The link contains embeddable content. Loading it means that
you accept cookies from the site - YouTube.
{% endblocktranslate %}
</p>
<hr>
<p class="mb-0">
<button
class="btn btn-primary btn-sm"
hx-post="{% url 'ui.saves.embed' %}"
hx-swap="innerHTML"
hx-target=".ui-embed"
hx-vars='{"pk":"{{ association.target.pk }}"}'
role="button"
>
<i class="bi bi-eye"></i> {% translate 'Load content' %}
</button>
</p>
</div>
{% endif %}
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,37 @@
{% load i18n static ui %}
<!doctype html>
<!--
A
_ _ _ _ _ _
| |__ | |_| |__ | | __ _| |__ ___ _ __ | |
| '_ \| __| '_ \| |/ _` | '_ \/ __| | '_ \| |
| |_) | |_| | | | | (_| | |_) \__ \_| |_) | |
|_.__/ \__|_| |_|_|\__,_|_.__/|___(_) .__/|_|
|_|
production
-->
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,user-scalable=no">
<meta name="generator" content="pl.bthlabs.HotPocket.backend@{{ IMAGE_ID }}">
<meta name="theme-color" content="#2b3035"/>
<title>{% block title %}{% translate 'Not Found' %}{% endblock %} | {{ SITE_TITLE }}</title>
<link href="{% static 'ui/css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'ui/css/bootstrap-icons.min.css' %}" rel="stylesheet">
<link href="{% static 'ui/css/hotpocket-backend.css' %}" rel="stylesheet">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="HotPocket">
<link rel="apple-touch-icon" href="{% static 'ui/img/icon-180.png' %}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'ui/img/icon-32.png' %}">
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'ui/img/icon-16.png' %}">
<link rel="manifest" href="{% url 'ui.meta.manifest_json' %}">
{% block page_head %}{% endblock %}
</head>
<body class="{% block body_class %}{% endblock %}" hx-headers='{"x-csrftoken": "{{ csrf_token }}"}'>
{% block body %}
{% endblock %}
{% block scripts %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,21 @@
{% extends "ui/page.html" %}
{% load i18n static ui %}
{% block title %}{% translate 'Oops!' %}{% endblock %}
{% block button_bar_class %}d-none{% endblock %}
{% block page_body %}
<div class="container">
<div class="alert alert-danger mt-3" role="alert">
<h4 class="alert-heading">{% translate 'Oops!' %}</h4>
<p class="lead mb-0">
{% translate "HotPocket couldn't complete this operation." %}
</p>
<p>{% translate 'Please try again later.' %}</p>
<hr>
<p class="mb-0">RequestID: <code>{{ REQUEST_ID }}</code></p>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,20 @@
{% extends "ui/page.html" %}
{% load i18n static ui %}
{% block title %}{% translate 'Not Found' %}{% endblock %}
{% block button_bar_class %}d-none{% endblock %}
{% block page_body %}
<div class="container">
<div class="alert alert-danger mt-3" role="alert">
<h4 class="alert-heading">{% translate 'Not Found' %}</h4>
<p class="lead mb-0">
{% translate "This isn't the page you were looking for." %}
</p>
<hr>
<p class="mb-0">RequestID: <code>{{ REQUEST_ID }}</code></p>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends "ui/page.html" %}
{% load crispy_forms_tags i18n static ui %}
{% block title %}{% translate 'Import from Pocket' %}{% endblock %}
{% block page_body %}
<div class="container">
<p class="display-6 my-3">{% translate 'Import from Pocket' %}</p>
{% crispy form %}
</div>
{% endblock %}

View File

@@ -0,0 +1,157 @@
{% extends "ui/base.html" %}
{% load i18n static ui %}
{% block body %}
<nav id="navbar" class="navbar navbar-expand-sm bg-body-tertiary fixed-top">
<div class="container">
<ul class="navbar-nav flex-row flex-grow-1">
{% if not request.user.is_anonymous %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle ui-navbar-brand" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{% spaceless %}
<img src="{% static 'ui/img/icon-180.png' %}">
<span class="ms-2">{% block top_nav_title %}HotPocket{% endblock %}</span>
{% endspaceless %}
</a>
<ul class="dropdown-menu position-absolute">
<li>
<a class="dropdown-item" href="{% browse_associations_url %}">
<i class="bi bi-house-heart-fill"></i> {% translate "Home" %}
</a>
</li>
<li>
<a class="dropdown-item" href="{% browse_associations_url mode='STARRED' %}">
<i class="bi bi-star-fill"></i> {% translate "Starred" %}
</a>
</li>
<li>
<a class="dropdown-item" href="{% browse_associations_url mode='ARCHIVED' %}">
<i class="bi bi-archive-fill"></i> {% translate "Archived" %}
</a>
</li>
</ul>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link pe-none ui-navbar-brand">
<img src="{% static 'ui/img/icon-180.png' %}">
<span class="ms-2">{{ SITE_TITLE }}</span>
</a>
</li>
{% endif %}
<li class="nav-item ms-auto d-flex align-items-center">
<a class="nav-link px-1 py-0 fs-3" data-bs-toggle="offcanvas" href="#offcanvas-controls" aria-controls="offcanvas-controls">
<i class="bi bi-person-circle"></i>
</a>
</li>
</ul>
</div>
</nav>
<div class="ui-viewport">
{% spaceless %}
<div class="ui-messages container my-3">
{% if messages %}
{% for message in messages %}
<div class="alert {{ message|alert_class}} mt-2">
{{ message }}
<button type="button" class="btn-close ui-noscript-hide" data-bs-dismiss="alert" aria-label="{% translate 'Close' %}"></button>
</div>
{% endfor %}
{% endif %}
</div>
{% endspaceless %}
{% block page_body %}
{% endblock %}
<template id="Messages-Alert">
<div class="alert alert-dismissible fade show mt-2">
<span class="ui-alert-message"></span>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="{% translate 'Close' %}"></button>
</div>
</template>
</div>
{% if not request.user.is_anonymous %}
<nav id="button-bar" class="navbar navbar-expand-sm bg-body-tertiary fixed-bottom {% block button_bar_class %}{% endblock %}">
<div class="container">
<ul class="navbar-nav my-0 mx-auto">
<li class="nav-item d-flex align-items-center ms-2">
<a class="nav-link py-1 px-1 text-primary ui-button-bar-link" href="{% url 'ui.saves.create' %}">
<i class="bi bi-plus-circle-fill"></i>
</a>
</li>
</ul>
</div>
</nav>
{% endif %}
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvas-controls" aria-labelledby="offcanvas-controls-label">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvas-controls-label">
{% if request.user.is_anonymous %}
{% translate 'Guest' %}
{% else %}
{{ request.user.get_full_name }}
{% endif %}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="{% translate 'Close' %}"></button>
</div>
<div class="offcanvas-body d-flex flex-column px-0 pt-0">
<ul class="nav flex-column flex-grow-1">
{% if not request.user.is_anonymous %}
<li class="nav-item">
<a class="nav-link" href="{% url 'ui.accounts.settings' %}">
<i class="bi bi-person-gear"></i>
{% translate 'Account' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'ui.imports.pocket' %}">
<i class="bi bi-file-earmark-arrow-up"></i>
{% translate 'Import from Pocket' %}
</a>
</li>
<li class="nav-item mt-auto">
<a class="nav-link" href="{% url 'ui.accounts.logout' %}">
<i class="bi bi-box-arrow-right"></i>
{% translate 'Log out' %}
</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'ui.accounts.login' %}">
<i class="bi bi-box-arrow-in-left"></i>
{% translate 'Log in' %}
</a>
</li>
{% endif %}
</ul>
{% include "ui/ui/partials/uname.html" %}
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{% static 'ui/js/bootstrap.bundle.min.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/htmx.min.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.htmx.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.htmx.confirm.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.ui.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.ui.Form.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.ui.Messages.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.ui.Modal.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.ui.BrowseSavesView.js' %}" type="text/javascript"></script>
<script src="{% static 'ui/js/hotpocket-backend.ui.ViewAssociationView.js' %}" type="text/javascript"></script>
{% block page_scripts %}{% endblock %}
<script type="text/javascript">
(() => {
window.HotPocket.run({
debug: {% if DEBUG %}true{% else %}false{% endif %},
});
})();
</script>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends "ui/page.html" %}
{% load crispy_forms_tags i18n static ui %}
{% block title %}{% translate 'Save a link' %}{% endblock %}
{% block page_body %}
<div class="container">
<p class="display-6 my-3">{% translate 'Save a link' %}</p>
{% crispy form %}
</div>
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% load i18n static ui %}
{% if save.is_youtube_video %}
<div class="mb-0 d-flex justify-content-center">
<iframe
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
class="ui-youtube-iframe"
frameborder="0"
height="200"
src="{{ save|render_youtube_embed_url }}"
title="YouTube video player"
width="320"
>
</iframe>
</div>
{% endif %}

View File

@@ -0,0 +1 @@
<input {% for attribute, value in attributes.items %}{{ attribute }}="{{ value }}"{% endfor %} />

View File

@@ -0,0 +1,9 @@
<div{% if formactions.attrs %} {{ formactions.flat_attrs }}{% endif %} class="{{ formactions.css_class|default_if_none:'mb-3' }} row">
{% if label_class %}
<div class="aab {{ label_class }}"></div>
{% endif %}
<div class="{{ field_class }}">
{{ fields_output|safe }}
</div>
</div>

View File

@@ -0,0 +1,3 @@
<div{% if formactions.attrs %} {{ formactions.flat_attrs }}{% endif %} class="{{ formactions.css_class|default_if_none:'mb-3' }}">
{{ fields_output|safe }}
</div>

View File

@@ -0,0 +1,8 @@
<p class="mb-0 mt-2 text-center text-muted ui-uname">
<span>
<a href="https://hotpocket.app/" target="_blank" rel="noopener noreferer">{{ SITE_TITLE }}</a> v{{ VERSION }}
(<code>{{ IMAGE_ID }}</code>)
</span>
<br>
<span>Copyright &copy; 2025-present by BTHLabs. All rights reserved.</span>
</p>