diff --git a/README.md b/README.md index 555fc0d..798c356 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ The name comes from: - Bookmark archive - Extensions for [Firefox](https://addons.mozilla.org/de/firefox/addon/linkding-extension/) and [Chrome](https://chrome.google.com/webstore/detail/linkding-extension/beakmhbijpdhipnjhnclmhgjlddhidpe) - Bookmarklet that should work in most browsers +- Dark mode - Easy to set up using Docker - Uses SQLite as database - Works without Javascript @@ -161,4 +162,4 @@ The frontend is now available under http://localhost:8000 ## Community -- [linkding-extension](https://github.com/jeroenpardon/linkding-extension) Chromium compatible extension that wraps the linkding bookmarklet. Tested with Chrome, Edge, Brave. By [jeroenpardon](https://github.com/jeroenpardon) \ No newline at end of file +- [linkding-extension](https://github.com/jeroenpardon/linkding-extension) Chromium compatible extension that wraps the linkding bookmarklet. Tested with Chrome, Edge, Brave. By [jeroenpardon](https://github.com/jeroenpardon) diff --git a/bookmarks/admin.py b/bookmarks/admin.py index c351998..834723d 100644 --- a/bookmarks/admin.py +++ b/bookmarks/admin.py @@ -7,7 +7,7 @@ from django.utils.translation import ngettext, gettext from rest_framework.authtoken.admin import TokenAdmin from rest_framework.authtoken.models import Token -from bookmarks.models import Bookmark, Tag +from bookmarks.models import Bookmark, Tag, UserProfile from bookmarks.services.bookmarks import archive_bookmark, unarchive_bookmark @@ -77,8 +77,24 @@ class AdminTag(admin.ModelAdmin): ), messages.SUCCESS) +class AdminUserProfileInline(admin.StackedInline): + model = UserProfile + can_delete = False + verbose_name_plural = 'Profile' + fk_name = 'user' + + +class AdminCustomUser(UserAdmin): + inlines = (AdminUserProfileInline,) + + def get_inline_instances(self, request, obj=None): + if not obj: + return list() + return super(AdminCustomUser, self).get_inline_instances(request, obj) + + linkding_admin_site = LinkdingAdminSite() linkding_admin_site.register(Bookmark, AdminBookmark) linkding_admin_site.register(Tag, AdminTag) -linkding_admin_site.register(User, UserAdmin) +linkding_admin_site.register(User, AdminCustomUser) linkding_admin_site.register(Token, TokenAdmin) diff --git a/bookmarks/components/SearchAutoComplete.svelte b/bookmarks/components/SearchAutoComplete.svelte index 5d1b5e3..6b1e9e9 100644 --- a/bookmarks/components/SearchAutoComplete.svelte +++ b/bookmarks/components/SearchAutoComplete.svelte @@ -264,17 +264,4 @@ z-index: 2; } - /* TODO: Should be read from theme */ - .menu-item.selected > a { - background: #f1f1fc; - color: #5755d9; - } - - .group-item, .group-item:hover { - color: #999999; - text-transform: uppercase; - background: none; - font-size: 0.6rem; - font-weight: bold; - } \ No newline at end of file diff --git a/bookmarks/components/TagAutocomplete.svelte b/bookmarks/components/TagAutocomplete.svelte index efbdd18..1ef81aa 100644 --- a/bookmarks/components/TagAutocomplete.svelte +++ b/bookmarks/components/TagAutocomplete.svelte @@ -124,10 +124,4 @@ .menu.open { display: block; } - - /* TODO: Should be read from theme */ - .menu-item.selected > a { - background: #f1f1fc; - color: #5755d9; - } - \ No newline at end of file + diff --git a/bookmarks/migrations/0007_userprofile.py b/bookmarks/migrations/0007_userprofile.py new file mode 100644 index 0000000..20850b5 --- /dev/null +++ b/bookmarks/migrations/0007_userprofile.py @@ -0,0 +1,43 @@ +# Generated by Django 2.2.18 on 2021-03-26 22:39 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +def forwards(apps, schema_editor): + User = apps.get_model('auth', 'User') + UserProfile = apps.get_model('bookmarks', 'UserProfile') + for user in User.objects.all(): + try: + if user.profile: + continue + except UserProfile.DoesNotExist: + profile = UserProfile(user=user) + profile.save() + + +def reverse(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('bookmarks', '0006_bookmark_is_archived'), + ] + + operations = [ + migrations.CreateModel( + name='UserProfile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('theme', + models.CharField(choices=[('auto', 'Auto'), ('light', 'Light'), ('dark', 'Dark')], default='auto', + max_length=10)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', + to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.RunPython(forwards, reverse), + ] diff --git a/bookmarks/models.py b/bookmarks/models.py index fd9905b..7bbb535 100644 --- a/bookmarks/models.py +++ b/bookmarks/models.py @@ -2,7 +2,10 @@ from typing import List from django import forms from django.contrib.auth import get_user_model +from django.contrib.auth.models import User from django.db import models +from django.db.models.signals import post_save +from django.dispatch import receiver from bookmarks.utils import unique from bookmarks.validators import BookmarkURLValidator @@ -93,3 +96,33 @@ class BookmarkForm(forms.ModelForm): class Meta: model = Bookmark fields = ['url', 'tag_string', 'title', 'description', 'auto_close', 'return_url'] + + +class UserProfile(models.Model): + THEME_AUTO = 'auto' + THEME_LIGHT = 'light' + THEME_DARK = 'dark' + THEME_CHOICES = [ + (THEME_AUTO, 'Auto'), + (THEME_LIGHT, 'Light'), + (THEME_DARK, 'Dark'), + ] + user = models.OneToOneField(get_user_model(), related_name='profile', on_delete=models.CASCADE) + theme = models.CharField(max_length=10, choices=THEME_CHOICES, blank=False, default=THEME_AUTO) + + +class UserProfileForm(forms.ModelForm): + class Meta: + model = UserProfile + fields = ['theme'] + + +@receiver(post_save, sender=get_user_model()) +def create_user_profile(sender, instance, created, **kwargs): + if created: + UserProfile.objects.create(user=instance) + + +@receiver(post_save, sender=get_user_model()) +def save_user_profile(sender, instance, **kwargs): + instance.profile.save() diff --git a/bookmarks/static/logo.png b/bookmarks/static/logo.png new file mode 100644 index 0000000..893029e Binary files /dev/null and b/bookmarks/static/logo.png differ diff --git a/bookmarks/styles/base.scss b/bookmarks/styles/base.scss index 8c2388f..6299542 100644 --- a/bookmarks/styles/base.scss +++ b/bookmarks/styles/base.scss @@ -10,20 +10,22 @@ header { .navbar-brand { + display: flex; + align-items: center; + .logo { - background-color: $primary-color; - color: $light-color; - padding: 14px; + width: 28px; + height: 28px; } h1 { text-transform: uppercase; display: inline-block; + margin: 0 0 0 8px; } } .dropdown-toggle { - padding: 0; } } @@ -39,9 +41,18 @@ h2 { color: $gray-color-dark; } -// Button color should not change for anchor elements -.btn:visited:not(.btn-primary) { - color: $primary-color; +// Fix up visited styles +a:visited { + color: $link-color; +} +a:visited:hover { + color: $link-color-dark; +} +.btn-link:visited:not(.btn-primary) { + color: $link-color; +} +.btn-link:visited:not(.btn-primary):hover { + color: $link-color-dark; } // Increase spacing between columns @@ -57,4 +68,20 @@ h2 { // Override border color for tab block .tab-block { border-bottom: solid 1px $border-color; -} \ No newline at end of file +} + +// Form auto-complete menu +.form-autocomplete .menu { + .menu-item.selected > a, .menu-item > a:hover { + background: $secondary-color; + color: $primary-color; + } + + .group-item, .group-item:hover { + color: $gray-color; + text-transform: uppercase; + background: none; + font-size: 0.6rem; + font-weight: bold; + } +} diff --git a/bookmarks/styles/bookmarks.scss b/bookmarks/styles/bookmarks.scss index 3fe88e9..c1d1a90 100644 --- a/bookmarks/styles/bookmarks.scss +++ b/bookmarks/styles/bookmarks.scss @@ -39,7 +39,7 @@ ul.bookmark-list { .description { color: $gray-color-dark; - a { + a, a:visited:hover { color: $alternative-color; } } @@ -71,7 +71,7 @@ ul.bookmark-list { .tag-cloud { - a { + a, a:visited:hover { color: $alternative-color; } diff --git a/bookmarks/styles/dark.scss b/bookmarks/styles/dark.scss new file mode 100644 index 0000000..7cc4cb4 --- /dev/null +++ b/bookmarks/styles/dark.scss @@ -0,0 +1,27 @@ +/* Dark theme overrides */ + +/* Buttons */ +.btn.btn-primary { + background: $dt-primary-button-color; + border-color: darken($dt-primary-button-color, 5%); + + &:hover, &:active, &:focus { + background: darken($dt-primary-button-color, 5%); + border-color: darken($dt-primary-button-color, 10%); + } +} + +/* Focus ring*/ +a:focus, .btn:focus { + box-shadow: 0 0 0 .1rem rgba($primary-color, .5); +} + +/* Forms */ +.has-error .form-input, .form-input.is-error, .has-error .form-select, .form-select.is-error { + background: darken($error-color, 40%); +} + +/* Pagination */ +.pagination .page-item.active a { + background: $dt-primary-button-color; +} diff --git a/bookmarks/styles/index.scss b/bookmarks/styles/index.scss deleted file mode 100644 index 556c57c..0000000 --- a/bookmarks/styles/index.scss +++ /dev/null @@ -1,27 +0,0 @@ -// Font sizes -$html-font-size: 18px !default; - -//$alternative-color: #c84e00; -//$alternative-color: #FF84E8; -//$alternative-color: #98C1D9; -//$alternative-color: #7B287D; -$alternative-color: #05a6a3; -$alternative-color-dark: darken($alternative-color, 5%); - -// Import Spectre CSS lib -@import "../../node_modules/spectre.css/src/spectre"; -@import "../../node_modules/spectre.css/src/autocomplete"; -// Import Spectre icons -@import "../../node_modules/spectre.css/src/icons/icons-core"; -@import "../../node_modules/spectre.css/src/icons/icons-navigation"; -@import "../../node_modules/spectre.css/src/icons/icons-action"; -@import "../../node_modules/spectre.css/src/icons/icons-object"; - - -// Import style modules -@import "base"; -@import "util"; -@import "shared"; -@import "bookmarks"; -@import "settings"; -@import "auth"; diff --git a/bookmarks/styles/theme-dark.scss b/bookmarks/styles/theme-dark.scss new file mode 100644 index 0000000..77ce779 --- /dev/null +++ b/bookmarks/styles/theme-dark.scss @@ -0,0 +1,17 @@ +// Import custom variables +@import "variables-dark"; + +// Import Spectre CSS lib +@import "../../node_modules/spectre.css/src/spectre"; +@import "../../node_modules/spectre.css/src/autocomplete"; + +// Import style modules +@import "base"; +@import "util"; +@import "shared"; +@import "bookmarks"; +@import "settings"; +@import "auth"; + +// Dark theme overrides +@import "dark"; diff --git a/bookmarks/styles/theme-light.scss b/bookmarks/styles/theme-light.scss new file mode 100644 index 0000000..7ba2a2b --- /dev/null +++ b/bookmarks/styles/theme-light.scss @@ -0,0 +1,14 @@ +// Import custom variables +@import "variables-light"; + +// Import Spectre CSS lib +@import "../../node_modules/spectre.css/src/spectre"; +@import "../../node_modules/spectre.css/src/autocomplete"; + +// Import style modules +@import "base"; +@import "util"; +@import "shared"; +@import "bookmarks"; +@import "settings"; +@import "auth"; diff --git a/bookmarks/styles/variables-dark.scss b/bookmarks/styles/variables-dark.scss new file mode 100644 index 0000000..384e567 --- /dev/null +++ b/bookmarks/styles/variables-dark.scss @@ -0,0 +1,28 @@ +$html-font-size: 18px !default; + +$body-bg: #161822 !default; +$bg-color: lighten($body-bg, 5%) !default; +$bg-color-light: lighten($body-bg, 5%) !default; + +$border-color: #4C4E53 !default; +$border-color-dark: $border-color !default; + +$body-font-color: #b5bec8 !default; +$light-color: #fafafa !default; + +$gray-color: #7f879b !default; +$gray-color-dark: lighten($gray-color, 20%) !default; + +$primary-color: #a8b1ff !default; +$primary-color-dark: saturate($primary-color, 5%) !default; +$secondary-color: lighten($body-bg, 10%) !default; + +$link-color: $primary-color !default; +$link-color-dark: darken($link-color, 5%) !default; +$link-color-light: $link-color !default; + +$alternative-color: #59bdb9; +$alternative-color-dark: #73f1eb; + +/* Dark theme specific */ +$dt-primary-button-color: #5761cb !default; diff --git a/bookmarks/styles/variables-light.scss b/bookmarks/styles/variables-light.scss new file mode 100644 index 0000000..f24591d --- /dev/null +++ b/bookmarks/styles/variables-light.scss @@ -0,0 +1,4 @@ +$html-font-size: 18px !default; + +$alternative-color: #05a6a3; +$alternative-color-dark: darken($alternative-color, 5%); diff --git a/bookmarks/templates/bookmarks/empty_bookmarks.html b/bookmarks/templates/bookmarks/empty_bookmarks.html index 7434c09..0018889 100644 --- a/bookmarks/templates/bookmarks/empty_bookmarks.html +++ b/bookmarks/templates/bookmarks/empty_bookmarks.html @@ -2,7 +2,7 @@
You have no bookmarks yet
You can get started by adding bookmarks, - importing your existing bookmarks or configuring the + importing your existing bookmarks or configuring the browser extension or the bookmarklet.
diff --git a/bookmarks/templates/bookmarks/form.html b/bookmarks/templates/bookmarks/form.html index b9496b9..9a1dead 100644 --- a/bookmarks/templates/bookmarks/form.html +++ b/bookmarks/templates/bookmarks/form.html @@ -7,7 +7,7 @@ {{ form.return_url|attr:"type:hidden" }}