Skip to content

Commit

Permalink
Merge pull request #210 from freelawproject/24-add-system-for-curators
Browse files Browse the repository at this point in the history
feat(subscription): Add system for curators
  • Loading branch information
mlissner authored May 2, 2023
2 parents a622fb8 + 807fae2 commit 795ff6f
Show file tree
Hide file tree
Showing 32 changed files with 4,252 additions and 76 deletions.
3,566 changes: 3,566 additions & 0 deletions bc/assets/static-global/js/htmx.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions bc/assets/static-global/js/htmx.min.js

Large diffs are not rendered by default.

179 changes: 179 additions & 0 deletions bc/assets/static-global/js/loading-states.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
;(function () {
let loadingStatesUndoQueue = []

function loadingStateContainer(target) {
return htmx.closest(target, '[data-loading-states]') || document.body
}

function mayProcessUndoCallback(target, callback) {
if (document.body.contains(target)) {
callback()
}
}

function mayProcessLoadingStateByPath(elt, requestPath) {
const pathElt = htmx.closest(elt, '[data-loading-path]')
if (!pathElt) {
return true
}

return pathElt.getAttribute('data-loading-path') === requestPath
}

function queueLoadingState(sourceElt, targetElt, doCallback, undoCallback) {
const delayElt = htmx.closest(sourceElt, '[data-loading-delay]')
if (delayElt) {
const delayInMilliseconds =
delayElt.getAttribute('data-loading-delay') || 200
const timeout = setTimeout(() => {
doCallback()

loadingStatesUndoQueue.push(() => {
mayProcessUndoCallback(targetElt, () => undoCallback())
})
}, delayInMilliseconds)

loadingStatesUndoQueue.push(() => {
mayProcessUndoCallback(targetElt, () => clearTimeout(timeout))
})
} else {
doCallback()
loadingStatesUndoQueue.push(() => {
mayProcessUndoCallback(targetElt, () => undoCallback())
})
}
}

function getLoadingStateElts(loadingScope, type, path) {
return Array.from(htmx.findAll(loadingScope, `[${type}]`)).filter(
(elt) => mayProcessLoadingStateByPath(elt, path)
)
}

function getLoadingTarget(elt) {
if (elt.getAttribute('data-loading-target')) {
return Array.from(
htmx.findAll(elt.getAttribute('data-loading-target'))
)
}
return [elt]
}

htmx.defineExtension('loading-states', {
onEvent: function (name, evt) {
if (name === 'htmx:beforeRequest') {
const container = loadingStateContainer(evt.target)

const loadingStateTypes = [
'data-loading',
'data-loading-class',
'data-loading-class-remove',
'data-loading-disable',
'data-loading-aria-busy',
]

let loadingStateEltsByType = {}

loadingStateTypes.forEach((type) => {
loadingStateEltsByType[type] = getLoadingStateElts(
container,
type,
evt.detail.pathInfo.requestPath
)
})

loadingStateEltsByType['data-loading'].forEach((sourceElt) => {
getLoadingTarget(sourceElt).forEach((targetElt) => {
queueLoadingState(
sourceElt,
targetElt,
() =>
(targetElt.style.display =
sourceElt.getAttribute('data-loading') ||
'inline-block'),
() => (targetElt.style.display = 'none')
)
})
})

loadingStateEltsByType['data-loading-class'].forEach(
(sourceElt) => {
const classNames = sourceElt
.getAttribute('data-loading-class')
.split(' ')

getLoadingTarget(sourceElt).forEach((targetElt) => {
queueLoadingState(
sourceElt,
targetElt,
() =>
classNames.forEach((className) =>
targetElt.classList.add(className)
),
() =>
classNames.forEach((className) =>
targetElt.classList.remove(className)
)
)
})
}
)

loadingStateEltsByType['data-loading-class-remove'].forEach(
(sourceElt) => {
const classNames = sourceElt
.getAttribute('data-loading-class-remove')
.split(' ')

getLoadingTarget(sourceElt).forEach((targetElt) => {
queueLoadingState(
sourceElt,
targetElt,
() =>
classNames.forEach((className) =>
targetElt.classList.remove(className)
),
() =>
classNames.forEach((className) =>
targetElt.classList.add(className)
)
)
})
}
)

loadingStateEltsByType['data-loading-disable'].forEach(
(sourceElt) => {
getLoadingTarget(sourceElt).forEach((targetElt) => {
queueLoadingState(
sourceElt,
targetElt,
() => (targetElt.disabled = true),
() => (targetElt.disabled = false)
)
})
}
)

loadingStateEltsByType['data-loading-aria-busy'].forEach(
(sourceElt) => {
getLoadingTarget(sourceElt).forEach((targetElt) => {
queueLoadingState(
sourceElt,
targetElt,
() => (targetElt.setAttribute("aria-busy", "true")),
() => (targetElt.removeAttribute("aria-busy"))
)
})
}
)
}

if (name === 'htmx:beforeOnLoad') {
while (loadingStatesUndoQueue.length > 0) {
loadingStatesUndoQueue.shift()()
}
}
},
})
})()
2 changes: 1 addition & 1 deletion bc/assets/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
<link rel="icon" href="{% static "ico/favicon.ico" %}" sizes="any">
<link rel="icon" href="{% static "svg/icon.svg" %}" type="image/svg+xml">
<link rel="apple-touch-icon" href="{% static "png/apple-touch-icon.png" %}">

