2026-03-09 01:07:16 -04:00
|
|
|
import stripe
|
|
|
|
|
|
|
|
|
|
from django.conf import settings
|
2026-03-01 21:19:12 -05:00
|
|
|
from django.contrib import messages
|
2026-03-04 00:07:10 -05:00
|
|
|
from django.contrib.auth.decorators import login_required
|
2026-03-07 00:05:32 -05:00
|
|
|
from django.db.models import Max, Q
|
2026-03-09 01:07:16 -04:00
|
|
|
from django.http import HttpResponse, HttpResponseForbidden, JsonResponse
|
2026-01-13 20:58:05 -05:00
|
|
|
from django.shortcuts import redirect, render
|
2026-03-09 01:07:16 -04:00
|
|
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
2026-02-21 23:13:23 -05:00
|
|
|
|
2026-03-09 16:08:28 -04:00
|
|
|
from apps.applets.models import Applet, UserApplet
|
2026-03-09 21:13:35 -04:00
|
|
|
from apps.applets.utils import applet_context
|
2026-03-11 13:59:43 -04:00
|
|
|
from apps.dashboard.forms import ExistingNoteItemForm, ItemForm
|
|
|
|
|
from apps.dashboard.models import Item, Note
|
2026-03-09 01:07:16 -04:00
|
|
|
from apps.lyric.models import PaymentMethod, Token, User, Wallet
|
2026-01-13 20:58:05 -05:00
|
|
|
|
2026-02-21 23:13:23 -05:00
|
|
|
|
2026-03-11 13:59:43 -04:00
|
|
|
APPLET_ORDER = ["wallet", "new-note", "my-notes", "username", "palette"]
|
2026-03-05 14:45:55 -05:00
|
|
|
UNLOCKED_PALETTES = frozenset(["palette-default"])
|
|
|
|
|
PALETTES = [
|
|
|
|
|
{"name": "palette-default", "label": "Earthman", "locked": False},
|
|
|
|
|
{"name": "palette-nirvana", "label": "Nirvana", "locked": True},
|
|
|
|
|
{"name": "palette-sheol", "label": "Sheol", "locked": True},
|
2026-03-07 15:05:49 -05:00
|
|
|
{"name": "palette-inferno", "label": "Inferno", "locked": True},
|
|
|
|
|
{"name": "palette-terrestre", "label": "Terrestre", "locked": True},
|
|
|
|
|
{"name": "palette-celestia", "label": "Celestia", "locked": True},
|
2026-03-02 15:45:12 -05:00
|
|
|
]
|
2026-03-02 13:57:03 -05:00
|
|
|
|
|
|
|
|
|
2026-03-11 13:59:43 -04:00
|
|
|
def _recent_notes(user, limit=3):
|
2026-03-07 00:05:32 -05:00
|
|
|
return (
|
2026-03-11 13:59:43 -04:00
|
|
|
Note
|
2026-03-07 00:05:32 -05:00
|
|
|
.objects
|
|
|
|
|
.filter(Q(owner=user) | Q(shared_with=user))
|
|
|
|
|
.annotate(last_item=Max('item__id'))
|
|
|
|
|
.order_by('-last_item')
|
|
|
|
|
.distinct()[:limit]
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-13 20:58:05 -05:00
|
|
|
def home_page(request):
|
2026-03-07 00:05:32 -05:00
|
|
|
context = {
|
|
|
|
|
"form": ItemForm(),
|
|
|
|
|
"palettes": PALETTES,
|
|
|
|
|
"page_class": "page-dashboard",
|
|
|
|
|
}
|
2026-03-05 16:08:40 -05:00
|
|
|
if request.user.is_authenticated:
|
2026-03-09 21:13:35 -04:00
|
|
|
context["applets"] = applet_context(request.user, "dashboard")
|
2026-03-11 13:59:43 -04:00
|
|
|
context["recent_notes"] = _recent_notes(request.user)
|
2026-03-05 16:08:40 -05:00
|
|
|
return render(request, "apps/dashboard/home.html", context)
|
2026-01-13 20:58:05 -05:00
|
|
|
|
2026-03-11 13:59:43 -04:00
|
|
|
def new_note(request):
|
2026-01-20 15:14:05 -05:00
|
|
|
form = ItemForm(data=request.POST)
|
|
|
|
|
if form.is_valid():
|
2026-03-11 13:59:43 -04:00
|
|
|
nunote = Note.objects.create()
|
2026-02-08 22:33:15 -05:00
|
|
|
if request.user.is_authenticated:
|
2026-03-11 13:59:43 -04:00
|
|
|
nunote.owner = request.user
|
|
|
|
|
nunote.save()
|
|
|
|
|
form.save(for_note=nunote)
|
|
|
|
|
return redirect(nunote)
|
2026-01-20 15:14:05 -05:00
|
|
|
else:
|
2026-03-07 00:05:32 -05:00
|
|
|
context = {
|
|
|
|
|
"form": form,
|
|
|
|
|
"palettes": PALETTES,
|
|
|
|
|
"page_class": "page-dashboard",
|
|
|
|
|
}
|
2026-03-06 21:34:43 -05:00
|
|
|
if request.user.is_authenticated:
|
2026-03-09 21:13:35 -04:00
|
|
|
context["applets"] = applet_context(request.user, "dashboard")
|
2026-03-11 13:59:43 -04:00
|
|
|
context["recent_notes"] = _recent_notes(request.user)
|
2026-03-06 21:34:43 -05:00
|
|
|
return render(request, "apps/dashboard/home.html", context)
|
2026-01-13 20:58:05 -05:00
|
|
|
|
2026-03-11 13:59:43 -04:00
|
|
|
def view_note(request, note_id):
|
|
|
|
|
our_note = Note.objects.get(id=note_id)
|
2026-02-22 21:50:25 -05:00
|
|
|
|
2026-03-11 13:59:43 -04:00
|
|
|
if our_note.owner:
|
2026-02-22 21:50:25 -05:00
|
|
|
if not request.user.is_authenticated:
|
|
|
|
|
return redirect("/")
|
2026-03-11 13:59:43 -04:00
|
|
|
if request.user != our_note.owner and request.user not in our_note.shared_with.all():
|
2026-02-22 21:50:25 -05:00
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
|
2026-03-11 13:59:43 -04:00
|
|
|
form = ExistingNoteItemForm(for_note=our_note)
|
2026-01-19 19:09:11 -05:00
|
|
|
|
2026-01-19 18:48:21 -05:00
|
|
|
if request.method == "POST":
|
2026-03-11 13:59:43 -04:00
|
|
|
form = ExistingNoteItemForm(for_note=our_note, data=request.POST)
|
2026-01-21 14:41:25 -05:00
|
|
|
if form.is_valid():
|
2026-01-23 22:39:12 -05:00
|
|
|
form.save()
|
2026-03-11 13:59:43 -04:00
|
|
|
return redirect(our_note)
|
|
|
|
|
return render(request, "apps/dashboard/note.html", {"note": our_note, "form": form})
|
|
|
|
|
|
|
|
|
|
def my_notes(request, user_id):
|
2026-02-08 22:18:41 -05:00
|
|
|
owner = User.objects.get(id=user_id)
|
2026-02-17 20:26:42 -05:00
|
|
|
if not request.user.is_authenticated:
|
|
|
|
|
return redirect("/")
|
|
|
|
|
if request.user.id != owner.id:
|
|
|
|
|
return HttpResponseForbidden()
|
2026-03-11 13:59:43 -04:00
|
|
|
return render(request, "apps/dashboard/my_notes.html", {"owner": owner})
|
2026-02-18 13:53:05 -05:00
|
|
|
|
2026-03-11 13:59:43 -04:00
|
|
|
def share_note(request, note_id):
|
|
|
|
|
our_note = Note.objects.get(id=note_id)
|
2026-02-18 15:14:35 -05:00
|
|
|
try:
|
|
|
|
|
recipient = User.objects.get(email=request.POST["recipient"])
|
2026-02-22 21:18:22 -05:00
|
|
|
if recipient == request.user:
|
2026-03-11 13:59:43 -04:00
|
|
|
return redirect(our_note)
|
|
|
|
|
our_note.shared_with.add(recipient)
|
2026-02-18 15:14:35 -05:00
|
|
|
except User.DoesNotExist:
|
|
|
|
|
pass
|
2026-03-01 21:19:12 -05:00
|
|
|
messages.success(request, "An invite has been sent if that address is registered.")
|
2026-03-11 13:59:43 -04:00
|
|
|
return redirect(our_note)
|
2026-03-02 13:57:03 -05:00
|
|
|
|
2026-03-04 00:07:10 -05:00
|
|
|
@login_required(login_url="/")
|
2026-03-05 14:45:55 -05:00
|
|
|
def set_palette(request):
|
2026-03-02 13:57:03 -05:00
|
|
|
if request.method == "POST":
|
2026-03-05 14:45:55 -05:00
|
|
|
palette = request.POST.get("palette", "")
|
|
|
|
|
if palette in UNLOCKED_PALETTES:
|
|
|
|
|
request.user.palette = palette
|
|
|
|
|
request.user.save(update_fields=["palette"])
|
2026-03-02 13:57:03 -05:00
|
|
|
return redirect("home")
|
2026-03-04 00:07:10 -05:00
|
|
|
|
|
|
|
|
@login_required(login_url="/")
|
|
|
|
|
def set_profile(request):
|
|
|
|
|
if request.method == "POST":
|
|
|
|
|
username = request.POST.get("username", "")
|
|
|
|
|
request.user.username = username
|
|
|
|
|
request.user.save(update_fields=["username"])
|
|
|
|
|
return redirect("/")
|
2026-03-05 14:45:55 -05:00
|
|
|
|
|
|
|
|
@login_required(login_url="/")
|
|
|
|
|
def toggle_applets(request):
|
|
|
|
|
checked = request.POST.getlist("applets")
|
2026-03-09 21:13:35 -04:00
|
|
|
for applet in Applet.objects.filter(context="dashboard"):
|
2026-03-05 14:45:55 -05:00
|
|
|
UserApplet.objects.update_or_create(
|
|
|
|
|
user=request.user,
|
|
|
|
|
applet=applet,
|
|
|
|
|
defaults={"visible": applet.slug in checked},
|
|
|
|
|
)
|
|
|
|
|
if request.headers.get("HX-Request"):
|
2026-03-05 16:08:40 -05:00
|
|
|
return render(request, "apps/dashboard/_partials/_applets.html", {
|
2026-03-09 21:13:35 -04:00
|
|
|
"applets": applet_context(request.user, "dashboard"),
|
2026-03-05 16:08:40 -05:00
|
|
|
"palettes": PALETTES,
|
2026-03-06 21:34:43 -05:00
|
|
|
"form": ItemForm(),
|
2026-03-11 13:59:43 -04:00
|
|
|
"recent_notes": _recent_notes(request.user),
|
2026-03-05 16:08:40 -05:00
|
|
|
})
|
2026-03-05 14:45:55 -05:00
|
|
|
return redirect("home")
|
2026-03-08 15:14:41 -04:00
|
|
|
|
|
|
|
|
@login_required(login_url="/")
|
2026-03-09 01:07:16 -04:00
|
|
|
@ensure_csrf_cookie
|
2026-03-08 15:14:41 -04:00
|
|
|
def wallet(request):
|
|
|
|
|
return render(request, "apps/dashboard/wallet.html", {
|
2026-03-11 00:58:24 -04:00
|
|
|
"wallet": request.user.wallet,
|
2026-03-15 01:17:09 -04:00
|
|
|
"pass_token": request.user.tokens.filter(token_type=Token.PASS).first(),
|
2026-03-11 00:58:24 -04:00
|
|
|
"coin": request.user.tokens.filter(token_type=Token.COIN).first(),
|
|
|
|
|
"free_tokens": list(request.user.tokens.filter(token_type=Token.FREE)),
|
|
|
|
|
"tithe_tokens": list(request.user.tokens.filter(token_type=Token.TITHE)),
|
|
|
|
|
"applets": applet_context(request.user, "wallet"),
|
|
|
|
|
"page_class": "page-wallet",
|
2026-03-08 15:14:41 -04:00
|
|
|
})
|
2026-03-09 01:07:16 -04:00
|
|
|
|
2026-03-15 01:17:09 -04:00
|
|
|
|
|
|
|
|
@login_required(login_url="/")
|
|
|
|
|
def kit_bag(request):
|
|
|
|
|
tokens = list(request.user.tokens.all())
|
|
|
|
|
return render(request, "core/_partials/_kit_bag_panel.html", {"tokens": tokens})
|
|
|
|
|
|
2026-03-11 00:58:24 -04:00
|
|
|
@login_required(login_url="/")
|
|
|
|
|
def toggle_wallet_applets(request):
|
|
|
|
|
checked = request.POST.getlist("applets")
|
|
|
|
|
for applet in Applet.objects.filter(context="wallet"):
|
|
|
|
|
UserApplet.objects.update_or_create(
|
|
|
|
|
user=request.user,
|
|
|
|
|
applet=applet,
|
|
|
|
|
defaults={"visible": applet.slug in checked},
|
|
|
|
|
)
|
|
|
|
|
if request.headers.get("HX-Request"):
|
|
|
|
|
return render(request, "apps/wallet/_partials/_applets.html", {
|
|
|
|
|
"applets": applet_context(request.user, "wallet"),
|
|
|
|
|
"wallet": request.user.wallet,
|
2026-03-15 01:17:09 -04:00
|
|
|
"pass_token": request.user.tokens.filter(token_type=Token.PASS).first(),
|
2026-03-11 00:58:24 -04:00
|
|
|
"coin": request.user.tokens.filter(token_type=Token.COIN).first(),
|
|
|
|
|
"free_tokens": list(request.user.tokens.filter(token_type=Token.FREE)),
|
|
|
|
|
"tithe_tokens": list(request.user.tokens.filter(token_type=Token.TITHE)),
|
|
|
|
|
})
|
|
|
|
|
return redirect("wallet")
|
|
|
|
|
|
2026-03-09 01:07:16 -04:00
|
|
|
@login_required(login_url="/")
|
|
|
|
|
def setup_intent(request):
|
|
|
|
|
stripe.api_key = settings.STRIPE_SECRET_KEY
|
|
|
|
|
user = request.user
|
|
|
|
|
if not user.stripe_customer_id:
|
|
|
|
|
customer = stripe.Customer.create(email=user.email)
|
|
|
|
|
user.stripe_customer_id = customer.id
|
|
|
|
|
user.save(update_fields=["stripe_customer_id"])
|
|
|
|
|
intent = stripe.SetupIntent.create(customer=user.stripe_customer_id)
|
|
|
|
|
return JsonResponse({
|
|
|
|
|
"client_secret": intent.client_secret,
|
|
|
|
|
"publishable_key": settings.STRIPE_PUBLISHABLE_KEY,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@login_required(login_url="/")
|
|
|
|
|
def save_payment_method(request):
|
|
|
|
|
stripe.api_key = settings.STRIPE_SECRET_KEY
|
|
|
|
|
pm_id = request.POST.get("payment_method_id")
|
|
|
|
|
pm = stripe.PaymentMethod.retrieve(pm_id)
|
|
|
|
|
stripe.PaymentMethod.attach(pm_id, customer=request.user.stripe_customer_id)
|
|
|
|
|
PaymentMethod.objects.create(
|
|
|
|
|
user=request.user,
|
|
|
|
|
stripe_pm_id=pm_id,
|
|
|
|
|
last4=pm.card.last4,
|
|
|
|
|
brand=pm.card.brand,
|
|
|
|
|
)
|
|
|
|
|
return JsonResponse({"last4": pm.card.last4, "brand": pm.card.brand})
|