</head>

<body class="font-sans text-bcb-black">
Expand All @@ -38,6 +37,7 @@

<script src="{% static "js/dropdown.js" %}"></script>

{% block footer-scripts %}{% endblock %}
</body>

</html>
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
{% comment %}
This template renders a yellow button that fills the horizontal space available and
This template renders a button that fills the horizontal space available and
takes up to three keyword arguments described below:

Parameters:
link: The URL that the hyperlink points to.
text: Text of the button.
size: Sets the padding of the button. Possible values are: sm, md, lg
color: Sets the background color of the button. Possible values are: saffron, white

It's advisable to wrap this template with a div tag and use the parent element to handle
the sizing and the responsive behavior. Here's an example:

<div class="flex w-44 md:w-48 lg:w-52">
{% include 'includes/yellow-button.html' with link="https://free.law/contact/" text='Contact Us' size="sm" %}
{% include 'includes/action-button.html' with link="https://free.law/contact/" text='Contact Us' size="sm" color='saffron'%}
</div>
{% endcomment %}

<a href="{{link}}"
class="flex justify-center w-full text-center whitespace-nowrap no-underline border border-transparent rounded-md shadow-sm text-bcb-black bg-saffron-400 hover:bg-saffron-500 {% if size == 'lg' %} p-4 {% elif size == 'md' %} p-3 {% else %} p-2 {% endif %}">
class="flex justify-center w-full text-center whitespace-nowrap no-underline border border-transparent rounded-md shadow-sm text-bcb-black {% if color == 'saffron' %} bg-saffron-400 hover:bg-saffron-500 {% else %} bg-white hover:bg-gray-100{% endif %} {% if size == 'lg' %} p-4 {% elif size == 'md' %} p-3 {% else %} p-2 {% endif %}">
{{text}}
</a>

17 changes: 14 additions & 3 deletions bc/assets/templates/includes/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
{% include './inlines/caret.svg' %}
</div>
</button>
<div class="absolute top-16 -translate-x-8 xl:-translate-x-32">
<div class="absolute top-16 -translate-x-8 xl:-translate-x-32 z-50">
<div id="dropdown" class="z-10 bg-white hidden divide-y divide-gray-100 rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 overflow-hidden max-w-xs md:w-screen lg:max-w-md">
<div class="relative grid px-5 py-6 gap-4 text-sm text-gray-700 dark:text-gray-400 md:gap-8 sm:p-8" aria-labelledby="dropdownNavbarButton">
<a href="{% url 'big_cases_about' %}" class="flex items-start -m-3 p-3 hover:bg-gray-100" aria-label="About Me">
Expand Down Expand Up @@ -127,12 +127,23 @@
<a href="{% url 'collaboration' %}" aria-label="Chat Apps" class="text-sm lg:text-base font-medium uppercase hover:text-gray-700">
Chat Apps
</a>
{% if user.is_authenticated %}
<a href="{% url 'add_cases' %}" aria-label="Chat Apps" class="text-sm lg:text-base font-medium uppercase hover:text-gray-700">
Add a case
</a>
{% endif %}
</div>
</div>
</div>

<div class="hidden md:flex w-28 text-sm font-medium">
{% include './yellow-button.html' with link="https://free.law/donate/" text="Donate" size='sm' %}
<div class="hidden md:flex items-center">
<div class="w-28 text-sm font-medium mr-1">
{% include './action-button.html' with link="https://free.law/donate/" text="Donate" size='sm' color='saffron' %}
</div>
<div class="w-20 text-sm font-medium sm:text-md {% if user.is_authenticated %}hidden{% endif %}">
{% url 'sign-in' as sign_in %}
{% include './action-button.html' with link=sign_in text="Sign In" size='sm' %}
</div>
</div>
</div>
</div>
Expand Down
5 changes: 5 additions & 0 deletions bc/assets/templates/includes/inlines/magnifying-glass.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions bc/assets/templates/includes/inlines/spinning-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions bc/assets/templates/includes/submit-button.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<input
type="submit"
value={{value}}
value="{{value}}"
name="submit"
class="tracking-[.8px] text-center whitespace-nowrap border border-transparent rounded-md shadow-sm text-base font-medium no-underline px-4 py-2 sm:text-md cursor bg-saffron-400 hover:bg-saffron-500"/>
class="tracking-[.8px] text-center whitespace-nowrap border border-transparent rounded-md shadow-sm text-base font-medium no-underline px-4 py-2 sm:text-md cursor bg-saffron-400 hover:bg-saffron-500"/>
5 changes: 3 additions & 2 deletions bc/settings/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,14 @@
# other apps
"django_rq",
"tailwind",
"django_htmx",
]

if DEVELOPMENT:
INSTALLED_APPS.append("django_browser_reload")

TAILWIND_APP_NAME = "bc.web"

INTERNAL_IPS = ("127.0.0.1",)

MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
Expand All @@ -55,13 +54,15 @@
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_ratelimit.middleware.RatelimitMiddleware",
"django_htmx.middleware.HtmxMiddleware",
"csp.middleware.CSPMiddleware",
]

if DEVELOPMENT:
MIDDLEWARE.append(
"django_browser_reload.middleware.BrowserReloadMiddleware"
)
MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")

ROOT_URLCONF = "bc.urls"
AUTH_USER_MODEL = "users.User"
Expand Down
16 changes: 13 additions & 3 deletions bc/settings/project/security.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import socket

import environ

from ..django import DEVELOPMENT
from ..django import DEVELOPMENT, INSTALLED_APPS
from ..third_party.aws import AWS_S3_CUSTOM_DOMAIN

env = environ.FileAwareEnv()
Expand All @@ -27,8 +29,16 @@
CSRF_COOKIE_SECURE = False
SESSION_COOKIE_DOMAIN = None
# For debug_toolbar
# INSTALLED_APPS.append('debug_toolbar')
INTERNAL_IPS = ("127.0.0.1",)
INSTALLED_APPS.append("debug_toolbar")

# Get the list of IPv4 addresses for the interface on the same host. If you want to know more
# about this, you can check the following links:
# https://github.com/freelawproject/bigcases2/pull/210#discussion_r1182078837
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#configure-internal-ips
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS = [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips] + [
"127.0.0.1"
]
else:
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Expand Down
16 changes: 2 additions & 14 deletions bc/subscription/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from rest_framework.response import Response
from rq import Retry

from bc.channel.selectors import get_enabled_channels
from bc.subscription.exceptions import (
IdempotencyKeyMissing,
WebhookNotSupported,
Expand All @@ -19,7 +18,7 @@
from .models import FilingWebhookEvent
from .tasks import (
check_webhook_before_posting,
make_post_for_webhook_event,
enqueue_posts_for_docket_alert,
process_fetch_webhook_event,
process_filing_webhook_event,
)
Expand Down Expand Up @@ -113,18 +112,7 @@ def handle_recap_fetch_webhook(request: Request) -> Response:
docket_alert.save(update_fields=["status"])

# schedule tasks to create the new posts(tweet and toot) without thumbnails.
for channel in get_enabled_channels():
queue.enqueue(
make_post_for_webhook_event,
channel.pk,
docket_alert.subscription.pk, # type: ignore
docket_alert.pk,
None,
retry=Retry(
max=settings.RQ_MAX_NUMBER_OF_RETRIES,
interval=settings.RQ_RETRY_INTERVAL,
),
)
enqueue_posts_for_docket_alert(docket_alert.pk)
else:
# schedule task to retrieve the document and create the transaction before posting.
queue.enqueue(
Expand Down
Loading

0 comments on commit 795ff6f

Please sign in to comment.