Compare commits

...

107 commits

Author SHA1 Message Date
hynet-mel
8ab3d34614 Squashed commit of the following:
commit 7cc6c35654
Merge: e7a558a5 228679e4
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Feb 24 13:55:57 2024 +0000

    Merge branch 'develop' into stable

commit 228679e49e
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Feb 24 13:55:10 2024 +0000

    2024.02 release

commit d610a46c32
Merge: ed0b403c 09257632
Author: Weblate <noreply@weblate.org>
Date:   Tue Feb 20 16:24:40 2024 +0000

    Merge branch 'origin/develop' into Weblate.

commit ed0b403c33
Merge: 0f842b30 a77a9e04
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue Feb 20 16:24:37 2024 +0000

    Merge pull request 'Auto-approve followbacks (frontend part)' (#365) from Oneric/akkoma-fe:followbacks-fe into develop

    Reviewed-on: #365

commit 0925763267
Merge: 0f842b30 e292af42
Author: Weblate <noreply@weblate.org>
Date:   Tue Feb 20 16:20:18 2024 +0000

    Merge branch 'origin/develop' into Weblate.

commit 0f842b300b
Merge: 865cb6f9 050c7df2
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue Feb 20 16:20:14 2024 +0000

    Merge pull request 'Display profile background of other users' (#371) from Oneric/akkoma-fe:profile-backgrounds into develop

    Reviewed-on: #371

commit e292af4211
Merge: 865cb6f9 4e7d5d3a
Author: Weblate <noreply@weblate.org>
Date:   Mon Feb 19 14:04:37 2024 +0000

    Merge branch 'origin/develop' into Weblate.

commit 865cb6f96a
Merge: 8dce31d0 a5733499
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Feb 19 14:04:34 2024 +0000

    Merge pull request 'Add Indonesian translation' (#366) from leap123/akkoma-fe:leap123-patch-1 into develop

    Reviewed-on: #366

commit 4e7d5d3a08
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:33 2024 +0000

    Translated using Weblate (Vietnamese)

    Currently translated at 92.2% (965 of 1046 strings)

    Translated using Weblate (Vietnamese)

    Currently translated at 92.2% (965 of 1046 strings)

    Translated using Weblate (Vietnamese)

    Currently translated at 84.3% (882 of 1046 strings)

    Translated using Weblate (Vietnamese)

    Currently translated at 84.3% (882 of 1046 strings)

    Translated using Weblate (Vietnamese)

    Currently translated at 79.8% (835 of 1046 strings)

    Translated using Weblate (Vietnamese)

    Currently translated at 79.8% (835 of 1046 strings)

    Co-authored-by: Nguyễn Gia Phong <cnx@loang.net>
    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: xarvos <huyngo@disroot.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/vi/
    Translation: Pleroma fe/pleroma-fe

commit f2d0c4c7d5
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:33 2024 +0000

    Translated using Weblate (Japanese)

    Currently translated at 0.2% (3 of 1046 strings)

    Added translation using Weblate (Japanese)

    Co-authored-by: Nakaya <s_fpfb_sub-second@yahoo.co.jp>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja/
    Translation: Pleroma fe/pleroma-fe

commit 02a6591f20
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:33 2024 +0000

    Translated using Weblate (Greek)

    Currently translated at 15.9% (167 of 1046 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: getimiskon <getimiskon@disroot.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/el/
    Translation: Pleroma fe/pleroma-fe

commit 94c70f8914
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:33 2024 +0000

    Merge branch 'origin/develop' into Weblate.

    Translated using Weblate (Chinese (Simplified))

    Currently translated at 100.0% (1046 of 1046 strings)

    Co-authored-by: Poesty Li <poesty7450@gmail.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/zh_Hans/
    Translation: Pleroma fe/pleroma-fe

commit 3ba8c90e1e
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:33 2024 +0000

    Translated using Weblate (Russian)

    Currently translated at 68.7% (719 of 1046 strings)

    Co-authored-by: Mel <hi@mel.gg>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ru/
    Translation: Pleroma fe/pleroma-fe

commit 83db80f88c
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:32 2024 +0000

    Translated using Weblate (Polish)

    Currently translated at 100.0% (1046 of 1046 strings)

    Translated using Weblate (Polish)

    Currently translated at 100.0% (1046 of 1046 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: subtype <subtype@hollow.capital>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/pl/
    Translation: Pleroma fe/pleroma-fe

commit 1489d92997
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:32 2024 +0000

    Translated using Weblate (Japanese (ja_EASY))

    Currently translated at 72.3% (757 of 1046 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: kazari <6c577a54-aac9-482a-955e-745c858445e3@simplelogin.com>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_EASY/
    Translation: Pleroma fe/pleroma-fe

commit db5c9572dc
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:32 2024 +0000

    Translated using Weblate (Italian)

    Currently translated at 80.4% (841 of 1045 strings)

    Translated using Weblate (Italian)

    Currently translated at 65.3% (683 of 1045 strings)

    Co-authored-by: Cuche <cuche@mailbox.org>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/it/
    Translation: Pleroma fe/pleroma-fe

commit 5bb53c8b0d
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:32 2024 +0000

    Translated using Weblate (Spanish)

    Currently translated at 93.9% (983 of 1046 strings)

    Translated using Weblate (Spanish)

    Currently translated at 92.5% (967 of 1045 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: taretka <info@tarteka.net>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/es/
    Translation: Pleroma fe/pleroma-fe

commit 665f88f5c7
Author: Weblate <noreply@weblate.org>
Date:   Thu Feb 15 17:32:32 2024 +0000

    Translated using Weblate (Indonesian)

    Currently translated at 71.9% (753 of 1046 strings)

    Co-authored-by: Aldiantoro Nugroho <kriwil@gmail.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/id/
    Translation: Pleroma fe/pleroma-fe

commit 050c7df2e6
Author: Oneric <oneric@oneric.stub>
Date:   Wed Feb 14 17:44:57 2024 +0000

    Display profile background of other users

    And add a new frontend setting to hide other people's background.

commit a77a9e04d9
Author: Oneric <oneric@oneric.stub>
Date:   Wed Jan 17 19:50:47 2024 +0000

    Expose new server-side permit_followback setting

    Added to backend in AkkomaGang/akkoma#674

commit a57334991e
Author: leap123 <leapofazzam@protonmail.com>
Date:   Fri Jan 19 04:27:26 2024 +0000

    Add Indonesian translation

    The Indonesian translation is technically almost complete, just not added to messages.js

commit 8dce31d0ad
Merge: d304be65 ea9ad4d6
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 20 18:49:40 2023 +0000

    Merge pull request 'Improve UX of subject / Content Warning field' (#362) from hazelnoot/akkoma-fe:develop into develop

    Reviewed-on: #362

commit ea9ad4d600
Author: Hazel Koehler <acomputerdog@gmail.com>
Date:   Wed Dec 20 12:39:31 2023 -0500

    fix "always show content warning" setting

commit 34e2800f59
Author: Hazel Koehler <acomputerdog@gmail.com>
Date:   Sat Dec 16 14:44:26 2023 -0500

    add button to toggle the spoiler / CW field

commit 3d65eccf04
Author: Hazel Koehler <acomputerdog@gmail.com>
Date:   Sat Dec 16 13:37:59 2023 -0500

    use main emoji button for spoiler / CW field

commit d304be654f
Merge: aee97fa9 1de62fff
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 15 12:24:33 2023 +0000

    Merge pull request 'Update build setup instructions' (#343) from norm/pleroma-fe:update-build-setup into develop

    Reviewed-on: #343

commit aee97fa948
Merge: 7da1687f d9e1bc4d
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 15 12:24:07 2023 +0000

    Merge pull request 'Re-added extension checking for still-image' (#346) from Mergan/pleroma-fe:still-image-ultimate into develop

    Reviewed-on: #346

commit 7da1687f31
Merge: a8f193d4 235c734d
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 15 12:12:28 2023 +0000

    Merge pull request 'Use relative font size and set appropriate overflow behavior' (#355) from xarvos/pleroma-fe:update-css into develop

    Reviewed-on: #355

commit a8f193d4bd
Merge: 81c82e11 beee99e7
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 15 11:57:47 2023 +0000

    Merge pull request 'Stop constant movement of notifications due to changing timestamps' (#353) from Oneric/akkoma-fe:notification-writhing into develop

    Reviewed-on: #353

commit 81c82e11bc
Merge: 00cadce5 ab250c2f
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 15 11:54:15 2023 +0000

    Merge pull request 'Explicitly set SameSite attribute for cookies' (#352) from Oneric/akkoma-fe:cookie-samesite into develop

    Reviewed-on: #352

commit 00cadce5b4
Merge: 40a08f27 1b28ec3b
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 15 11:52:59 2023 +0000

    Merge pull request 'Format dates, times with window.navigator.language instead of UI i18n locale' (#354) from smitten/akkoma-fe:date-locale-fix-cherrypick into develop

    Reviewed-on: #354

commit 40a08f279b
Merge: 52b0b6f0 c524a47e
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Thu Nov 16 10:41:17 2023 +0000

    Merge pull request 'Drop broken "@ symbol as icon" setting' (#359) from Oneric/akkoma-fe:at-icon into develop

    Reviewed-on: #359

commit c524a47e6f
Author: Oneric <oneric@oneric.stub>
Date:   Wed Nov 15 23:36:19 2023 +0100

    Drop broken "@ symbol as icon" setting

    It was merged into pleroma-fe on 2022-02-03 in
    76547fe66d and imported
    into akkoma-fe on 2022-06-08 with the merge commit
    f6cf509a04.

    However, something went wrong in the merge and while the setting
    and its infrastructure exist, it is never used anywhere and @ is
    always displayed as text.

    Given it existed in this broken state for nearly one and a half years,
    never worked on akkoma-fe and no bugs were filed about this, it appears
    nobody cares, so let’s just remove it.

commit 235c734d37
Author: Ngô Ngọc Đức Huy <huyngo@disroot.org>
Date:   Sun Nov 5 09:21:49 2023 +0700

    Use overflow: auto for description

    Previously it sets overflow: scroll, so there's an unnecessary
    horizontal scroll.
    overflow: auto only shows scrollbar when it overflows

commit deaef1d0b9
Author: Ngô Ngọc Đức Huy <huyngo@disroot.org>
Date:   Sun Nov 5 09:21:01 2023 +0700

    Use relative unit for font size

commit 1b28ec3b72
Author: smitten <everything-cozy@pm.me>
Date:   Wed Nov 1 23:10:57 2023 -0400

    Match UI i18n configuration to browser locales

commit c9dc8f00f9
Author: smitten <everything-cozy@pm.me>
Date:   Mon Oct 30 18:54:07 2023 -0400

    Use window.navigator.language before interface i18n language

commit beee99e733
Author: Oneric <oneric@oneric.stub>
Date:   Wed Oct 18 22:51:24 2023 +0200

    Stop notifications boxes from change size over time

    Notifications about favourites and follows use .notification-right,
    notifications about replies instead use .heading-right.

    Previously only the former set a min-width, however the
    chosen value of 3em was too small to fit the worst case.
    As a consequence, when the timestamp text changes over time,
    its element width changes, which may result in neighbouring text
    (no longer) needing to wrap to a new line in turn changing the size
    of the whole notification box pushing older notification boxes down/up.

    These constant movements at the side of the screen can be quite
    annoying and confusing when the cause cannot be immediately discerned.

    Avoid this, by reserving enough space for any timestamp.

    For English, the worst case is the five-character 'XXmin', since the
    short identifier for minutes is the longest with three letters.
    With two exceptions, all other current localisation also do not exceed
    three letters in any short unit identifier up to days.
    However, some localisations (e.g. Polish) additionally insert a space
    between numerical value and unit. This matches SI recommendations
    pushing the worst case to 6 characters.

    6 characters will be sufficient for timestamps up to 3 weeks in all
    languages (minus prev exceptions), which seems reasonable enough
    as beyond this timestamps rarely change anyway.

    The aforementioned exceptions being Vietnamese and Occitan,
    but in the current localisation all or the relevant short unit
    identifiers are identical to the long forms indicating this is
    just due to incomplete translation.
    Indeed, Vietnamese Wikipedia (read through machine translation) suggests
    “ph” is commonly used as unit identifiers for minutes, but the current
    localisation fully spells it out as “phút”.

commit ccb0ffdc8a
Author: Oneric <oneric@oneric.stub>
Date:   Tue Oct 24 18:23:47 2023 +0200

    Don't show direction in notification timestamps

    Currently all notifications except follow-related once include
    and explicit direction text. (It missing in follow notifs is due to an
    omission in 804ba0cdb6 which only added
    the newly introduced with-direction to status-related notifs. Before,
    presumably all notifs included direction text.)

    But in the notification tray horizontal space is scarce
    and notifs can already be assumed to only come from the past.
    While it might not be too bad for the English localisation’s 4-letter
    ' ago' suffix, e.g. the Indonesian localisation’s ' yang lalu' needs
    10 letters.

    Thus instead of fixing the omission for follow-related notifs,
    drop direction text from all notification timestamps.

commit ab250c2f3a
Author: Oneric <oneric@oneric.stub>
Date:   Thu Oct 19 00:45:11 2023 +0200

    Explicitly set SameSite attribute for cookies

    Modern browsers start to tighten down on third-party access to cookies.
    E.g. in current Firefox, a warning about the userLanguage cookie was
    shown since it did not yet explicitly set the SameSite attribute and the
    default is about to change.

    The cookie name being referred to as BACKEND_LANGUAGE_COOKIE_NAME
    suggests it should be readable by the actual Akkoma backend, which can
    live at a different domain than akkoma-fe. Thus explicitly enable
    sharing with third-party sites.

    No warnings were shown for other cookies, so I assume
    this was the only one not yet setting SameSite.

commit 1de62fffcd
Author: Norm <normandy@biribiri.dev>
Date:   Sat Sep 23 20:34:30 2023 -0400

    Update config.example.json link and example domain

commit 306cea04a1
Author: Norm <normandy@biribiri.dev>
Date:   Sat Sep 23 20:33:46 2023 -0400

    Use corepack in build instructions

commit d9e1bc4d99
Author: Mergan <mergan@viridianpatriots.com>
Date:   Mon Oct 2 15:29:54 2023 -0700

    Re-added extension checking for still-image

    - Bonus refactoring

commit 52b0b6f008
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Mon Oct 2 13:28:23 2023 +0100

    add VI to messages.js

commit 8afbe5e3bc
Merge: 58be48d1 1056b89f
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Sep 25 13:29:29 2023 +0000

    Merge pull request 'Making still-image better' (#341) from Mergan/pleroma-fe:still-image-ultimate into develop

    Reviewed-on: #341

commit 58be48d164
Merge: 174f98b1 6a1409e0
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Sep 25 13:24:12 2023 +0000

    Merge pull request 'Do not copy all emojis in recentEmoji getter' (#340) from sn0w/akkoma-fe:feature/optimize-recent-emojis into develop

    Reviewed-on: #340

commit 1056b89fd1
Author: Mergan <mergan@viridianpatriots.com>
Date:   Tue Sep 12 04:19:08 2023 -0700

    Disabled aggressive matching for reduced motion (we search for gif now)

commit 3e64d78d05
Author: Mergan <mergan@viridianpatriots.com>
Date:   Tue Sep 12 04:17:28 2023 -0700

    An oopsie

commit 3947aafeba
Author: Mergan <mergan@viridianpatriots.com>
Date:   Tue Sep 12 04:08:47 2023 -0700

    Aligning canvas to image

commit 345934c2f3
Author: Mergan <mergan@viridianpatriots.com>
Date:   Tue Sep 12 03:36:14 2023 -0700

    Make label visible on avatar

commit 42a13b0f1b
Author: Mergan <mergan@viridianpatriots.com>
Date:   Tue Sep 12 03:05:42 2023 -0700

    Modify label

commit e13c4b6b85
Author: Mergan <mergan@viridianpatriots.com>
Date:   Tue Sep 12 02:48:53 2023 -0700

    Revamped still-image

commit 6a1409e09b
Author: sn0w <me@sn0w.cx>
Date:   Sun Sep 3 16:05:56 2023 +0200

    Do not copy all emojis in recentEmoji getter

commit e7a558a533
Merge: 5106fced 174f98b1
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Aug 5 14:17:53 2023 +0100

    Merge branch 'develop' into stable

commit 174f98b1cb
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Aug 5 14:17:42 2023 +0100

    don't die on my arm box please

commit 5106fcedd6
Merge: 1db322ba ab146b67
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Aug 5 13:29:55 2023 +0100

    Merge branch 'develop' into stable

commit ab146b67ec
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Aug 5 13:29:44 2023 +0100

    version

commit 1db322bae6
Merge: e530c2b4 3b4208ea
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Aug 5 13:29:26 2023 +0100

    Merge branch 'develop' into stable

commit 3b4208ea41
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Aug 4 16:48:13 2023 +0100

    debounce emoji searching

commit 856324fa26
Merge: 5a9322d2 0da0e2c8
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Aug 4 15:09:56 2023 +0000

    Merge pull request 'Make favicon next to post username use Still-Image functionality' (#327) from Mergan/pleroma-fe:still-image-instance-favicon into develop

    Reviewed-on: #327

commit 5a9322d2c7
Merge: b52bfbcb 43d0a245
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Aug 4 15:09:32 2023 +0000

    Merge pull request 'StillImage: Improved animated image detection' (#335) from yukijoou/akkoma-fe:still-image-better-gif-detection into develop

    Reviewed-on: #335

commit b52bfbcba0
Merge: 2b05a738 fa5d31b7
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 14:56:22 2023 +0000

    Merge branch 'origin/develop' into Weblate.

commit 2b05a738c9
Merge: 29cfdcbb 7c14e1a5
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Aug 4 14:56:20 2023 +0000

    Merge pull request 'Add replying info for redraft' (#332) from xarvos/pleroma-fe:fix-reply-redraft into develop

    Reviewed-on: #332

commit fa5d31b793
Merge: 29cfdcbb 5174b959
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 14:54:38 2023 +0000

    Merge branch 'origin/develop' into Weblate.

commit 29cfdcbbcd
Author: tusooa <tusooa@kazv.moe>
Date:   Fri Aug 4 15:54:04 2023 +0100

    Add load more to blocks/mutes

commit 5174b95918
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Greek)

    Currently translated at 15.4% (162 of 1046 strings)

    Added translation using Weblate (Greek)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: getimiskon <getimiskon@disroot.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/el/
    Translation: Pleroma fe/pleroma-fe

commit 56528206b8
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Portuguese)

    Currently translated at 62.7% (656 of 1046 strings)

    Co-authored-by: Jonathan Soares <jontix@murena.io>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/pt/
    Translation: Pleroma fe/pleroma-fe

commit 590380c084
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Thai)

    Currently translated at 1.7% (18 of 1046 strings)

    Added translation using Weblate (Thai)

    Co-authored-by: Chanakan Mungtin <chanakan5591@chanakancloud.net>
    Co-authored-by: Pongsatorn Paewsoongnern <akkoma@miraiverse.me>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/th/
    Translation: Pleroma fe/pleroma-fe

commit dfcbb3c1ec
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Ukrainian)

    Currently translated at 100.0% (1046 of 1046 strings)

    Co-authored-by: Denys Nykula <vegan@libre.net.ua>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/uk/
    Translation: Pleroma fe/pleroma-fe

commit 9b01c31283
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Chinese (Simplified))

    Currently translated at 100.0% (1046 of 1046 strings)

    Co-authored-by: Poesty Li <poesty7450@gmail.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/zh_Hans/
    Translation: Pleroma fe/pleroma-fe

commit 6be003b2f8
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (French)

    Currently translated at 98.3% (1029 of 1046 strings)

    Co-authored-by: Thomate <thomas@burdick.fr>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/fr/
    Translation: Pleroma fe/pleroma-fe

commit cc302fb0e2
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Japanese (ja_EASY))

    Currently translated at 72.3% (757 of 1046 strings)

    Translated using Weblate (Japanese (ja_EASY))

    Currently translated at 71.9% (753 of 1046 strings)

    Co-authored-by: Hikaru Shinagawa <hikali.47041@gmail.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: kazari <6c577a54-aac9-482a-955e-745c858445e3@simplelogin.com>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_EASY/
    Translation: Pleroma fe/pleroma-fe

commit c4e768f977
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Indonesian)

    Currently translated at 67.8% (710 of 1046 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: Yonle <yonle@lecturify.net>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/id/
    Translation: Pleroma fe/pleroma-fe

commit d8a7217335
Author: Weblate <noreply@weblate.org>
Date:   Fri Aug 4 13:34:26 2023 +0000

    Translated using Weblate (Turkish)

    Currently translated at 9.7% (102 of 1046 strings)

    Co-authored-by: Hasan Yıldız <hasanyildiz0@yaani.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/tr/
    Translation: Pleroma fe/pleroma-fe

commit 35d10ab9ff
Merge: af97dd74 51ebe643
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Aug 4 13:34:23 2023 +0000

    Merge pull request 'components: Honour 'prefers reduced motion' setting in many components' (#333) from yukijoou/akkoma-fe:fix-reduced-motion into develop

    Reviewed-on: #333

commit 43d0a24547
Author: Yuki Joou <yukijoou@laposte.net>
Date:   Thu Aug 3 16:03:12 2023 +0200

    StillImage: Improved animated image detection

    This patch makes StillImage's animation detection return early in cases
    where we can't detect the mimetype of the image. It also sets the image
    as animated in those cases if the user agent wants reduced motion.

    As reduced motion is an accessibility setting, I think it's best to use
    a "better safe than sorry" approach, it's better to accidentally mark
    something as animated that isn't than to have unblocked animations.

commit 51ebe643d5
Author: Yuki Joou <yukijoou@laposte.net>
Date:   Wed Aug 2 12:45:18 2023 +0200

    components: Honour 'prefers reduced motion' setting in many components

    This helps accessibility for motion-sensitive people such as myself, and
    can improve battery life in "battery saving" mode on most devices

commit 7c14e1a5bd
Author: Ngô Ngọc Đức Huy <huyngo@disroot.org>
Date:   Wed Aug 2 08:40:19 2023 +0700

    Add replying info for redraft

commit 0da0e2c814
Author: David <dmgf2008@hotmail.com>
Date:   Mon Jul 24 01:08:11 2023 -0700

    Make favicon next to post username use Still-Image functionality

commit af97dd7484
Merge: 28bf5974 2d387e2e
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Jul 21 19:44:25 2023 +0000

    Merge pull request 'docs: Update Pleroma references to Akkoma' (#318) from norm/pleroma-fe:update-docs into develop

    Reviewed-on: #318

commit 28bf597443
Merge: a249baea 14ed359c
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Jul 21 19:43:14 2023 +0000

    Merge pull request '[workaround] fix: emoji picker not scrollable on ios' (#323) from SukinoVerse/akkoma-fe:fix/emoji-picker-not-scrollable-ios into develop

    Reviewed-on: #323

commit a249baea8c
Merge: a4e82f78 32dc55b0
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Jul 21 19:42:41 2023 +0000

    Merge pull request 'Make emoji reactions use Still-Image functionality' (#326) from Mergan/pleroma-fe:still-image-emoji-reactions into develop

    Reviewed-on: #326

commit a4e82f7886
Merge: 42ffce97 91eab22d
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Jul 21 19:37:43 2023 +0000

    Merge pull request '[feat] Support prefers-reduced-motion disabling auto-play of animated images (#324)' (#325) from Mergan/pleroma-fe:support-prefers-reduced-motion into develop

    Reviewed-on: #325

commit 32dc55b07c
Author: David <dmgf2008@hotmail.com>
Date:   Thu Jul 20 17:20:54 2023 -0700

    Make emoji reactions use Still-Image functionality

commit 91eab22d77
Author: David <dmgf2008@hotmail.com>
Date:   Thu Jul 20 17:18:59 2023 -0700

    Simplified and fixed and/or oopsie

commit e2125c57d6
Author: David <dmgf2008@hotmail.com>
Date:   Thu Jul 20 16:26:52 2023 -0700

    Turn on by default

commit e0a6418e91
Author: David <dmgf2008@hotmail.com>
Date:   Thu Jul 20 16:14:36 2023 -0700

    Add prefers-reduced-motion support

commit 14ed359c33
Author: SukinoVERSΞ <github@miraiverse.me>
Date:   Tue Jul 11 22:16:37 2023 +0700

    fix: emoji picker not scrollable on ios

commit 2d387e2eb4
Author: Francis Dinh <normandy@biribiri.dev>
Date:   Fri May 26 23:02:38 2023 -0400

    update more images

commit 57f70371a9
Author: Francis Dinh <normandy@biribiri.dev>
Date:   Fri May 26 22:59:55 2023 -0400

    use better example emoji

commit 8feffbcdf6
Author: Francis Dinh <normandy@biribiri.dev>
Date:   Fri May 26 22:49:29 2023 -0400

    docs: Update Pleroma references to Akkoma

commit e530c2b462
Merge: 9aa64d82 42ffce97
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Tue May 23 14:10:31 2023 +0100

    Merge branch 'develop' into stable

commit 42ffce97d6
Merge: 2f479c67 ee6e7026
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Tue May 23 13:47:14 2023 +0100

    Merge remote-tracking branch 'origin/translations' into dm-privacy

commit 2f479c670f
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Tue May 23 13:46:59 2023 +0100

    Add DM settings

commit ee6e7026ab
Merge: 17c05a5c 42896c2a
Author: Weblate <noreply@weblate.org>
Date:   Tue May 23 11:38:58 2023 +0000

    Merge branch 'origin/develop' into Weblate.

commit 17c05a5ca2
Merge: ecb6be21 6b3b5545
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue May 23 11:38:57 2023 +0000

    Merge pull request 'paper theme: more contrast and fix setting tab hover' (#314) from denys/akkoma-fe:cool-paper-theme into develop

    Reviewed-on: #314

commit 42896c2abf
Merge: ecb6be21 6c92983a
Author: Weblate <noreply@weblate.org>
Date:   Tue May 23 11:38:30 2023 +0000

    Merge branch 'origin/develop' into Weblate.

commit ecb6be2152
Merge: 9e4985e2 8c6ccc32
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue May 23 11:38:28 2023 +0000

    Merge pull request 'fix unfinished post being sent when scrolling' (#312) from denys/akkoma-fe:accidental-mobile-posts into develop

    Reviewed-on: #312

commit 6c92983af6
Merge: 9e4985e2 60ff715a
Author: Weblate <noreply@weblate.org>
Date:   Tue May 23 11:37:26 2023 +0000

    Merge branch 'origin/develop' into Weblate.

commit 9e4985e225
Merge: 596ae7e3 2a76be56
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue May 23 11:37:24 2023 +0000

    Merge pull request 'fix apply theme button without page refresh' (#309) from denys/akkoma-fe:fix-apply-theme into develop

    Reviewed-on: #309

commit 60ff715aff
Author: Weblate <noreply@weblate.org>
Date:   Sun May 21 20:58:06 2023 +0000

    Translated using Weblate (Chinese (Simplified))

    Currently translated at 100.0% (1042 of 1042 strings)

    Co-authored-by: Poesty Li <poesty7450@gmail.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/zh_Hans/
    Translation: Pleroma fe/pleroma-fe

commit 04bcf7d804
Author: Weblate <noreply@weblate.org>
Date:   Sun May 21 20:58:06 2023 +0000

    Translated using Weblate (Polish)

    Currently translated at 66.1% (689 of 1042 strings)

    Co-authored-by: Jeder <jeder@jeder.pl>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/pl/
    Translation: Pleroma fe/pleroma-fe

commit 5fa305c58c
Author: Weblate <noreply@weblate.org>
Date:   Sun May 21 20:58:06 2023 +0000

    Translated using Weblate (Japanese (ja_EASY))

    Currently translated at 72.0% (751 of 1042 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: kazari <6c577a54-aac9-482a-955e-745c858445e3@simplelogin.com>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_EASY/
    Translation: Pleroma fe/pleroma-fe

commit a2ceb89d5e
Author: Weblate <noreply@weblate.org>
Date:   Sun May 21 20:58:06 2023 +0000

    Translated using Weblate (Turkish)

    Currently translated at 4.0% (42 of 1042 strings)

    Added translation using Weblate (Turkish)

    Co-authored-by: Hasan Yıldız <hasanyildiz0@yaani.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/tr/
    Translation: Pleroma fe/pleroma-fe

commit 6b3b55455d
Author: Denys Nykula <vegan@libre.net.ua>
Date:   Thu May 18 23:05:19 2023 +0300

    paper theme: more contrast and fix setting tab hover

commit 8c6ccc321d
Author: Denys Nykula <vegan@libre.net.ua>
Date:   Mon May 15 03:11:07 2023 +0300

    fix unfinished post being sent when scrolling

commit 596ae7e377
Merge: 0d22a22a 94d640f9
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon May 8 15:29:58 2023 +0000

    Merge pull request 'fix dropdown-item-icon and form controls using missing variables' (#307) from denys/akkoma-fe:missing-sass-vars into develop

    Reviewed-on: #307

commit 0d22a22a10
Merge: 1f943ce8 661a98d3
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon May 8 15:28:54 2023 +0000

    Merge pull request 'order bubble after public in sidebar like in other two menus' (#306) from denys/akkoma-fe:consistent-bubble-order into develop

    Reviewed-on: #306

commit 2a76be56e7
Author: Denys Nykula <vegan@libre.net.ua>
Date:   Mon May 1 20:54:18 2023 +0300

    fix apply theme button without page refresh

commit 661a98d38d
Author: Denys Nykula <vegan@libre.net.ua>
Date:   Mon May 1 20:53:29 2023 +0300

    order bubble after public in sidebar like in other two menus

commit 94d640f9f1
Author: Denys Nykula <vegan@libre.net.ua>
Date:   Mon May 1 20:50:31 2023 +0300

    fix dropdown-item-icon and form controls using missing variables

commit 9aa64d82c9
Merge: 85abc622 1f943ce8
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Apr 14 18:10:41 2023 +0100

    Merge branch 'develop' into stable

commit 1f943ce8a5
Merge: c5407644 a4dfdc08
Author: Weblate <noreply@weblate.org>
Date:   Fri Apr 14 16:43:42 2023 +0000

    Merge branch 'origin/develop' into Weblate.

commit c540764408
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Apr 14 17:43:05 2023 +0100

    ensure we only fetch reports when we're an admin

    Ref #288

commit a4dfdc0853
Merge: ddea499a f1bf2243
Author: Weblate <noreply@weblate.org>
Date:   Fri Apr 14 16:30:56 2023 +0000

    Merge branch 'origin/develop' into Weblate.

commit ddea499a36
Merge: 14cedc5e db33fe8e
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Apr 14 16:30:55 2023 +0000

    Merge pull request 'Fix edits and redrafts being erased by drafts' (#297) from solidsanek/pleroma-fe:drafts-edit-redraft-fix into develop

    Reviewed-on: #297

commit db33fe8ee2
Author: solidsanek <solidsanek@outerheaven.club>
Date:   Sun Apr 9 11:02:13 2023 +0200

    Drafts: Fix drafts erasing edits and redrafts

commit f1bf22436d
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (Portuguese)

    Currently translated at 62.8% (655 of 1042 strings)

    Co-authored-by: cel <8cbv6di5@duck.com>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/pt/
    Translation: Pleroma fe/pleroma-fe

commit 459c73ec02
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (Chinese (Simplified))

    Currently translated at 100.0% (1041 of 1041 strings)

    Translated using Weblate (Chinese (Simplified))

    Currently translated at 99.9% (1040 of 1041 strings)

    Translated using Weblate (Chinese (Simplified))

    Currently translated at 99.9% (1040 of 1041 strings)

    Translated using Weblate (Chinese (Simplified))

    Currently translated at 99.9% (1039 of 1040 strings)

    Co-authored-by: Poesty Li <poesty7450@gmail.com>
    Co-authored-by: SevicheCC <sevicheee@outlook.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/zh_Hans/
    Translation: Pleroma fe/pleroma-fe

commit 2acf1e5c59
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (Ukrainian)

    Currently translated at 87.2% (908 of 1041 strings)

    Co-authored-by: Denys Nykula <vegan@libre.net.ua>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/uk/
    Translation: Pleroma fe/pleroma-fe

commit 33c4459744
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (French)

    Currently translated at 98.5% (1027 of 1042 strings)

    Translated using Weblate (French)

    Currently translated at 98.3% (1024 of 1041 strings)

    Translated using Weblate (French)

    Currently translated at 96.9% (1002 of 1033 strings)

    Co-authored-by: Thomate <thomas@burdick.fr>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/fr/
    Translation: Pleroma fe/pleroma-fe

commit b00487e51f
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (Japanese (ja_EASY))

    Currently translated at 71.6% (747 of 1042 strings)

    Translated using Weblate (Japanese (ja_EASY))

    Currently translated at 71.6% (747 of 1042 strings)

    Translated using Weblate (Japanese (ja_EASY))

    Currently translated at 54.1% (564 of 1042 strings)

    Co-authored-by: Hikaru Shinagawa <hikali.47041@gmail.com>
    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: kazari <6c577a54-aac9-482a-955e-745c858445e3@simplelogin.com>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_EASY/
    Translation: Pleroma fe/pleroma-fe

commit 1e1cab643c
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (Dutch)

    Currently translated at 99.7% (1038 of 1041 strings)

    Co-authored-by: Fristi <fristi@subcon.town>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/nl/
    Translation: Pleroma fe/pleroma-fe

commit 8d3219a6d2
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (Indonesian)

    Currently translated at 65.4% (676 of 1033 strings)

    Translated using Weblate (Indonesian)

    Currently translated at 65.4% (676 of 1033 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: t1 <taaa@fedora.email>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/id/
    Translation: Pleroma fe/pleroma-fe

commit ec9753758f
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (Spanish)

    Currently translated at 90.8% (938 of 1033 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: taretka <info@tarteka.net>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/es/
    Translation: Pleroma fe/pleroma-fe

commit 97ff4a7241
Author: Weblate <noreply@weblate.org>
Date:   Wed Apr 5 18:57:45 2023 +0000

    Translated using Weblate (German)

    Currently translated at 99.5% (1036 of 1041 strings)

    Co-authored-by: Johann <johann@qwertqwefsday.eu>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/de/
    Translation: Pleroma fe/pleroma-fe

commit 14cedc5ed1
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Apr 1 07:55:47 2023 +0100

    don't crash if `class` isn't a list

commit 5911777aa2
Merge: 7e1b1e79 47fc082f
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Thu Mar 30 09:49:38 2023 +0000

    Merge pull request 'Fix floating point error for poll expiry' (#294) from xarvos/pleroma-fe:fix-poll-expire into develop

    Reviewed-on: #294

commit 47fc082fb9
Author: Ngô Ngọc Đức Huy <huyngo@disroot.org>
Date:   Fri Mar 24 20:44:29 2023 +0700

    Fix floating point error for poll expiry

    Previous code multiply with 0.001 before multiplication which leads to a
    floating point error.  By changing it to division by 1000 after
    multiplication this is avoided.

commit 7e1b1e79f4
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Mar 18 20:22:47 2023 +0000

    simplify timeline vibility checks

commit b92b2f74a4
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Mar 18 20:01:05 2023 +0000

    add timeline visibility setting parsing

commit 7361f4e77e
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Thu Mar 16 16:41:37 2023 +0000

    Add checks for currentUser on sidebar

commit 9f7f9e2798
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Wed Mar 15 23:00:31 2023 +0000

    Remove unused bits and bobs

commit 42ab3eada4
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Wed Mar 15 22:20:54 2023 +0000

    Remove links from navs if we can't see the timeline

commit 85abc62213
Merge: 8569b594 6fdef479
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Mar 11 17:27:22 2023 +0000

    Merge branch 'develop' into stable

commit 6fdef479d0
Author: flisk <akkomadev.mvch71fq@flisk.xyz>
Date:   Fri Mar 10 19:10:42 2023 +0000

    add recently used emojis panel to emoji picker (#283)

    ~~(not intended for merging yet, just submitting this for preliminary review and discussion)~~

    this patch adds a tab with recently used emojis to the emoji picker: https://akko.lain.gay/notice/ASoGCtyoiXbYPJjqpk

    there's a couple of things i'm ~~still trying to work out~~ not totally happy with and i'd appreciate any feedback on them:

    * the recentEmojis getter is called very frequently and has to do a possibly somewhat expensive lookup of emoji objects by their `displayName` each time, which i'm not sure is ideal
    * ~~emoji reactions on posts added through the picker are picked up by the recentEmojis module, but clicks on existing emoji reactions are not, because `addReaction` in `react_button.js` only currently receives the replacement and not the full emoji object (if there even is one wherever that method is called from)~~ this works now and does the same stupid full search of all emojis by their name which i guess is less bad because this only happens when you hit a reaction emoji button that already existed

    Reviewed-on: #283
    Co-authored-by: flisk <akkomadev.mvch71fq@flisk.xyz>
    Co-committed-by: flisk <akkomadev.mvch71fq@flisk.xyz>

commit fe08691f05
Merge: 6a976495 626c8800
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Mar 10 18:57:38 2023 +0000

    Merge pull request 'support Misskey's oblong reactions' (#284) from yheuhtozr/pleroma-fe:oblong-reactions into develop

    Reviewed-on: #284

commit 6a9764951f
Merge: 0f33b1cd 999c3859
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Mar 10 18:56:31 2023 +0000

    Merge pull request 'fix realtime updates in 'following' replies filter' (#285) from flisk/akkoma-fe:fix-realtime-reply-filter into develop

    Reviewed-on: #285

commit 0f33b1cd79
Merge: 6a9d169e 2c007f06
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Mar 10 18:55:03 2023 +0000

    Merge pull request 'Post drafting' (#282) from solidsanek/pleroma-fe:drafts into develop

    Reviewed-on: #282

commit 999c38594e
Author: flisk <akkomadev.mvch71fq@flisk.xyz>
Date:   Fri Feb 24 00:23:53 2023 +0100

    fix realtime updates in 'following' replies filter

    i'm not sure how this code was supposed to work, but the way it was
    written would only add statuses to the timeline if they were in reply to
    someone the user is following and erroneously filter out posts that
    aren't replies.

commit 626c880038
Author: Yhëhtozr <conlang2012@outlook.com>
Date:   Wed Feb 22 10:20:25 2023 +0900

    oblong emoji in status

commit 6d7761c7e5
Author: Yhëhtozr <conlang2012@outlook.com>
Date:   Mon Feb 20 23:27:41 2023 +0900

    perhaps more graceful cqw

commit 996ce3dde3
Author: Yhëhtozr <conlang2012@outlook.com>
Date:   Mon Feb 20 23:18:04 2023 +0900

    support oblong reactions

commit 2c007f06e3
Author: solidsanek <solidsanek@outerheaven.club>
Date:   Sun Feb 19 18:58:53 2023 +0100

    Post: remove debug logs

commit 00704bd88c
Author: solidsanek <solidsanek@outerheaven.club>
Date:   Fri Feb 17 13:56:01 2023 +0100

    Post: Add drafting feature

commit 8569b5946e
Merge: 9c9b4cc0 6a9d169e
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Feb 11 10:50:04 2023 +0000

    Merge branch 'develop' into stable

commit 6a9d169e24
Merge: 9e04e4fd 581c53a1
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Sat Feb 11 10:41:00 2023 +0000

    Merge pull request 'components: emoji_reactions: force custom emoji reaction height' (#280) from a1batross/akkoma-fe:a1batross-patch-1 into develop

    Reviewed-on: #280

commit 581c53a15e
Author: a1batross <a1ba.omarov@gmail.com>
Date:   Fri Feb 10 23:28:46 2023 +0000

    components: emoji_reactions: force custom emoji reaction height

    Prevents the usage of too long emoji reactions

commit 9e04e4fd80
Author: yanchan09 <yan@omg.lol>
Date:   Sat Feb 4 21:10:06 2023 +0000

    Improve emoji picker performance (#275)

    A simple virtual scroller is now used for the emoji grid. This avoids loading all emoji images at once, saving network bandwidth and reducing load on the server, while also putting less work on the browser's DOM and layout engine.

    Co-authored-by: yan <yan@omg.lol>
    Reviewed-on: #275
    Co-authored-by: yanchan09 <yan@omg.lol>
    Co-committed-by: yanchan09 <yan@omg.lol>

commit 88d5149db5
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Sat Feb 4 21:09:09 2023 +0000

    paginate-follow-requests (#277)

    Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
    Reviewed-on: #277

commit b4b13d777f
Merge: 56fd2e77 7f4dd9ff
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Jan 27 10:08:17 2023 +0000

    Merge pull request 'Add indicator to user card if user blocks you' (#274) from eris/pleroma-fe:block-indicator into develop

    Reviewed-on: #274

commit 7f4dd9ff03
Author: eris <femmediscord@gmail.com>
Date:   Fri Jan 27 00:26:50 2023 +0000

    Disable follow button if blocked by user

commit a9a95e9120
Author: eris <femmediscord@gmail.com>
Date:   Thu Jan 26 20:49:07 2023 +0000

    Add indicator if user blocks you

commit 56fd2e773b
Merge: 236bc2c7 42dc1a02
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Jan 15 17:59:50 2023 +0000

    Merge branch 'languages' into develop

commit 42dc1a027a
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Jan 15 17:59:32 2023 +0000

    add language input

commit 236bc2c762
Merge: f288d0c2 e9f47509
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Jan 9 22:20:31 2023 +0000

    Merge pull request 'Only show "keep open" emoji checkbox on post form' (#269) from sfr/pleroma-fe:fix/keepopen into develop

    Reviewed-on: #269

commit e9f47509ae
Author: Sol Fisher Romanoff <sol@solfisher.com>
Date:   Tue Jan 3 16:04:26 2023 +0200

    Only show "keep open" emoji checkbox on post form

commit f288d0c219
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Mon Jan 2 15:16:42 2023 +0000

    Make everything work with a strict CSP

commit d973396c96
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Jan 1 21:06:02 2023 +0000

    Remove console.log

commit 62287fffae
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Jan 1 21:05:25 2023 +0000

    add follow/unfollow to followed tags list

commit e9f16af82d
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Jan 1 20:11:07 2023 +0000

    Add list of followed hashtags to profile

commit dfba8be134
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 30 05:03:25 2022 +0000

    Fall back to nsfw image if no blurhash

commit 313ddcebcb
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 30 04:57:23 2022 +0000

    Add blurhash support

commit 236b19e854
Merge: ea941d7c 2e5001e5
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 30 03:20:30 2022 +0000

    Merge branch 'develop' of akkoma.dev:AkkomaGang/akkoma-fe into develop

commit ea941d7cfa
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 30 03:20:12 2022 +0000

    remove IHBA assets

commit 2e5001e5de
Author: Beefox <bee@beefox.xyz>
Date:   Fri Dec 30 03:04:15 2022 +0000

    Allow follow(er) lists to be acessible by account owner even if follower counts are disabled (#246)

    Currently, if a user has their follower/follow counts hidden, they cannot access their own list of followers/follows. This makes no real sense and means that they cannot modify those lists without disabling their privacy options.

    This fix simply allows those tabs to be accessed no matter if the counts are hidden or not.

    Reviewed-on: #246
    Co-authored-by: Beefox <bee@beefox.xyz>
    Co-committed-by: Beefox <bee@beefox.xyz>

commit 014f8b0dd2
Author: Norm <normandy@biribiri.dev>
Date:   Fri Dec 30 03:01:17 2022 +0000

    Make minimum width for 3-column layout 1280px (#255) (#256)

    1280px is a pretty common screen width for several resolutions
    (1280x720, 1280x800, 1280x1024, etc.). Since it is only 20px less than
    the current 1300px minimum, this shouldn't be a big issue to lower the
    minimum screen width for the 3-column layout to 1280px.

    Closes: AkkomaGang/pleroma-fe#255

    Co-authored-by: Francis Dinh <normandy@biribiri.dev>
    Reviewed-on: #256
    Co-authored-by: Norm <normandy@biribiri.dev>
    Co-committed-by: Norm <normandy@biribiri.dev>

commit dd403b295f
Merge: f668455d 9cd62fe0
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 30 03:00:49 2022 +0000

    Merge pull request 'Remove stray debug log' (#265) from sfr/pleroma-fe:del-log into develop

    Reviewed-on: #265

commit 9cd62fe08d
Author: Sol Fisher Romanoff <sol@solfisher.com>
Date:   Fri Dec 30 01:03:31 2022 +0200

    Remove stray debug log

commit f668455dff
Merge: 5a431538 da491f32
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Thu Dec 29 20:56:22 2022 +0000

    Merge branch 'link-verification' into develop

commit 5a4315384e
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Thu Dec 29 15:25:03 2022 +0000

    force CI build

commit 401dfa8fa6
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Thu Dec 29 15:22:06 2022 +0000

    update readme

commit bb243168b3
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Thu Dec 29 15:18:13 2022 +0000

    Revert "Merge pull request 'Don't show timeline links if disabled and logged out' (#250) from sfr/pleroma-fe:fix/hide-timelines into develop"

    This reverts commit 0b5793c1e0, reversing
    changes made to 72ef2e7454.

commit da491f3278
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Thu Dec 29 15:17:35 2022 +0000

    add verification of links

commit d00e28d5e9
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Thu Dec 22 05:43:01 2022 +0000

    fix emoji picker in replies in notifications

commit 7ff17ab722
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Tue Dec 20 13:20:13 2022 +0000

    don't crash out if notification status is null

commit b009428814
Merge: 7bec96a1 c39332c1
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 14 12:09:03 2022 +0000

    Merge pull request 'Revert "Revert "use v1 urls""' (#254) from v1-urls into develop

    Reviewed-on: AkkomaGang/pleroma-fe#254

commit 7bec96a1bf
Merge: 0b5793c1 830e8fdb
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 14 12:08:27 2022 +0000

    Merge pull request 'Fix user moderation dropdown clipping' (#249) from sfr/pleroma-fe:fix/dropdown into develop

    Reviewed-on: AkkomaGang/pleroma-fe#249

commit 0b5793c1e0
Merge: 72ef2e74 6e1ba218
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 14 12:08:08 2022 +0000

    Merge pull request 'Don't show timeline links if disabled and logged out' (#250) from sfr/pleroma-fe:fix/hide-timelines into develop

    Reviewed-on: AkkomaGang/pleroma-fe#250

commit 72ef2e7454
Merge: 8c6cf86d 413acbc7
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 14 12:07:27 2022 +0000

    Merge pull request 'Fix 404 when reacting with Keycap Number Sign' (#252) from fef/pleroma-fe:develop into develop

    Reviewed-on: AkkomaGang/pleroma-fe#252

commit c39332c1bf
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 14 09:39:01 2022 +0000

    Revert "Revert "use v1 urls""

    This reverts commit 8c6cf86de3.

commit 8c6cf86de3
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 14 09:38:46 2022 +0000

    Revert "use v1 urls"

    This reverts commit 909271c764.

commit 909271c764
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 14 09:38:07 2022 +0000

    use v1 urls

commit 413acbc7dd
Author: fef <owo@fef.moe>
Date:   Mon Dec 12 18:59:57 2022 +0100

    fix 404 when reacting with Keycap Number Sign

    The Unicode sequence for the Keycap Number Sign
    emoji starts with an ASCII "#" character, which
    the browser's URL parser will interpret as a URI
    fragment and truncate it before sending the
    request to the backend.

commit 6e1ba218df
Author: Sol Fisher Romanoff <sol@solfisher.com>
Date:   Sat Dec 10 21:17:41 2022 +0200

    Don't show timeline links if disabled and logged out

commit 830e8fdb45
Author: Sol Fisher Romanoff <sol@solfisher.com>
Date:   Sat Dec 10 21:03:12 2022 +0200

    Fix user moderation dropdown clipping

commit 9c9b4cc07c
Merge: 2c9b7364 9bf310d5
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Dec 10 14:52:00 2022 +0000

    Merge branch 'develop' into stable

commit 9bf310d509
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Dec 10 14:51:08 2022 +0000

    bump version

commit e3e8b19df3
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sat Dec 10 00:17:33 2022 +0000

    fix ES translation having weird o in a key

commit e86c7abb39
Merge: 8a0da886 b33d15a7
Author: Weblate <noreply@weblate.org>
Date:   Thu Dec 8 18:41:37 2022 +0000

    Merge branch 'origin/develop' into Weblate.

commit 8a0da8861d
Merge: 40e86998 6c7e691a
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Thu Dec 8 18:41:36 2022 +0000

    Merge pull request 'Add YAML bug and feat templates' (#247) from sfr/pleroma-fe:issue-template into develop

    Reviewed-on: AkkomaGang/pleroma-fe#247

commit 6c7e691aea
Author: Sol Fisher Romanoff <sol@solfisher.com>
Date:   Thu Dec 8 20:24:21 2022 +0200

    Add YAML bug and feat templates

commit b33d15a739
Merge: 40e86998 177f3440
Author: Weblate <noreply@weblate.org>
Date:   Wed Dec 7 22:37:54 2022 +0000

    Merge branch 'origin/develop' into Weblate.

commit 40e86998e6
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 7 22:37:52 2022 +0000

    Update 'ISSUE_TEMPLATE.md'

commit 177f344033
Merge: 9079ac4a dfc4e0a0
Author: Weblate <noreply@weblate.org>
Date:   Wed Dec 7 22:32:07 2022 +0000

    Merge branch 'origin/develop' into Weblate.

commit 9079ac4afa
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 7 22:31:49 2022 +0000

    Update 'ISSUE_TEMPLATE.md'

commit dfc4e0a026
Author: Weblate <noreply@weblate.org>
Date:   Wed Dec 7 22:31:39 2022 +0000

    Translated using Weblate (Japanese (ja_PEDANTIC))

    Currently translated at 99.8% (1031 of 1033 strings)

    Co-authored-by: Weblate Admin <hannah.ward9001@gmail.com>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_PEDANTIC/
    Translation: Pleroma fe/pleroma-fe

commit 3d732d1d28
Author: Weblate <noreply@weblate.org>
Date:   Wed Dec 7 22:31:39 2022 +0000

    Translated using Weblate (Indonesian)

    Currently translated at 59.4% (614 of 1033 strings)

    Translated using Weblate (Indonesian)

    Currently translated at 53.0% (548 of 1033 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: t1 <taaa@fedora.email>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/id/
    Translation: Pleroma fe/pleroma-fe

commit e8ee31afed
Author: Weblate <noreply@weblate.org>
Date:   Wed Dec 7 22:31:39 2022 +0000

    Translated using Weblate (English)

    Currently translated at 100.0% (1033 of 1033 strings)

    Co-authored-by: Weblate Admin <hannah.ward9001@gmail.com>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/en/
    Translation: Pleroma fe/pleroma-fe

commit d9d6b1e80b
Author: Weblate <noreply@weblate.org>
Date:   Wed Dec 7 22:31:39 2022 +0000

    Translated using Weblate (Spanish)

    Currently translated at 89.2% (918 of 1029 strings)

    Co-authored-by: Weblate <noreply@weblate.org>
    Co-authored-by: taretka <info@tarteka.net>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/es/
    Translation: Pleroma fe/pleroma-fe

commit 1dd7a89544
Author: Weblate <noreply@weblate.org>
Date:   Wed Dec 7 22:31:38 2022 +0000

    Translated using Weblate (German)

    Currently translated at 93.9% (967 of 1029 strings)

    Co-authored-by: Johann <johann@qwertqwefsday.eu>
    Co-authored-by: Weblate <noreply@weblate.org>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/de/
    Translation: Pleroma fe/pleroma-fe

commit d3280c4ab3
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 7 22:31:29 2022 +0000

    Add issue template

commit abc75c360b
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Wed Dec 7 11:01:58 2022 +0000

    Ensure only content gets clipped

commit a8e119b0f1
Merge: 17e574b1 71d2e0b0
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Tue Dec 6 15:56:06 2022 +0000

    Merge branch 'develop' of akkoma.dev:AkkomaGang/pleroma-fe into develop

commit 17e574b173
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Tue Dec 6 15:55:39 2022 +0000

    Move theme apply/reset to new row

    Fixes #225

commit 71d2e0b0ce
Merge: b68e968b 337a30fe
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue Dec 6 15:32:40 2022 +0000

    Merge pull request 'fix scope selector icon spacing' (#243) from nocebo/crt-fe:shared/fix-scope-spacing into develop

    Reviewed-on: AkkomaGang/pleroma-fe#243

commit b68e968bf9
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Tue Dec 6 15:26:16 2022 +0000

    Add ability to include custom CSS

commit eb49295422
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Sun Dec 4 17:31:41 2022 +0000

    Add hashtag following button (#244)

    Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
    Reviewed-on: AkkomaGang/pleroma-fe#244

commit 337a30fe01
Author: astra akari <nocebo@noreply.akkoma>
Date:   Sat Dec 3 07:12:55 2022 +0000

    remove whitespace between scope selector icons

    when i originally wrote this, for reasons unclear to the present me, i used literal whitespaces to space out the icons on the scope selector
    this causes strange inconsistencies in spacing depending on the font being used
    akkoma also did not include the whitespace when adding the local-only scope, resulting in even weirder spacing
    this corrects all of that by removing the whitespaces and using css instead

commit 105ecd3836
Merge: b86f12ce a3e490ed
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 2 12:01:43 2022 +0000

    Merge pull request 'pwa config' (#242) from pwa into develop

    Reviewed-on: AkkomaGang/pleroma-fe#242

commit a3e490edcd
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 2 12:00:30 2022 +0000

    use cutout of 512 logo

commit f8f5e1c89b
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 2 11:57:45 2022 +0000

    fix SW path

commit e132814478
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 2 11:56:15 2022 +0000

    Register serviceworker

commit 6af1df8bef
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 2 11:27:24 2022 +0000

    Add logo files

commit b86f12cede
Merge: 0900a9d8 c6697017
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Fri Dec 2 10:21:32 2022 +0000

    Merge pull request 'Add a small margin to search bar' (#240) from karl/pleroma-fe:search-bar-margin into develop

    Reviewed-on: AkkomaGang/pleroma-fe#240

commit c669701762
Author: Karl Prieb <karl@prieb.me>
Date:   Tue Nov 29 18:04:33 2022 -0300

    add a left margin on search bar

commit 0900a9d87b
Merge: 7860c885 0a01a2bd
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Nov 28 13:35:08 2022 +0000

    Merge pull request 'Add post expiry inputs' (#239) from default-post-expiry into develop

    Reviewed-on: AkkomaGang/pleroma-fe#239

commit 0a01a2bdf0
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Mon Nov 28 12:08:18 2022 +0000

    Add post expiry inputs

commit 7860c885c4
Author: darkkirb <lotte@chir.rs>
Date:   Sat Nov 26 20:57:04 2022 +0000

    Add link to RSS feed to the profile (#234)

    Today I learned that akkoma and mastodon (and potentially other activitypub services) offer RSS/Atom feeds for user profiles at `[user profile url].rss`. This PR adds a direct link to the feed because I haven’t seen anything link to the feed on either mastodon-fe or pleroma-fe

    Co-authored-by: Charlotte 🦝 Delenk <lotte@chir.rs>
    Reviewed-on: AkkomaGang/pleroma-fe#234
    Co-authored-by: darkkirb <lotte@chir.rs>
    Co-committed-by: darkkirb <lotte@chir.rs>

commit 1c3bd60af2
Merge: c01c62f1 b8faee5d
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Sat Nov 26 18:32:35 2022 +0000

    Merge pull request 'fix formatting for large number of favorites/interactions' (#236) from drudge/pleroma-fe:develop into develop

    Reviewed-on: AkkomaGang/pleroma-fe#236

commit b8faee5d6d
Author: Sean Meininger <sean.meininger@pm.me>
Date:   Sat Nov 26 00:15:12 2022 -0800

    added maintainer code

commit c01c62f149
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue Nov 22 14:44:44 2022 +0000

    Redirect users to "awaiting approval" message on register (#231)

    Ref #81 - this implements a "your request has been sent" message

     ![image](/attachments/61dc3f5e-2706-46f9-a1ca-4efe3f526ff3)

    Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
    Reviewed-on: AkkomaGang/pleroma-fe#231

commit 105b934f90
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Tue Nov 22 14:40:25 2022 +0000

    Only reload user if it _is_ a user (#232)

    Ref #181

    Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
    Reviewed-on: AkkomaGang/pleroma-fe#232

commit b1f41add0e
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Mon Nov 21 17:08:53 2022 +0000

    Don't error out if we can't fetch reports

commit e4e8ed812b
Merge: 684894ae e06348ee
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Nov 21 16:51:30 2022 +0000

    Merge pull request 'Allow using mouse wheel to navigate through the emoji tabs (#179)' (#222) from Mergan/pleroma-fe:emoji-picker-allow-scroll into develop

    Reviewed-on: AkkomaGang/pleroma-fe#222

commit 684894aee3
Author: Beefox <bee@beefox.xyz>
Date:   Mon Nov 21 16:49:18 2022 +0000

    mobile-newline (#226)

    Allows the handle of users to drop down onto the next line if there isn't enough room in order to improve useability on mobile

    Reviewed-on: AkkomaGang/pleroma-fe#226
    Co-authored-by: Beefox <bee@beefox.xyz>
    Co-committed-by: Beefox <bee@beefox.xyz>

commit f8a796b234
Merge: 70ea9e77 862c9370
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Nov 21 16:46:29 2022 +0000

    Merge pull request 'move domain block to drop down menu (#223)' (#224) from nocebo/crt-fe:shared/move-domain-mute into develop

    Reviewed-on: AkkomaGang/pleroma-fe#224

commit 70ea9e772c
Merge: efe0f537 fcbbbad8
Author: floatingghost <hannah@coffee-and-dreams.uk>
Date:   Mon Nov 21 10:11:19 2022 +0000

    Merge pull request 'Allow for searching unicode emoji via inputting emoji (#163 & #227)' (#230) from Mergan/pleroma-fe:beefox-emoji-search into develop

    Reviewed-on: AkkomaGang/pleroma-fe#230

commit efe0f53736
Author: Mergan <mergan@noreply.akkoma>
Date:   Mon Nov 21 10:10:50 2022 +0000

    Constrain content to status content (#218) (#220)

    MFM No longer overflows
    ![image](/attachments/7bbf519a-9fd2-492d-aba0-9a0e1ded6373)

    Co-authored-by: David <dmgf2008@hotmail.com>
    Reviewed-on: AkkomaGang/pleroma-fe#220
    Co-authored-by: Mergan <mergan@noreply.akkoma>
    Co-committed-by: Mergan <mergan@noreply.akkoma>

commit fcbbbad8d4
Author: Beefox <bee@beefox.xyz>
Date:   Sat Sep 10 16:36:12 2022 +1000

    Allow for searching unicode emoji via inputting emoji
    (This is needed for the react menu)

commit 39b6b0b49f
Merge: 867a86d8 2d4b2f2e
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Nov 20 22:26:32 2022 +0000

    Merge remote-tracking branch 'origin/translations' into develop

commit 867a86d887
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Nov 20 22:26:16 2022 +0000

    Fix build

commit 7538369fa1
Author: FloatingGhost <hannah@coffee-and-dreams.uk>
Date:   Sun Nov 20 22:18:34 2022 +0000

    Copy conversation display style from instance

    Fixes #210

commit 2d4b2f2e20
Author: Weblate <noreply@weblate.org>
Date:   Sat Nov 19 21:22:45 2022 +0000

    Translated using Weblate (Spanish)

    Currently translated at 87.1% (893 of 1025 strings)

    Co-authored-by: taretka <info@tarteka.net>
    Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/es/
    Translation: Pleroma fe/pleroma-fe

commit 862c93706c
Author: astra <nocebo@cathoderay.tube>
Date:   Fri Nov 18 08:49:57 2022 -0500

    move domain block to drop down menu

commit e06348ee33
Author: David <dmgf2008@hotmail.com>
Date:   Thu Nov 17 14:45:32 2022 -0800

    Allow using mouse wheel to navigate through the emoji tabs
2024-05-16 00:35:53 +02:00
hynet-mel
4f4aa6f75e Bruh I forgot the custom.css elements 2023-05-09 19:41:36 +02:00
Weblate
de7561ddde Translated using Weblate (Portuguese)
Currently translated at 62.8% (655 of 1042 strings)

Co-authored-by: cel <8cbv6di5@duck.com>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/pt/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
4f26a90106 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1041 of 1041 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.9% (1040 of 1041 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.9% (1040 of 1041 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.9% (1039 of 1040 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Co-authored-by: SevicheCC <sevicheee@outlook.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/zh_Hans/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
3640526ab7 Translated using Weblate (Ukrainian)
Currently translated at 87.2% (908 of 1041 strings)

Co-authored-by: Denys Nykula <vegan@libre.net.ua>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/uk/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
191589283f Translated using Weblate (French)
Currently translated at 98.5% (1027 of 1042 strings)

Translated using Weblate (French)

Currently translated at 98.3% (1024 of 1041 strings)

Translated using Weblate (French)

Currently translated at 96.9% (1002 of 1033 strings)

Co-authored-by: Thomate <thomas@burdick.fr>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/fr/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
7c59aac3d5 Translated using Weblate (Japanese (ja_EASY))
Currently translated at 71.6% (747 of 1042 strings)

Translated using Weblate (Japanese (ja_EASY))

Currently translated at 71.6% (747 of 1042 strings)

Translated using Weblate (Japanese (ja_EASY))

Currently translated at 54.1% (564 of 1042 strings)

Co-authored-by: Hikaru Shinagawa <hikali.47041@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: kazari <6c577a54-aac9-482a-955e-745c858445e3@simplelogin.com>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_EASY/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
5eb4e8a58b Translated using Weblate (Dutch)
Currently translated at 99.7% (1038 of 1041 strings)

Co-authored-by: Fristi <fristi@subcon.town>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/nl/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
8ed3826ee4 Translated using Weblate (Indonesian)
Currently translated at 65.4% (676 of 1033 strings)

Translated using Weblate (Indonesian)

Currently translated at 65.4% (676 of 1033 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: t1 <taaa@fedora.email>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/id/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
b333542a77 Translated using Weblate (Spanish)
Currently translated at 90.8% (938 of 1033 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: taretka <info@tarteka.net>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/es/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
Weblate
10163831ac Translated using Weblate (German)
Currently translated at 99.5% (1036 of 1041 strings)

Co-authored-by: Johann <johann@qwertqwefsday.eu>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/de/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:11:12 +02:00
25c205fe4c ensure we only fetch reports when we're an admin
Ref #288
2023-05-09 19:11:12 +02:00
solidsanek
9056321750 Drafts: Fix drafts erasing edits and redrafts 2023-05-09 19:11:12 +02:00
e5b226799d don't crash if class isn't a list 2023-05-09 19:11:12 +02:00
86d2553acf Fix floating point error for poll expiry
Previous code multiply with 0.001 before multiplication which leads to a
floating point error.  By changing it to division by 1000 after
multiplication this is avoided.
2023-05-09 19:11:12 +02:00
c3208db28d simplify timeline vibility checks 2023-05-09 19:11:12 +02:00
b552d8116a add timeline visibility setting parsing 2023-05-09 19:11:12 +02:00
0f4cdcda48 Add checks for currentUser on sidebar 2023-05-09 19:11:12 +02:00
6b4680bf37 Remove unused bits and bobs 2023-05-09 19:11:12 +02:00
3a8354abb4 Remove links from navs if we can't see the timeline 2023-05-09 19:11:12 +02:00
1e66217c16 add recently used emojis panel to emoji picker (#283)
~~(not intended for merging yet, just submitting this for preliminary review and discussion)~~

this patch adds a tab with recently used emojis to the emoji picker: https://akko.lain.gay/notice/ASoGCtyoiXbYPJjqpk

there's a couple of things i'm ~~still trying to work out~~ not totally happy with and i'd appreciate any feedback on them:

* the recentEmojis getter is called very frequently and has to do a possibly somewhat expensive lookup of emoji objects by their `displayName` each time, which i'm not sure is ideal
* ~~emoji reactions on posts added through the picker are picked up by the recentEmojis module, but clicks on existing emoji reactions are not, because `addReaction` in `react_button.js` only currently receives the replacement and not the full emoji object (if there even is one wherever that method is called from)~~ this works now and does the same stupid full search of all emojis by their name which i guess is less bad because this only happens when you hit a reaction emoji button that already existed

Reviewed-on: #283
Co-authored-by: flisk <akkomadev.mvch71fq@flisk.xyz>
Co-committed-by: flisk <akkomadev.mvch71fq@flisk.xyz>
2023-05-09 19:11:12 +02:00
9d450953b6 oblong emoji in status 2023-05-09 19:10:45 +02:00
e8e4b4fc96 perhaps more graceful cqw 2023-05-09 19:10:45 +02:00
417f7a3ac1 support oblong reactions 2023-05-09 19:10:45 +02:00
1b038d3fdc fix realtime updates in 'following' replies filter
i'm not sure how this code was supposed to work, but the way it was
written would only add statuses to the timeline if they were in reply to
someone the user is following and erroneously filter out posts that
aren't replies.
2023-05-09 19:10:45 +02:00
solidsanek
93e1b750cf Post: remove debug logs 2023-05-09 19:10:45 +02:00
solidsanek
7463cbca53 Post: Add drafting feature 2023-05-09 19:10:45 +02:00
f7ea95cd0e components: emoji_reactions: force custom emoji reaction height
Prevents the usage of too long emoji reactions
2023-05-09 19:10:45 +02:00
3dbbc2519f Improve emoji picker performance (#275)
A simple virtual scroller is now used for the emoji grid. This avoids loading all emoji images at once, saving network bandwidth and reducing load on the server, while also putting less work on the browser's DOM and layout engine.

Co-authored-by: yan <yan@omg.lol>
Reviewed-on: #275
Co-authored-by: yanchan09 <yan@omg.lol>
Co-committed-by: yanchan09 <yan@omg.lol>
2023-05-09 19:10:45 +02:00
5164f595bd paginate-follow-requests (#277)
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: #277
2023-05-09 19:10:45 +02:00
5f19c25fa5 Disable follow button if blocked by user 2023-05-09 19:10:45 +02:00
74026fc562 Add indicator if user blocks you 2023-05-09 19:10:45 +02:00
f7301dab5a add language input 2023-05-09 19:10:45 +02:00
Sol Fisher Romanoff
dca56dd40f Only show "keep open" emoji checkbox on post form 2023-05-09 19:10:45 +02:00
5bbac1fa61 Make everything work with a strict CSP 2023-05-09 19:10:45 +02:00
48700ea8d5 Remove console.log 2023-05-09 19:10:45 +02:00
e095e22f2c add follow/unfollow to followed tags list 2023-05-09 19:10:45 +02:00
79d506e331 Add list of followed hashtags to profile 2023-05-09 19:10:45 +02:00
0e71597e56 Fall back to nsfw image if no blurhash 2023-05-09 19:10:45 +02:00
e834656dc6 Add blurhash support 2023-05-09 19:10:45 +02:00
7ac8c9122c Allow follow(er) lists to be acessible by account owner even if follower counts are disabled (#246)
Currently, if a user has their follower/follow counts hidden, they cannot access their own list of followers/follows. This makes no real sense and means that they cannot modify those lists without disabling their privacy options.

This fix simply allows those tabs to be accessed no matter if the counts are hidden or not.

Reviewed-on: #246
Co-authored-by: Beefox <bee@beefox.xyz>
Co-committed-by: Beefox <bee@beefox.xyz>
2023-05-09 19:10:45 +02:00
c9a76f6224 Make minimum width for 3-column layout 1280px (#255) (#256)
1280px is a pretty common screen width for several resolutions
(1280x720, 1280x800, 1280x1024, etc.). Since it is only 20px less than
the current 1300px minimum, this shouldn't be a big issue to lower the
minimum screen width for the 3-column layout to 1280px.

Closes: AkkomaGang/pleroma-fe#255

Co-authored-by: Francis Dinh <normandy@biribiri.dev>
Reviewed-on: #256
Co-authored-by: Norm <normandy@biribiri.dev>
Co-committed-by: Norm <normandy@biribiri.dev>
2023-05-09 19:10:45 +02:00
Sol Fisher Romanoff
ca775554d3 Remove stray debug log 2023-05-09 19:10:45 +02:00
d16182547a remove IHBA assets 2023-05-09 19:10:45 +02:00
d9fa3d8251 add verification of links 2023-05-09 19:10:31 +02:00
47b7017256 force CI build 2023-05-09 19:09:52 +02:00
d2ac971f7f update readme 2023-05-09 19:09:52 +02:00
4b45a96c77 Revert "Merge pull request 'Don't show timeline links if disabled and logged out' (#250) from sfr/pleroma-fe:fix/hide-timelines into develop"
This reverts commit 0b5793c1e0, reversing
changes made to 72ef2e7454.
2023-05-09 19:09:38 +02:00
af3d4f8065 fix emoji picker in replies in notifications 2023-05-09 19:09:38 +02:00
ee2568d21a don't crash out if notification status is null 2023-05-09 19:09:38 +02:00
109bb1fa8a Revert "Revert "use v1 urls""
This reverts commit 8c6cf86de3.
2023-05-09 19:09:38 +02:00
Sol Fisher Romanoff
9dc8c6ea9f Fix user moderation dropdown clipping 2023-05-09 19:09:38 +02:00
Sol Fisher Romanoff
b7411c85f4 Don't show timeline links if disabled and logged out 2023-05-09 19:09:38 +02:00
fef
8c664a1396 fix 404 when reacting with Keycap Number Sign
The Unicode sequence for the Keycap Number Sign
emoji starts with an ASCII "#" character, which
the browser's URL parser will interpret as a URI
fragment and truncate it before sending the
request to the backend.
2023-05-09 19:09:38 +02:00
c53faaf284 Revert "use v1 urls"
This reverts commit 909271c764.
2023-05-09 19:09:38 +02:00
29865caf9a use v1 urls 2023-05-09 19:09:38 +02:00
ae75bb598b bump version 2023-05-09 19:09:38 +02:00
03e2912598 fix ES translation having weird o in a key 2023-05-09 19:09:38 +02:00
Weblate
d6ab4f85d3 Translated using Weblate (Japanese (ja_PEDANTIC))
Currently translated at 99.8% (1031 of 1033 strings)

Co-authored-by: Weblate Admin <hannah.ward9001@gmail.com>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/ja_PEDANTIC/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:09:38 +02:00
Weblate
59a09bb60d Translated using Weblate (Indonesian)
Currently translated at 59.4% (614 of 1033 strings)

Translated using Weblate (Indonesian)

Currently translated at 53.0% (548 of 1033 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: t1 <taaa@fedora.email>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/id/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:09:38 +02:00
Weblate
0d81a1fcb3 Translated using Weblate (English)
Currently translated at 100.0% (1033 of 1033 strings)

Co-authored-by: Weblate Admin <hannah.ward9001@gmail.com>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/en/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:09:38 +02:00
Weblate
1bc98ce722 Translated using Weblate (Spanish)
Currently translated at 89.2% (918 of 1029 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: taretka <info@tarteka.net>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/es/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:08:31 +02:00
Weblate
7ba8c31adf Translated using Weblate (German)
Currently translated at 93.9% (967 of 1029 strings)

Co-authored-by: Johann <johann@qwertqwefsday.eu>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/de/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:08:31 +02:00
Sol Fisher Romanoff
24bc2c29ad Add YAML bug and feat templates 2023-05-09 19:08:31 +02:00
a339e03a8a Update 'ISSUE_TEMPLATE.md' 2023-05-09 19:08:31 +02:00
77e67f40ff Update 'ISSUE_TEMPLATE.md' 2023-05-09 19:08:31 +02:00
28a4c70516 Add issue template 2023-05-09 19:08:31 +02:00
3cdb990ea2 Ensure only content gets clipped 2023-05-09 19:08:31 +02:00
be4b1a8a5a remove whitespace between scope selector icons
when i originally wrote this, for reasons unclear to the present me, i used literal whitespaces to space out the icons on the scope selector 
this causes strange inconsistencies in spacing depending on the font being used 
akkoma also did not include the whitespace when adding the local-only scope, resulting in even weirder spacing 
this corrects all of that by removing the whitespaces and using css instead
2023-05-09 19:08:31 +02:00
19681d0c90 Move theme apply/reset to new row
Fixes #225
2023-05-09 19:08:31 +02:00
6b9c787bb8 Add ability to include custom CSS 2023-05-09 19:08:31 +02:00
11d374a3a1 Add hashtag following button (#244)
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: AkkomaGang/pleroma-fe#244
2023-05-09 19:08:31 +02:00
1f875844aa use cutout of 512 logo 2023-05-09 19:08:31 +02:00
4510d3d130 fix SW path 2023-05-09 19:08:31 +02:00
ec0164452c Register serviceworker 2023-05-09 19:08:31 +02:00
3da3e09dc5 Add logo files 2023-05-09 19:08:31 +02:00
Karl Prieb
fdaef74109 add a left margin on search bar 2023-05-09 19:08:00 +02:00
2e9a36557d Add post expiry inputs 2023-05-09 19:08:00 +02:00
613e8584de Add link to RSS feed to the profile (#234)
Today I learned that akkoma and mastodon (and potentially other activitypub services) offer RSS/Atom feeds for user profiles at `[user profile url].rss`. This PR adds a direct link to the feed because I haven’t seen anything link to the feed on either mastodon-fe or pleroma-fe

Co-authored-by: Charlotte 🦝 Delenk <lotte@chir.rs>
Reviewed-on: AkkomaGang/pleroma-fe#234
Co-authored-by: darkkirb <lotte@chir.rs>
Co-committed-by: darkkirb <lotte@chir.rs>
2023-05-09 19:08:00 +02:00
Sean Meininger
359886928e added maintainer code 2023-05-09 19:08:00 +02:00
d2f704c6f5 Redirect users to "awaiting approval" message on register (#231)
Ref #81 - this implements a "your request has been sent" message

 ![image](/attachments/61dc3f5e-2706-46f9-a1ca-4efe3f526ff3)

Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: AkkomaGang/pleroma-fe#231
2023-05-09 19:08:00 +02:00
cba4a23379 Only reload user if it _is_ a user (#232)
Ref #181

Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: AkkomaGang/pleroma-fe#232
2023-05-09 19:08:00 +02:00
64cb7443a8 Don't error out if we can't fetch reports 2023-05-09 19:08:00 +02:00
David
c3f829eb68 Allow using mouse wheel to navigate through the emoji tabs 2023-05-09 19:08:00 +02:00
46520dadfd mobile-newline (#226)
Allows the handle of users to drop down onto the next line if there isn't enough room in order to improve useability on mobile

Reviewed-on: AkkomaGang/pleroma-fe#226
Co-authored-by: Beefox <bee@beefox.xyz>
Co-committed-by: Beefox <bee@beefox.xyz>
2023-05-09 19:08:00 +02:00
f75571edd0 move domain block to drop down menu 2023-05-09 19:08:00 +02:00
240389a83f Allow for searching unicode emoji via inputting emoji
(This is needed for the react menu)
2023-05-09 19:08:00 +02:00
d03969eb03 Constrain content to status content (#218) (#220)
MFM No longer overflows
![image](/attachments/7bbf519a-9fd2-492d-aba0-9a0e1ded6373)

Co-authored-by: David <dmgf2008@hotmail.com>
Reviewed-on: AkkomaGang/pleroma-fe#220
Co-authored-by: Mergan <mergan@noreply.akkoma>
Co-committed-by: Mergan <mergan@noreply.akkoma>
2023-05-09 19:08:00 +02:00
Weblate
7ea89a8b44 Translated using Weblate (Spanish)
Currently translated at 87.1% (893 of 1025 strings)

Co-authored-by: taretka <info@tarteka.net>
Translate-URL: http://translate.akkoma.dev/projects/akkoma/pleroma-fe/es/
Translation: Pleroma fe/pleroma-fe
2023-05-09 19:08:00 +02:00
84950b9671 Fix build 2023-05-09 19:08:00 +02:00
4379c67180 Copy conversation display style from instance
Fixes #210
2023-05-09 19:08:00 +02:00
hynet-mel
a6d842b0b9 Customize the theme 2022-11-17 16:09:43 +01:00
hynet-mel
1547c34a6a Funky string customizing 2022-11-17 16:09:43 +01:00
hynet-mel
e651134249 Change NSFW image 2022-11-17 16:03:10 +01:00
hynet-mel
e27f020e2e Add custom fonts to css + misc tweaks like logo 2022-11-17 16:03:10 +01:00
hynet-mel
9660a57be1 Add fonts 2022-11-17 16:03:10 +01:00
hynet-mel
c02148c41c Replace background + base user prefs 2022-11-17 16:03:10 +01:00
hynet-mel
a87ddccaa6 Replace logos 2022-11-17 16:03:10 +01:00
hynet-mel
da958beb7b Add HyNET themes 2022-11-17 16:03:10 +01:00
hynet-mel
0090240e7d initial commit 2022-11-17 16:03:10 +01:00
2c9b73646c Merge pull request 'hotfix: mfm mysteries' (#215) from develop into stable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: AkkomaGang/pleroma-fe#215
2022-11-15 16:01:07 +00:00
80a519d7e4 Merge pull request 'hotfix: translation' (#207) from develop into stable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: AkkomaGang/pleroma-fe#207
2022-11-12 19:08:20 +00:00
975f04bf5a Merge pull request '2022.11 stable release' (#202) from develop into stable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: AkkomaGang/pleroma-fe#202
2022-11-12 15:33:57 +00:00
c8c8d40827 Merge pull request '2022.10 stable' (#177) from develop into stable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: AkkomaGang/pleroma-fe#177
2022-10-08 11:13:01 +00:00
d7499a1f91 Merge pull request '2022.09 stable' (#160) from develop into stable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: AkkomaGang/pleroma-fe#160
2022-09-10 14:39:13 +00:00
5972d89117 Merge pull request 'stable release' (#130) from develop into stable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: AkkomaGang/pleroma-fe#130
2022-08-12 15:26:51 +00:00
d03872d598 Merge pull request 'port MFM link into stable docs' (#38) from develop into stable
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: AkkomaGang/pleroma-fe#38
2022-07-15 13:02:06 +00:00
175 changed files with 7130 additions and 3917 deletions

View file

@ -0,0 +1,49 @@
name: "Bug report"
about: "Something isn't working as expected"
title: "[bug] "
body:
- type: markdown
attributes:
value: "Thanks for taking the time to file this bug report! Please try to be as specific and detailed as you can, so we can track down the issue and fix it as soon as possible."
- type: input
id: version
attributes:
label: "Version"
description: "Which version of pleroma-fe are you running? If running develop, specify the commit hash."
placeholder: "e.g. 2022.11, 40e86998e6"
- type: textarea
id: attempt
attributes:
label: "What were you trying to do?"
validations:
required: true
- type: textarea
id: expectation
attributes:
label: "What did you expect to happen?"
validations:
required: true
- type: textarea
id: reality
attributes:
label: "What actually happened?"
validations:
required: true
- type: dropdown
id: severity
attributes:
label: "Severity"
description: "Does this issue prevent you from using the software as normal?"
options:
- "I cannot use the software"
- "I cannot use it as easily as I'd like"
- "I can manage"
validations:
required: true
- type: checkboxes
id: searched
attributes:
label: "Have you searched for this issue?"
description: "Please double-check that your issue is not already being tracked on [the forums](https://meta.akkoma.dev) or [the issue tracker](https://akkoma.dev/AkkomaGang/pleroma-fe/issues)."
options:
- label: "I have double-checked and have not found this issue mentioned anywhere."

View file

@ -0,0 +1,29 @@
name: "Feature request"
about: "I'd like something to be added to pleroma-fe"
title: "[feat] "
body:
- type: markdown
attributes:
value: "Thanks for taking the time to request a new feature! Please be as concise and clear as you can in your proposal, so we could understand what you're going for."
- type: textarea
id: idea
attributes:
label: "The idea"
description: "What do you think you should be able to do in pleroma-fe?"
validations:
required: true
- type: textarea
id: reason
attributes:
label: "The reasoning"
description: "Why would this be a worthwhile feature? Does it solve any problems? Have people talked about wanting it?"
validations:
required: true
- type: checkboxes
id: searched
attributes:
label: "Have you searched for this feature request?"
description: "Please double-check that your issue is not already being tracked on [the forums](https://meta.akkoma.dev), [the issue tracker](https://akkoma.dev/AkkomaGang/pleroma-fe/issues), or the one for [the backend](https://akkoma.dev/AkkomaGang/akkoma/issues)."
options:
- label: "I have double-checked and have not found this feature request mentioned anywhere."
- label: "This feature is related to the pleroma-fe Akkoma frontend specifically, and not the backend."

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ selenium-debug.log
config/local.json config/local.json
config/local.*.json config/local.*.json
docs/site/ docs/site/
.vscode/

View file

@ -1,19 +1,13 @@
{ {
"extends": [ "extends": [
"stylelint-rscss/config", "stylelint-config-recommended-vue/scss",
"stylelint-config-recommended", "stylelint-config-recommended",
"stylelint-config-standard" "stylelint-config-standard"
], ],
"customSyntax": "postcss-scss",
"rules": { "rules": {
"declaration-no-important": true, "declaration-no-important": true,
"rscss/no-descendant-combinator": false, "selector-class-pattern": null,
"rscss/class-format": [ "custom-property-pattern": null
true,
{
"component": "pascal-case",
"variant": "^-[a-z]\\w+",
"element": "^[a-z]\\w+"
}
]
} }
} }

View file

@ -1,3 +1,4 @@
platform: linux/amd64
pipeline: pipeline:
lint: lint:
when: when:
@ -7,7 +8,7 @@ pipeline:
commands: commands:
- yarn - yarn
- yarn lint - yarn lint
- yarn stylelint #- yarn stylelint
test: test:
when: when:

View file

@ -1,28 +1,30 @@
# Pleroma-FE # Akkoma-FE
![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet) ![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet)
This is a fork of Pleroma-FE from the Pleroma project, with support for new Akkoma features such as: This is a fork of Akkoma-FE from the Pleroma project, with support for new Akkoma features such as:
- MFM support via [marked-mfm](https://akkoma.dev/sfr/marked-mfm) - MFM support via [marked-mfm](https://akkoma.dev/sfr/marked-mfm)
- Custom emoji reactions - Custom emoji reactions
And specificly, this is a tweaked version with all the hyena.network themes and tweaks in place
# For Translators # For Translators
The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Pleroma-FE. The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Akkoma-FE.
Alternatively, edit/create `src/i18n/$LANGUAGE_CODE.json` (where `$LANGUAGE_CODE` is the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language), then add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js) if it doesn't already exist there. Alternatively, edit/create `src/i18n/$LANGUAGE_CODE.json` (where `$LANGUAGE_CODE` is the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language), then add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js) if it doesn't already exist there.
Pleroma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js. Akkoma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js.
# FOR ADMINS # FOR ADMINS
To use Pleroma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Pleroma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc. To use Akkoma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Akkoma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc.
## Build Setup ## Build Setup
``` bash ``` bash
# install dependencies # install dependencies
npm install -g yarn corepack enable
yarn yarn
# serve with hot reload at localhost:8080 # serve with hot reload at localhost:8080
@ -37,7 +39,7 @@ npm run unit
# For Contributors: # For Contributors:
You can create file `/config/local.json` (see [example](https://git.pleroma.social/pleroma/pleroma-fe/blob/develop/config/local.example.json)) to enable some convenience dev options: You can create file `/config/local.json` (see [example](https://akkoma.dev/AkkomaGang/akkoma-fe/src/branch/develop/config/local.example.json)) to enable some convenience dev options:
* `target`: makes local dev server redirect to some existing instance's BE instead of local BE, useful for testing things in near-production environment and searching for real-life use-cases. * `target`: makes local dev server redirect to some existing instance's BE instead of local BE, useful for testing things in near-production environment and searching for real-life use-cases.
* `staticConfigPreference`: makes FE's `/static/config.json` take preference of BE-served `/api/statusnet/config.json`. Only works in dev mode. * `staticConfigPreference`: makes FE's `/static/config.json` take preference of BE-served `/api/statusnet/config.json`. Only works in dev mode.

View file

@ -2,7 +2,6 @@ var path = require('path')
var config = require('../config') var config = require('../config')
var utils = require('./utils') var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../') var projectRoot = path.resolve(__dirname, '../')
const WorkboxPlugin = require('workbox-webpack-plugin');
var { VueLoaderPlugin } = require('vue-loader') var { VueLoaderPlugin } = require('vue-loader')
var env = process.env.NODE_ENV var env = process.env.NODE_ENV
@ -119,11 +118,6 @@ module.exports = {
] ]
}, },
plugins: [ plugins: [
new WorkboxPlugin.InjectManifest({
swSrc: path.join(__dirname, '..', 'src/sw.js'),
swDest: 'sw-pleroma.js',
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
}),
new VueLoaderPlugin() new VueLoaderPlugin()
] ]
} }

View file

@ -2,6 +2,7 @@ var path = require('path')
var config = require('../config') var config = require('../config')
var utils = require('./utils') var utils = require('./utils')
var webpack = require('webpack') var webpack = require('webpack')
const WorkboxPlugin = require('workbox-webpack-plugin');
var { merge } = require('webpack-merge') var { merge } = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf') var baseWebpackConfig = require('./webpack.base.conf')
var MiniCssExtractPlugin = require('mini-css-extract-plugin') var MiniCssExtractPlugin = require('mini-css-extract-plugin')
@ -32,6 +33,11 @@ var webpackConfig = merge(baseWebpackConfig, {
chunkFilename: utils.assetsPath('js/[name].[chunkhash].js') chunkFilename: utils.assetsPath('js/[name].[chunkhash].js')
}, },
plugins: [ plugins: [
new WorkboxPlugin.InjectManifest({
swSrc: path.join(__dirname, '..', 'src/sw.js'),
swDest: 'sw-pleroma.js',
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
}),
// http://vuejs.github.io/vue-loader/workflow/production.html // http://vuejs.github.io/vue-loader/workflow/production.html
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': env, 'process.env': env,

View file

@ -38,6 +38,11 @@ module.exports = {
assetsSubDirectory: 'static', assetsSubDirectory: 'static',
assetsPublicPath: '/', assetsPublicPath: '/',
proxyTable: { proxyTable: {
'/manifest.json': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost'
},
'/api': { '/api': {
target, target,
changeOrigin: true, changeOrigin: true,

View file

@ -1,4 +1,4 @@
{ {
"target": "https://pleroma.soykaf.com/", "target": "https://otp.akkoma.dev/",
"staticConfigPreference": false "staticConfigPreference": false
} }

View file

@ -1,15 +1,15 @@
# Pleroma-FE configuration and customization for instance administrators # Akkoma-FE configuration and customization for instance administrators
* *For user configuration, see [Pleroma-FE user guide](../user_guide)* * *For user configuration, see [Akkoma-FE user guide](../user_guide)*
* *For local development server configuration, see [Hacking, tweaking, contributing](HACKING.md)* * *For local development server configuration, see [Hacking, tweaking, contributing](HACKING.md)*
## Where configuration is stored ## Where configuration is stored
PleromaFE gets its configuration from several sources, in order of preference (the one above overrides ones below it) Akkoma-FE gets its configuration from several sources, in order of preference (the one above overrides ones below it)
1. `/api/statusnet/config.json` - this is generated on Backend and contains multiple things including instance name, char limit etc. It also contains FE/Client-specific data, PleromaFE uses `pleromafe` field of it. For more info on changing config on BE, look [here](https://docs.akkoma.dev/stable/configuration/cheatsheet.md#frontend_configurations) 1. `/api/statusnet/config.json` - this is generated on Backend and contains multiple things including instance name, char limit etc. It also contains FE/Client-specific data, Akkoma-FE uses `pleromafe` field of it. For more info on changing config on BE, look [here](https://docs.akkoma.dev/stable/configuration/cheatsheet.md#frontend_configurations)
2. `/static/config.json` - this is a static FE-provided file, containing only FE specific configuration. This file is completely optional and could be removed but is useful as a fallback if some configuration JSON property isn't present in BE-provided config. It's also a reference point to check what default configuration are and what JSON properties even exist. In local dev mode it could be used to override BE configuration, more about that in HACKING.md. File is located [here](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/static/config.json). 2. `/static/config.json` - this is a static FE-provided file, containing only FE specific configuration. This file is completely optional and could be removed but is useful as a fallback if some configuration JSON property isn't present in BE-provided config. It's also a reference point to check what default configuration are and what JSON properties even exist. In local dev mode it could be used to override BE configuration, more about that in HACKING.md. File is located [here](https://akkoma.dev/AkkomaGang/akkoma-fe/src/branch/develop/static/config.json).
3. Built-in defaults. Those are hard-coded defaults that are used when `/static/config.json` is not available and BE-provided configuration JSON is missing some JSON properties. ( [Code](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/modules/instance.js) ) 3. Built-in defaults. Those are hard-coded defaults that are used when `/static/config.json` is not available and BE-provided configuration JSON is missing some JSON properties. ( [Code](https://akkoma.dev/AkkomaGang/akkoma-fe/src/branch/develop/src/modules/instance.js) )
## Instance-defaults ## Instance-defaults
@ -59,7 +59,7 @@ Instance `logo`, could be any image, including svg. By default it assumes logo u
`logoMargin` allows you to adjust vertical margins between logo boundary and navbar borders. The idea is that to have logo's image without any extra margins and instead adjust them to your need in layout. `logoMargin` allows you to adjust vertical margins between logo boundary and navbar borders. The idea is that to have logo's image without any extra margins and instead adjust them to your need in layout.
### `minimalScopesMode` ### `minimalScopesMode`
Limit scope selection to *Direct*, *User default* and *Scope of post replying to*. This also makes it impossible to reply to a DM with a non-DM post from PleromaFE. Limit scope selection to *Direct*, *User default* and *Scope of post replying to*. This also makes it impossible to reply to a DM with a non-DM post from Akkoma-FE.
### `nsfwCensorImage` ### `nsfwCensorImage`
Use custom image for NSFW'd images Use custom image for NSFW'd images
@ -77,7 +77,7 @@ Change alignment of sidebar and panels to the right. Defaults to `false`.
Show panel showcasing instance features/settings to logged-out visitors Show panel showcasing instance features/settings to logged-out visitors
### `showInstanceSpecificPanel` ### `showInstanceSpecificPanel`
This allows you to include arbitrary HTML content in a panel below navigation menu. PleromaFE looks for an html page `instance/panel.html`, by default it's not provided in FE, but BE bundles some [default one](https://git.pleroma.social/pleroma/pleroma/blob/develop/priv/static/instance/panel.html). De-facto instance-defaults, since user can hide instance-specific panel. This allows you to include arbitrary HTML content in a panel below navigation menu. Akkoma-FE looks for an html page `instance/panel.html`, by default it's not provided in FE, but BE bundles some [default one](https://git.pleroma.social/pleroma/pleroma/blob/develop/priv/static/instance/panel.html). De-facto instance-defaults, since user can hide instance-specific panel.
### `subjectLineBehavior` ### `subjectLineBehavior`
How to handle subject line (CW) when replying to a post. How to handle subject line (CW) when replying to a post.

View file

@ -1,8 +1,8 @@
# Hacking, tweaking, contributing # Hacking, tweaking, contributing
## What PleromaFE even is, how it works ## What Akkoma-FE even is, how it works
PleromaFE is an SPA (Single-Page Application) backed by [Vue](https://vuejs.org/) framework. It means that it's just a nearly-empty HTML page with bunch of JavaScript that actually generates and controls DOM (i.e. html elements) in Runtime. Currently, there's no way around it - you have to have Javascript enabled in the browser to make it work, there is a theoretical possibility to generate some HTML server-side but it's not implemented yet. Akkoma-FE is an SPA (Single-Page Application) backed by [Vue](https://vuejs.org/) framework. It means that it's just a nearly-empty HTML page with bunch of JavaScript that actually generates and controls DOM (i.e. html elements) in Runtime. Currently, there's no way around it - you have to have Javascript enabled in the browser to make it work, there is a theoretical possibility to generate some HTML server-side but it's not implemented yet.
You can serve static html page and everything from any HTTP(S) server but currently it will try to access /api/ path at same domain it's running on, meaning that as of right now you cannot put it on one domain and access the other without proxying requests. You can serve static html page and everything from any HTTP(S) server but currently it will try to access /api/ path at same domain it's running on, meaning that as of right now you cannot put it on one domain and access the other without proxying requests.
@ -67,19 +67,19 @@ server {
### API, Data, Operations ### API, Data, Operations
In 99% cases PleromaFE uses [MastoAPI](https://docs.joinmastodon.org/api/) with [Pleroma Extensions](https://docs.akkoma.dev/stable/differences_in_mastoapi_responses.md) to fetch the data. The rest is either QvitterAPI leftovers or pleroma-exclusive APIs. QvitterAPI doesn't exactly have documentation and uses different JSON structure and sometimes different parameters and workflows, [this](https://twitter-api.readthedocs.io/en/latest/index.html) could be a good reference though. Some pleroma-exclusive API may still be using QvitterAPI JSON structure. In 99% cases Akkoma-FE uses [MastoAPI](https://docs.joinmastodon.org/api/) with [Pleroma Extensions](https://docs.akkoma.dev/stable/differences_in_mastoapi_responses.md) to fetch the data. The rest is either QvitterAPI leftovers or pleroma-exclusive APIs. QvitterAPI doesn't exactly have documentation and uses different JSON structure and sometimes different parameters and workflows, [this](https://twitter-api.readthedocs.io/en/latest/index.html) could be a good reference though. Some pleroma-exclusive API may still be using QvitterAPI JSON structure.
PleromaFE supports both formats by transforming them into internal format which is basically QvitterAPI one with some additions and renaming. All data is passed trough [Entity Normalizer](https://git.pleroma.social/pleroma/pleroma-fe/-/blob/develop/src/services/entity_normalizer/entity_normalizer.service.js) which can serve as a reference of API and what's actually used, it's also a host for all the hacks and data transformation. Akkoma-FE supports both formats by transforming them into internal format which is basically QvitterAPI one with some additions and renaming. All data is passed trough [Entity Normalizer](https://akkoma.dev/AkkomaGang/akkoma-fe/src/branch/develop/src/services/entity_normalizer/entity_normalizer.service.js) which can serve as a reference of API and what's actually used, it's also a host for all the hacks and data transformation.
For most part, PleromaFE tries to store all the info it can get in global vuex store - every user and post are passed trough updating mechanism where data is either added or merged with existing data, reactively updating the information throughout UI, so if in newest request user's post counter increased, it will be instantly updated in open user profile cards. This is also used to find users, posts and sometimes to build timelines and/or request parameters. For most part, Akkoma-FE tries to store all the info it can get in global vuex store - every user and post are passed trough updating mechanism where data is either added or merged with existing data, reactively updating the information throughout UI, so if in newest request user's post counter increased, it will be instantly updated in open user profile cards. This is also used to find users, posts and sometimes to build timelines and/or request parameters.
PleromaFE also tries to persist this store, however only stable data is stored, such as user authentication and preferences, user highlights. Persistence is performed by saving and loading chunk of vuex store in browser's LocalStorage/IndexedDB. Akkoma-FE also tries to persist this store, however only stable data is stored, such as user authentication and preferences, user highlights. Persistence is performed by saving and loading chunk of vuex store in browser's LocalStorage/IndexedDB.
TODO: Refactor API code and document it here TODO: Refactor API code and document it here
### Themes ### Themes
PleromaFE uses custom theme "framework" which is pretty much just a style tag rendered by vue which only contains CSS3 variables. Every color used in UI should be derived from theme. Theme is stored in a JSON object containing color, opacity, shadow and font information, with most of it being optional. Akkoma-FE uses custom theme "framework" which is pretty much just a style tag rendered by vue which only contains CSS3 variables. Every color used in UI should be derived from theme. Theme is stored in a JSON object containing color, opacity, shadow and font information, with most of it being optional.
The most basic theme can consist of 4 to 8 "basic colors", which is also what previous version of themes allowed, with all other colors being derived from those basic colors, i.e. "light background" will be "background" color lightened/darkened, "panel header" will be same as "foreground". The idea is that you can specify just basic color palette and everything else will be generated automatically, but if you really need to tweak some specific color - you can. The most basic theme can consist of 4 to 8 "basic colors", which is also what previous version of themes allowed, with all other colors being derived from those basic colors, i.e. "light background" will be "background" color lightened/darkened, "panel header" will be same as "foreground". The idea is that you can specify just basic color palette and everything else will be generated automatically, but if you really need to tweak some specific color - you can.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -12,6 +12,13 @@ code,
--md-accent-fg-color: #455a63; --md-accent-fg-color: #455a63;
} }
#app { background-repeat: repeat !important; }
@media all and (min-width: 800px) { .tab-icon { display: none !important; } }
.tab-switcher .tab-icon { color: #fff !important; }
.site-name { font-family: BLACKOUT; font-size: 3rem !important; line-height: 4rem;}
.site-name::before { content: "%"; }
.dropdown-item { color: #fff !important; }
@font-face { @font-face {
font-family: 'Tiresias PCFont'; font-family: 'Tiresias PCFont';
font-style: normal; font-style: normal;
@ -26,3 +33,42 @@ code,
src: local('Tiresias Infofont'), local('Tiresias Infofont'), src: local('Tiresias Infofont'), local('Tiresias Infofont'),
url('./fonts/Tiresias_Infofont.ttf') format('truetype') url('./fonts/Tiresias_Infofont.ttf') format('truetype')
} }
@font-face{
font-family:'blockzone';
src:url('./fonts/blockzone-webfont.woff2') format("woff2"),
url('./fonts/blockzone-webfont.woff') format("woff");
font-style:normal;
font-weight:400
}
@font-face{
font-family:'PeanutMoney';
src:url('./fonts/PeanutMoney.ttf') format("opentype");
font-style:normal;
font-weight:400
}
@font-face{
font-family:'Heading';
src:url('./fonts/heading.woff');
font-style:normal;
font-weight:400
}
@font-face{
font-family:'MonoMMM_5';
src:url('./fonts/monoMMM_5.woff');
font-style:normal;
font-weight:400
}
@font-face{
font-family:'Tiny';
src:url('./fonts/tiny.woff');
font-style:normal;
font-weight:400
}
@font-face{
font-family:'BLACKOUT';
src:url('./fonts/BLACKOUT.woff');
font-style:normal;
font-weight:400
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 362.83 362.83">
<defs>
<style>
.cls-1, .cls-2 {
fill: #fff;
}
.cls-2 {
stroke: #fff;
stroke-miterlimit: 10;
}
</style>
</defs>
<g id="Layer_6" data-name="Layer 6">
<path class="cls-1" d="M115.2,131.89c6.26-6.54,20.19-20.63,42.39-26.14,15.79-3.92,28.51-1.28,33.51,0,83.72,21.41,116.03,201.78,77.79,226.32-10.28,6.6-26.86,2.7-36.77-3.3-32.63-19.78-29.3-72.87-44.44-73.73-5.11-.29-7.15,5.8-20.91,24.94-19.63,27.3-31.49,43.44-49.21,50.87-2.53,1.06-26.91,12.07-41.84,1.23-38.55-28-2.96-155.84,39.49-200.18Zm56.31,10.45c-27.39-.52-46.38,38.21-37.98,54.55,10.09,19.62,65.5,18.26,74.77-3.3,7.21-16.78-11.38-50.77-36.79-51.24Z"/>
</g>
<g id="Layer_4" data-name="Layer 4">
<path class="cls-1" d="M68.93,86.51c-6.55,27.74,252.45,113.97,267.56,89.66,9.24-14.87-64.9-83.62-163.53-97.57-39.06-5.52-100.95-5.14-104.03,7.91Z"/>
</g>
<g id="Layer_5" data-name="Layer 5">
<path class="cls-2" d="M138.96,93.76c.41-5.25,6.51-5.74,28.85-19.42,26.97-16.51,28.85-22.38,56.86-40.83,30.07-19.81,48.46-31.94,54.82-26.61,9.72,8.15-25.18,43.33-21.31,99.35,.87,12.61,3.12,17.79-.86,23.01-18.25,23.95-120.07-13.68-118.35-35.5Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 B

View file

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg4485"
width="512"
height="512"
viewBox="0 0 512 512"
sodipodi:docname="pleroma_logo_vector_nobg.svg"
inkscape:version="0.92.1 r15371">
<metadata
id="metadata4491">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4489" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="997"
id="namedview4487"
showgrid="false"
inkscape:zoom="0.70710678"
inkscape:cx="26.131594"
inkscape:cy="235.37499"
inkscape:window-x="1912"
inkscape:window-y="22"
inkscape:window-maximized="1"
inkscape:current-layer="svg4485" />
<g
id="g4612">
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path4495"
d="M 235,89 V 423 H 152 V 115 l 26,-26 z"
style="opacity:1;fill:#fba457;fill-opacity:1;stroke:#009bff;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.17587938" />
<circle
r="26"
cx="178"
cy="115"
id="path4497"
style="opacity:1;fill:#fba457;fill-opacity:1;stroke:#009bff;stroke-width:0;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.17587938" />
<circle
r="26"
cx="335"
cy="230"
id="path4497-0"
style="opacity:1;fill:#fba457;fill-opacity:1;stroke:#009bff;stroke-width:0;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.17587938" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path4516"
d="M 277,256 V 89 l 84,3e-6 L 361.00002,230 335,256 Z"
style="fill:#fba457;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="26"
cx="335"
cy="397"
id="path4497-0-6"
style="opacity:1;fill:#fba457;fill-opacity:1;stroke:#009bff;stroke-width:0;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.17587938" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path4516-5"
d="m 277,423 v -83 h 84 l 2e-5,57 L 335,423 Z"
style="opacity:1;fill:#fba457;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -1,8 +1,8 @@
# Introduction to Pleroma-FE # Introduction to Akkoma-FE
## What is Pleroma-FE? ## What is Akkoma-FE?
Pleroma-FE is the default user-facing frontend for Pleroma. It's user interface is modeled after Qvitter which is modeled after an older Twitter design. It provides a simple 2-column interface for microblogging. While being simple by default it also provides many powerful customization options. Akkoma-FE is the default user-facing frontend for Pleroma. It's user interface is modeled after Qvitter which is modeled after an older Twitter design. It provides a simple 2-column interface for microblogging. While being simple by default it also provides many powerful customization options.
## How can I use it? ## How can I use it?
If your instance uses Pleroma-FE, you can acces it by going to your instance (e.g. <https://pleroma.soykaf.com>). You can read more about it's basic functionality in the [Pleroma-FE User Guide](./user_guide/). We also have [a guide for administrators](./CONFIGURATION.md) and for [hackers/contributors](./HACKING.md). If your instance uses Akkoma-FE, you can acces it by going to your instance (e.g. <https://pleroma.soykaf.com>). You can read more about it's basic functionality in the [Akkoma-FE User Guide](./user_guide/). We also have [a guide for administrators](./CONFIGURATION.md) and for [hackers/contributors](./HACKING.md).

View file

@ -1,13 +1,13 @@
# Adding stickers # Adding stickers
Pleroma-fe supports stickers, which are essentially little images stored server-side Akkoma-FE supports stickers, which are essentially little images stored server-side
which can be selected by a user to automatically attach them to a post. which can be selected by a user to automatically attach them to a post.
There's no explicit setting for these, they just rely on the existence of certain files. There's no explicit setting for these, they just rely on the existence of certain files.
## Initialising the sticker config file ## Initialising the sticker config file
You're probably serving pleroma-fe from your instance's `instance/static/` directory - You're probably serving Akkoma-FE from your instance's `instance/static/` directory -
this directy can also override files served at a given path. this directy can also override files served at a given path.
The first thing we need to do is set up our `stickers.json` file. At `instance/static/static/stickers.json`, The first thing we need to do is set up our `stickers.json` file. At `instance/static/static/stickers.json`,
@ -50,4 +50,4 @@ The `tabIcon` will appear on the sticker picker itself as a representative of th
You can add as many stickers as you like. They should all be in the same directory as your `pack.json`. You can add as many stickers as you like. They should all be in the same directory as your `pack.json`.
Now you should find that there's a sticky note icon on the emoji picker on pleroma-fe that allows you to attach stickers. Now you should find that there's a sticky note icon on the emoji picker on Akkoma-FE that allows you to attach stickers.

View file

@ -8,13 +8,13 @@
> >
> --Catbag > --Catbag
Pleroma-FE is the default user-facing frontend for Pleroma. If your instance uses Pleroma-FE, you can access it by going to your instance (e.g. <https://pleroma.soykaf.com>). After logging in you will have two columns in front of you. Here we're going to keep it to the default behaviour, but some instances swap the left and right columns. If you're on such an instance what we refer to as the left column will be on your right and vice versa. Akkoma-FE is the default user-facing frontend for Pleroma. If your instance uses Akkoma-FE, you can access it by going to your instance (e.g. <https://pleroma.soykaf.com>). After logging in you will have two columns in front of you. Here we're going to keep it to the default behaviour, but some instances swap the left and right columns. If you're on such an instance what we refer to as the left column will be on your right and vice versa.
### Left column ### Left column
- first block: This section is dedicated to [posting](posting_reading_basic_functions.md) - first block: This section is dedicated to [posting](posting_reading_basic_functions.md)
- second block: Here you can switch between the different views for the right column. - second block: Here you can switch between the different views for the right column.
- Optional third block: This is the Instance panel that can be activated, but is deactivated by default. It's fully customisable by instance admins and by default has links to the Pleroma-FE and Mastodon-FE. - Optional third block: This is the Instance panel that can be activated, but is deactivated by default. It's fully customisable by instance admins and by default has links to the Akkoma-FE and Mastodon-FE.
- fourth block: This is the Notifications block, here you will get notified whenever somebody mentions you, follows you, repeats or favorites one of your statuses - fourth block: This is the Notifications block, here you will get notified whenever somebody mentions you, follows you, repeats or favorites one of your statuses
### Right column ### Right column

View file

@ -15,11 +15,11 @@ Posts will contain the text you are posting, but some content will be modified:
Let's clear up some basic stuff. When you post something it's called a **post** or it could be called a **status** or even a **toot** or a **prööt** depending on whom you ask. Post has body/content but it also has some other stuff in it - from attachments, visibility scope, subject line... Let's clear up some basic stuff. When you post something it's called a **post** or it could be called a **status** or even a **toot** or a **prööt** depending on whom you ask. Post has body/content but it also has some other stuff in it - from attachments, visibility scope, subject line...
**Emoji** are small images embedded in text, there are two major types of emoji: [unicode emoji](https://en.wikipedia.org/wiki/Emoji) and custom emoji. While unicode emoji are universal and standardized, they can appear differently depending on where you are using them or may not appear at all on older systems. Custom emoji are a more *fun* kind - instance administrator can define many images as *custom emoji* for their users. This works very simple - custom emoji is defined by its *shortcode* and an image, so that any shortcode enclosed in colons get replaced with image if such shortcode exist. **Emoji** are small images embedded in text, there are two major types of emoji: [unicode emoji](https://en.wikipedia.org/wiki/Emoji) and custom emoji. While unicode emoji are universal and standardized, they can appear differently depending on where you are using them or may not appear at all on older systems. Custom emoji are a more *fun* kind - instance administrator can define many images as *custom emoji* for their users. This works very simple - custom emoji is defined by its *shortcode* and an image, so that any shortcode enclosed in colons get replaced with image if such shortcode exist.
Let's say there's a `:pleroma:` emoji defined on an instance. That means Let's say there's a `:akkoma:` emoji defined on an instance. That means
> First time using :pleroma: pleroma! > First time using :akkoma: akkoma!
will become will become
> First time using ![pleroma](../assets/example_emoji.png) pleroma! > First time using ![akkoma](../assets/example_emoji.png) akkoma!
Note that you can only use emoji defined on your instance, you cannot "copy" someone else's emoji, and will have to ask your administrator to copy emoji from other instance to yours. Note that you can only use emoji defined on your instance, you cannot "copy" someone else's emoji, and will have to ask your administrator to copy emoji from other instance to yours.
Lastly, there's two convenience options for emoji: an emoji picker (smiley face to the right of "submit" button) and autocomplete suggestions - when you start typing :shortcode: it will automatically try to suggest you emoji and complete the shortcode for you if you select one. If emoji doesn't show up in suggestions nor in emoji picker it means there's no such emoji on your instance, if shortcode doesn't match any defined emoji it will appear as text. Lastly, there's two convenience options for emoji: an emoji picker (smiley face to the right of "submit" button) and autocomplete suggestions - when you start typing :shortcode: it will automatically try to suggest you emoji and complete the shortcode for you if you select one. If emoji doesn't show up in suggestions nor in emoji picker it means there's no such emoji on your instance, if shortcode doesn't match any defined emoji it will appear as text.
@ -42,7 +42,7 @@ A few things to consider about the security and usage of these scopes:
- Changing scopes during a thread or adding people to a direct message will not retroactively make them see the whole conversation. If you add someone to a direct message conversation, they will not see the post that happened before they were mentioned. - Changing scopes during a thread or adding people to a direct message will not retroactively make them see the whole conversation. If you add someone to a direct message conversation, they will not see the post that happened before they were mentioned.
* **Reply-to** if you are replying to someone, your post will also contain a note that your post is referring to the post you're replying to. Person you're replying to will receive a notification *even* if you remove them from mentioned people. You won't receive notifications when replying to your own posts, but it's useful to reply to your own posts to provide people some context if it's a follow-up to a previous post. There's a small "Reply to ..." label under post author's name which you can hover on to see what post it's referring to. * **Reply-to** if you are replying to someone, your post will also contain a note that your post is referring to the post you're replying to. Person you're replying to will receive a notification *even* if you remove them from mentioned people. You won't receive notifications when replying to your own posts, but it's useful to reply to your own posts to provide people some context if it's a follow-up to a previous post. There's a small "Reply to ..." label under post author's name which you can hover on to see what post it's referring to.
Sometimes you may encounter posts that seem different than what they are supposed to. For example, you might see a direct message without any mentions in the text. This can happen because internally, the Fediverse has a different addressing mechanism similar to email, with `to` and `cc` fields. While these are not directly accessible in PleromaFE, other software in the Fediverse might generate those posts. Do not worry in these cases, these are normal and not a bug. Sometimes you may encounter posts that seem different than what they are supposed to. For example, you might see a direct message without any mentions in the text. This can happen because internally, the Fediverse has a different addressing mechanism similar to email, with `to` and `cc` fields. While these are not directly accessible in Akkoma-FE, other software in the Fediverse might generate those posts. Do not worry in these cases, these are normal and not a bug.
## Rich text ## Rich text
@ -67,7 +67,7 @@ If you set the input-method to Markdown, and post this, it will look something l
## Misskey markdown ## Misskey markdown
The akkoma version of pleroma-fe includes support for writing and rendering Akkoma-FE includes support for writing and rendering
misskey markdown (MFM). To write this you will need to select "MFM" from misskey markdown (MFM). To write this you will need to select "MFM" from
the content type dropdown (if supported), and then you can format text the content type dropdown (if supported), and then you can format text
[in MFM](https://akkoma.dev/sfr/marked-mfm/src/branch/master/docs/syntax.md). [in MFM](https://akkoma.dev/sfr/marked-mfm/src/branch/master/docs/syntax.md).

View file

@ -83,7 +83,7 @@ Here you can change your password, revoke access tokens, configure 2-factor auth
## Theme ## Theme
Here you can change the look and feel of Pleroma-FE. You can choose from several instance-provided presets and you can load one from file and save current theme to file. Before you apply new theme you can see what it will look like approximately in preview section. Here you can change the look and feel of Akkoma-FE. You can choose from several instance-provided presets and you can load one from file and save current theme to file. Before you apply new theme you can see what it will look like approximately in preview section.
The themes engine was made to be easy to use while giving an option for powerful in-depth customization - you can just tweak colors on "Common" tab and leave everything else as is. The themes engine was made to be easy to use while giving an option for powerful in-depth customization - you can just tweak colors on "Common" tab and leave everything else as is.

View file

@ -4,7 +4,7 @@ When you see someone, you can click on their user picture to view their profile,
**Following** is self-explanatory, it adds them to your Home Timeline, lists you as a follower and gives you access to follower-only posts if they have any. **Following** is self-explanatory, it adds them to your Home Timeline, lists you as a follower and gives you access to follower-only posts if they have any.
**Muting** collapses posts and notifications made by them, giving you an option to see the post if you're curious. Clients other than PleromaFE may completely remove their posts. **Muting** collapses posts and notifications made by them, giving you an option to see the post if you're curious. Clients other than Akkoma-FE may completely remove their posts.
**Blocking** a user removes them from your timeline and notifications and prevents them from following you (automatically unfollows them from you). **Blocking** a user removes them from your timeline and notifications and prevents them from following you (automatically unfollows them from you).

View file

@ -1,11 +1,11 @@
site_name: Pleroma-FE Documentation site_name: Akkoma-FE Documentation
theme: theme:
favicon: 'images/pleroma_logo_vector_bg_32.png' favicon: 'images/akkoma_logo_vector_bg_32.png'
name: 'material' name: 'material'
custom_dir: 'theme' custom_dir: 'theme'
# Disable google fonts # Disable google fonts
font: false font: false
logo: 'images/pleroma_logo_vector_nobg.svg' logo: 'images/akkoma_logo_vector_nobg.svg'
features: features:
- tabs - tabs
palette: palette:
@ -14,8 +14,8 @@ theme:
extra_css: extra_css:
- css/extra.css - css/extra.css
repo_name: 'AkkomaGang/pleroma-fe' repo_name: 'AkkomaGang/akkoma-fe'
repo_url: 'https://akkoma.dev/AkkomaGang/pleroma-fe' repo_url: 'https://akkoma.dev/AkkomaGang/akkoma-fe'
extra: extra:
repo_icon: gitea repo_icon: gitea

View file

@ -38,11 +38,11 @@
{% endif %} {% endif %}
{% if page and page.url.startswith('backend') %} {% if page and page.url.startswith('backend') %}
{% set repo_url = "https://git.pleroma.social/pleroma/pleroma" %} {% set repo_url = "https://akkoma.dev/AkkomaGang/akkoma" %}
{% set repo_name = "pleroma/pleroma" %} {% set repo_name = "AkkomaGang/akkoma" %}
{% elif page and page.url.startswith('frontend') %} {% elif page and page.url.startswith('frontend') %}
{% set repo_url = "https://git.pleroma.social/pleroma/pleroma-fe" %} {% set repo_url = "https://akkoma.dev/AkkomaGang/akkoma-fe" %}
{% set repo_name = "pleroma/pleroma-fe" %} {% set repo_name = "AkkomaGang/akkoma-fe" %}
{% else %} {% else %}
{% set repo_url = config.repo_url %} {% set repo_url = config.repo_url %}
{% set repo_name = config.repo_name %} {% set repo_name = config.repo_name %}

View file

@ -4,13 +4,14 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<title>Akkoma</title> <title>Akkoma</title>
<link rel="stylesheet" href="/static/font/css/fontello.css">
<link rel="stylesheet" href="/static/font/css/animation.css">
<link rel="stylesheet" href="/static/font/tiresias.css"> <link rel="stylesheet" href="/static/font/tiresias.css">
<link rel="stylesheet" href="/static/font/css/lato.css"> <link rel="stylesheet" href="/static/font/css/lato.css">
<link rel="stylesheet" href="/static/mfm.css"> <link rel="stylesheet" href="/static/mfm.css">
<link rel="stylesheet" href="/static/custom.css">
<link rel="stylesheet" href="/static/theme-holder.css" id="theme-holder">
<!--server-generated-meta--> <!--server-generated-meta-->
<link rel="icon" type="image/png" href="/favicon.png"> <link rel="icon" type="image/png" href="/favicon.png">
<link rel="manifest" href="/manifest.json">
</head> </head>
<body class="hidden"> <body class="hidden">
<noscript>To use Akkoma, please enable JavaScript.</noscript> <noscript>To use Akkoma, please enable JavaScript.</noscript>

View file

@ -1,6 +1,6 @@
{ {
"name": "pleroma_fe", "name": "pleroma_fe",
"version": "3.2.0", "version": "3.11.0",
"description": "A frontend for Akkoma instances", "description": "A frontend for Akkoma instances",
"author": "Roger Braun <roger@rogerbraun.net>", "author": "Roger Braun <roger@rogerbraun.net>",
"private": true, "private": true,
@ -11,30 +11,30 @@
"unit:watch": "karma start test/unit/karma.conf.js --single-run=false", "unit:watch": "karma start test/unit/karma.conf.js --single-run=false",
"e2e": "node test/e2e/runner.js", "e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e", "test": "npm run unit && npm run e2e",
"stylelint": "npx stylelint src/components/status/status.scss", "stylelint": "stylelint src/**/*.scss",
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs", "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs" "lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "7.17.8", "@babel/runtime": "7.17.8",
"@chenfengyuan/vue-qrcode": "2.0.0", "@chenfengyuan/vue-qrcode": "2.0.0",
"@floatingghost/pinch-zoom-element": "^1.3.1",
"@fortawesome/fontawesome-svg-core": "1.3.0", "@fortawesome/fontawesome-svg-core": "1.3.0",
"@fortawesome/free-regular-svg-icons": "^6.1.2", "@fortawesome/free-regular-svg-icons": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.2.0", "@fortawesome/free-solid-svg-icons": "^6.2.0",
"@fortawesome/vue-fontawesome": "3.0.1", "@fortawesome/vue-fontawesome": "3.0.1",
"@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@vuelidate/core": "^2.0.0",
"@vuelidate/core": "2.0.0-alpha.42", "@vuelidate/validators": "^2.0.0",
"@vuelidate/validators": "2.0.0-alpha.30", "blurhash": "^2.0.4",
"body-scroll-lock": "2.7.1", "body-scroll-lock": "2.7.1",
"chromatism": "3.0.0", "chromatism": "3.0.0",
"click-outside-vue3": "4.0.1", "click-outside-vue3": "4.0.1",
"cropperjs": "1.5.12", "cropperjs": "1.5.12",
"diff": "3.5.0", "diff": "3.5.0",
"escape-html": "1.0.3", "escape-html": "1.0.3",
"iso-639-1": "^2.1.15",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"localforage": "1.10.0", "localforage": "1.10.0",
"marked": "^4.2.2",
"marked-mfm": "^0.5.0",
"parse-link-header": "^2.0.0", "parse-link-header": "^2.0.0",
"phoenix": "1.6.2", "phoenix": "1.6.2",
"punycode.js": "2.1.0", "punycode.js": "2.1.0",
@ -84,7 +84,6 @@
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"http-proxy-middleware": "0.21.0", "http-proxy-middleware": "0.21.0",
"inject-loader": "2.0.1", "inject-loader": "2.0.1",
"iso-639-1": "2.1.15",
"isparta-loader": "2.0.0", "isparta-loader": "2.0.0",
"json-loader": "0.5.7", "json-loader": "0.5.7",
"karma": "6.3.17", "karma": "6.3.17",
@ -103,7 +102,9 @@
"nightwatch": "0.9.21", "nightwatch": "0.9.21",
"opn": "4.0.2", "opn": "4.0.2",
"ora": "0.4.1", "ora": "0.4.1",
"postcss-html": "^1.5.0",
"postcss-loader": "3.0.0", "postcss-loader": "3.0.0",
"postcss-sass": "^0.5.0",
"raw-loader": "0.5.1", "raw-loader": "0.5.1",
"sass": "^1.56.0", "sass": "^1.56.0",
"sass-loader": "^13.2.0", "sass-loader": "^13.2.0",
@ -112,9 +113,11 @@
"shelljs": "0.8.5", "shelljs": "0.8.5",
"sinon": "2.4.1", "sinon": "2.4.1",
"sinon-chai": "2.14.0", "sinon-chai": "2.14.0",
"stylelint": "13.6.1", "stylelint": "^14.15.0",
"stylelint-config-standard": "20.0.0", "stylelint-config-recommended-vue": "^1.4.0",
"stylelint-rscss": "0.4.0", "stylelint-config-standard": "^29.0.0",
"stylelint-config-standard-scss": "^6.1.0",
"stylelint-rscss": "^0.4.0",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"vue-loader": "^17.0.0", "vue-loader": "^17.0.0",
"vue-style-loader": "^4.1.2", "vue-style-loader": "^4.1.2",

View file

@ -64,6 +64,11 @@ export default {
'-' + this.layoutType '-' + this.layoutType
] ]
}, },
pageBackground () {
return this.mergedConfig.displayPageBackgrounds
? this.$store.state.users.displayBackground
: null
},
currentUser () { return this.$store.state.users.currentUser }, currentUser () { return this.$store.state.users.currentUser },
userBackground () { return this.currentUser.background_image }, userBackground () { return this.currentUser.background_image },
instanceBackground () { instanceBackground () {
@ -71,7 +76,7 @@ export default {
? null ? null
: this.$store.state.instance.background : this.$store.state.instance.background
}, },
background () { return this.userBackground || this.instanceBackground }, background () { return this.pageBackground || this.userBackground || this.instanceBackground },
bgStyle () { bgStyle () {
if (this.background) { if (this.background) {
return { return {

View file

@ -1,13 +1,14 @@
// stylelint-disable rscss/class-format // stylelint-disable rscss/class-format
@import './_variables.scss'; @import './_variables.scss';
@import '@fortawesome/fontawesome-svg-core/styles.css';
@import '@floatingghost/pinch-zoom-element/dist/pinch-zoom.css';
:root { :root {
--navbar-height: 3.5rem; --navbar-height: 3.5rem;
--post-line-height: 1.4; --post-line-height: 1.4;
} }
html { html {
font-size: 14px; font-size: 0.875rem;
// overflow-x: clip causes my browser's tab to crash with SIGILL lul // overflow-x: clip causes my browser's tab to crash with SIGILL lul
} }
@ -468,7 +469,7 @@ textarea,
color: $fallback--lightText; color: $fallback--lightText;
color: var(--inputText, $fallback--lightText); color: var(--inputText, $fallback--lightText);
font-family: sans-serif; font-family: sans-serif;
font-family: var(--inputFont, sans-serif); font-family: var(--interfaceFont, sans-serif);
font-size: 1em; font-size: 1em;
margin: 0; margin: 0;
box-sizing: border-box; box-sizing: border-box;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 5 KiB

View file

@ -4,6 +4,8 @@ import { createRouter, createWebHistory } from 'vue-router'
import vClickOutside from 'click-outside-vue3' import vClickOutside from 'click-outside-vue3'
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
import { config } from '@fortawesome/fontawesome-svg-core';
config.autoAddCss = false
import App from '../App.vue' import App from '../App.vue'
import routes from './routes' import routes from './routes'
@ -150,6 +152,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
copyInstanceOption('showPanelNavShortcuts') copyInstanceOption('showPanelNavShortcuts')
copyInstanceOption('stopGifs') copyInstanceOption('stopGifs')
copyInstanceOption('logo') copyInstanceOption('logo')
copyInstanceOption('conversationDisplay')
store.dispatch('setInstanceOption', { store.dispatch('setInstanceOption', {
name: 'logoMask', name: 'logoMask',
@ -319,6 +322,9 @@ const getNodeInfo = async ({ store }) => {
: federation.enabled : federation.enabled
}) })
store.dispatch('setInstanceOption', { name: 'publicTimelineVisibility', value: metadata.publicTimelineVisibility })
store.dispatch('setInstanceOption', { name: 'federatedTimelineAvailable', value: metadata.federatedTimelineAvailable })
const accountActivationRequired = metadata.accountActivationRequired const accountActivationRequired = metadata.accountActivationRequired
store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired }) store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired })
@ -393,9 +399,6 @@ const afterStoreSetup = async ({ store, i18n }) => {
]) ])
// Start fetching things that don't need to block the UI // Start fetching things that don't need to block the UI
store.dispatch('fetchMutes')
store.dispatch('startFetchingAnnouncements')
store.dispatch('startFetchingReports')
getTOS({ store }) getTOS({ store })
getStickers({ store }) getStickers({ store })

View file

@ -22,6 +22,8 @@ import Lists from 'components/lists/lists.vue'
import ListTimeline from 'components/list_timeline/list_timeline.vue' import ListTimeline from 'components/list_timeline/list_timeline.vue'
import ListEdit from 'components/list_edit/list_edit.vue' import ListEdit from 'components/list_edit/list_edit.vue'
import AnnouncementsPage from 'components/announcements_page/announcements_page.vue' import AnnouncementsPage from 'components/announcements_page/announcements_page.vue'
import RegistrationRequestSent from 'components/registration_request_sent/registration_request_sent.vue'
import AwaitingEmailConfirmation from 'components/awaiting_email_confirmation/awaiting_email_confirmation.vue'
export default (store) => { export default (store) => {
const validateAuthenticatedRoute = (to, from, next) => { const validateAuthenticatedRoute = (to, from, next) => {
@ -62,6 +64,8 @@ export default (store) => {
{ name: 'interactions', path: '/users/:username/interactions', component: Interactions, beforeEnter: validateAuthenticatedRoute }, { name: 'interactions', path: '/users/:username/interactions', component: Interactions, beforeEnter: validateAuthenticatedRoute },
{ name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute }, { name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },
{ name: 'registration', path: '/registration', component: Registration }, { name: 'registration', path: '/registration', component: Registration },
{ name: 'registration-request-sent', path: '/registration-request-sent', component: RegistrationRequestSent },
{ name: 'awaiting-email-confirmation', path: '/awaiting-email-confirmation', component: AwaitingEmailConfirmation },
{ name: 'password-reset', path: '/password-reset', component: PasswordReset, props: true }, { name: 'password-reset', path: '/password-reset', component: PasswordReset, props: true },
{ name: 'registration-token', path: '/registration/:token', component: Registration }, { name: 'registration-token', path: '/registration/:token', component: Registration },
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute }, { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },

View file

@ -26,6 +26,9 @@ const AccountActions = {
ConfirmModal ConfirmModal
}, },
methods: { methods: {
refetchRelationship () {
return this.$store.dispatch('fetchUserRelationship', this.user.id)
},
showConfirmBlock () { showConfirmBlock () {
this.showingConfirmBlock = true this.showingConfirmBlock = true
}, },
@ -57,6 +60,14 @@ const AccountActions = {
}, },
reportUser () { reportUser () {
this.$store.dispatch('openUserReportingModal', { userId: this.user.id }) this.$store.dispatch('openUserReportingModal', { userId: this.user.id })
},
muteDomain () {
this.$store.dispatch('muteDomain', this.user.screen_name.split('@')[1])
.then(() => this.refetchRelationship())
},
unmuteDomain () {
this.$store.dispatch('unmuteDomain', this.user.screen_name.split('@')[1])
.then(() => this.refetchRelationship())
} }
}, },
computed: { computed: {

View file

@ -55,6 +55,20 @@
> >
{{ $t('user_card.report') }} {{ $t('user_card.report') }}
</button> </button>
<button
v-if="relationship.domain_blocking"
class="btn button-default btn-block dropdown-item"
@click="unmuteDomain"
>
{{ $t('user_card.domain_muted') }}
</button>
<button
v-else-if="!user.is_local"
class="btn button-default btn-block dropdown-item"
@click="muteDomain"
>
{{ $t('user_card.mute_domain') }}
</button>
</div> </div>
</template> </template>
<template v-slot:trigger> <template v-slot:trigger>

View file

@ -18,6 +18,7 @@ import {
faPencilAlt, faPencilAlt,
faAlignRight faAlignRight
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import Blurhash from '../blurhash/Blurhash.vue'
library.add( library.add(
faFile, faFile,
@ -63,7 +64,8 @@ const Attachment = {
components: { components: {
Flash, Flash,
StillImage, StillImage,
VideoAttachment VideoAttachment,
Blurhash
}, },
computed: { computed: {
classNames () { classNames () {
@ -84,6 +86,9 @@ const Attachment = {
useContainFit () { useContainFit () {
return this.$store.getters.mergedConfig.useContainFit return this.$store.getters.mergedConfig.useContainFit
}, },
useBlurhash () {
return this.$store.getters.mergedConfig.useBlurhash
},
placeholderName () { placeholderName () {
if (this.attachment.description === '' || !this.attachment.description) { if (this.attachment.description === '' || !this.attachment.description) {
return this.type.toUpperCase() return this.type.toUpperCase()

View file

@ -37,7 +37,7 @@
white-space: pre-line; white-space: pre-line;
word-break: break-word; word-break: break-word;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: scroll; overflow: auto;
} }
&.-static { &.-static {

View file

@ -64,7 +64,15 @@
:title="attachment.description" :title="attachment.description"
@click.prevent.stop="toggleHidden" @click.prevent.stop="toggleHidden"
> >
<Blurhash
v-if="useBlurhash && attachment.blurhash"
:height="512"
:width="1024"
:hash="attachment.blurhash"
:punch="1"
/>
<img <img
v-else
:key="nsfwImage" :key="nsfwImage"
class="nsfw" class="nsfw"
:src="nsfwImage" :src="nsfwImage"

View file

@ -0,0 +1,4 @@
export default {
computed: {
}
}

View file

@ -0,0 +1,12 @@
<template>
<div class="panel panel-default">
<div class="panel-heading">
<h4>{{ $t('registration.awaiting_email_confirmation_title') }}</h4>
</div>
<div class="panel-body">
<p>{{ $t('registration.awaiting_email_confirmation') }}</p>
</div>
</div>
</template>
<script src="./awaiting_email_confirmation.js"></script>

View file

@ -0,0 +1,66 @@
<template>
<canvas
ref="canvas"
class="blurhash"
/>
</template>
<script>
import { decode } from "blurhash";
export default {
name: 'Blurhash',
props: {
hash: {
type: String,
required: true,
},
width: {
type: Number,
required: true,
},
height: {
type: Number,
required: true,
},
punch: {
type: Number,
default: null,
},
},
data() {
return {
canvas: null,
ctx: null,
};
},
mounted() {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext('2d');
this.canvas.width = 1024;
this.canvas.height = 512;
this.draw();
},
methods: {
draw() {
const pixels = decode(this.hash, this.width, this.height, this.punch);
const imageData = this.ctx.createImageData(this.width, this.height);
imageData.data.set(pixels);
this.ctx.putImageData(imageData, 0, 0);
fetch("/static/blurhash-overlay.png")
.then((response) => response.blob())
.then((blob) => {
const img = new Image();
img.src = URL.createObjectURL(blob);
img.onload = () => {
this.ctx.drawImage(img, 0, 0, this.width, this.height);
};
});
},
}
}
</script>
<style scoped>
</style>

View file

@ -1,6 +1,11 @@
import SearchBar from 'components/search_bar/search_bar.vue' import SearchBar from 'components/search_bar/search_bar.vue'
import ConfirmModal from '../confirm_modal/confirm_modal.vue' import ConfirmModal from '../confirm_modal/confirm_modal.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import {
publicTimelineVisible,
federatedTimelineVisible,
bubbleTimelineVisible,
} from '../../lib/timeline_visibility'
import { import {
faSignInAlt, faSignInAlt,
faSignOutAlt, faSignOutAlt,
@ -19,6 +24,7 @@ import {
faInfoCircle, faInfoCircle,
faUserTie faUserTie
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { mapState } from 'vuex'
library.add( library.add(
faSignInAlt, faSignInAlt,
@ -103,7 +109,12 @@ export default {
}, },
showBubbleTimeline () { showBubbleTimeline () {
return this.$store.state.instance.localBubbleInstances.length > 0 return this.$store.state.instance.localBubbleInstances.length > 0
} },
...mapState({
publicTimelineVisible,
federatedTimelineVisible,
bubbleTimelineVisible,
})
}, },
methods: { methods: {
scrollToTop () { scrollToTop () {

View file

@ -46,6 +46,7 @@
<router-link <router-link
:to="{ name: 'public-timeline' }" :to="{ name: 'public-timeline' }"
class="nav-icon" class="nav-icon"
v-if="publicTimelineVisible"
> >
<FAIcon <FAIcon
fixed-width fixed-width
@ -55,7 +56,7 @@
/> />
</router-link> </router-link>
<router-link <router-link
v-if="currentUser && showBubbleTimeline" v-if="bubbleTimelineVisible"
:to="{ name: 'bubble-timeline' }" :to="{ name: 'bubble-timeline' }"
class="nav-icon" class="nav-icon"
> >
@ -69,6 +70,7 @@
<router-link <router-link
:to="{ name: 'public-external-timeline' }" :to="{ name: 'public-external-timeline' }"
class="nav-icon" class="nav-icon"
v-if="federatedTimelineVisible"
> >
<FAIcon <FAIcon
fixed-width fixed-width

View file

@ -0,0 +1,133 @@
const EMOJI_SIZE = 32 + 8
const GROUP_TITLE_HEIGHT = 24
const BUFFER_SIZE = 3 * EMOJI_SIZE
const EmojiGrid = {
props: {
groups: {
required: true,
type: Array
}
},
data () {
return {
containerWidth: 0,
containerHeight: 0,
scrollPos: 0,
resizeObserver: null
}
},
mounted () {
const rect = this.$refs.container.getBoundingClientRect()
this.containerWidth = rect.width
this.containerHeight = rect.height
this.resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
this.containerWidth = entry.contentRect.width
this.containerHeight = entry.contentRect.height
}
})
this.resizeObserver.observe(this.$refs.container)
},
beforeUnmount () {
this.resizeObserver.disconnect()
this.resizeObserver = null
},
watch: {
groups () {
// Scroll to top when grid content changes
if (this.$refs.container) {
this.$refs.container.scrollTo(0, 0)
}
},
activeGroup (group) {
this.$emit('activeGroup', group)
}
},
methods: {
onScroll () {
this.scrollPos = this.$refs.container.scrollTop
},
onEmoji (emoji) {
this.$emit('emoji', emoji)
},
scrollToItem (itemId) {
const container = this.$refs.container
if (!container) return
for (const item of this.itemList) {
if (item.id === itemId) {
container.scrollTo(0, item.position.y)
return
}
}
}
},
computed: {
// Total height of scroller content
gridHeight () {
if (this.itemList.length === 0) return 0
const lastItem = this.itemList[this.itemList.length - 1]
return (
lastItem.position.y +
('title' in lastItem ? GROUP_TITLE_HEIGHT : EMOJI_SIZE)
)
},
activeGroup () {
const items = this.itemList
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i]
if ('title' in item && item.position.y <= this.scrollPos) {
return item.id
}
}
return null
},
itemList () {
const items = []
let x = 0
let y = 0
for (const group of this.groups) {
items.push({ position: { x, y }, id: group.id, title: group.text })
if (group.text.length) {
y += GROUP_TITLE_HEIGHT
}
for (const emoji of group.emojis) {
items.push({
position: { x, y },
id: `${group.id}-${emoji.displayText}`,
emoji
})
x += EMOJI_SIZE
if (x + EMOJI_SIZE > this.containerWidth) {
y += EMOJI_SIZE
x = 0
}
}
if (x > 0) {
y += EMOJI_SIZE
x = 0
}
}
return items
},
visibleItems () {
const startPos = this.scrollPos - BUFFER_SIZE
const endPos = this.scrollPos + this.containerHeight + BUFFER_SIZE
return this.itemList.filter((i) => {
return i.position.y >= startPos && i.position.y < endPos
})
},
scrolledClass () {
if (this.scrollPos <= 5) {
return 'scrolled-top'
} else if (this.scrollPos >= this.gridHeight - this.containerHeight - 5) {
return 'scrolled-bottom'
} else {
return 'scrolled-middle'
}
}
}
}
export default EmojiGrid

View file

@ -0,0 +1,60 @@
.emoji {
&-grid {
flex: 1 1 1px;
position: relative;
overflow: auto;
user-select: none;
mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat,
linear-gradient(to bottom, white 0, transparent 100%) top no-repeat,
linear-gradient(to top, white, white);
transition: mask-size 150ms;
mask-size: 100% 20px, 100% 20px, auto;
// Autoprefixed seem to ignore this one, and also syntax is different
-webkit-mask-composite: xor;
mask-composite: exclude;
&.scrolled {
&-top {
mask-size: 100% 20px, 100% 0, auto;
}
&-bottom {
mask-size: 100% 0, 100% 20px, auto;
}
}
margin-left: 5px;
min-height: 200px;
}
&-group-title {
position: absolute;
font-size: 0.85em;
width: 100%;
margin: 0;
height: 24px;
display: flex;
align-items: end;
&.disabled {
display: none;
}
}
&-item {
position: absolute;
width: 32px;
height: 32px;
box-sizing: border-box;
display: flex;
font-size: 32px;
align-items: center;
justify-content: center;
margin: 4px;
cursor: pointer;
img {
object-fit: contain;
max-width: 100%;
max-height: 100%;
}
}
}

View file

@ -0,0 +1,48 @@
<template>
<div
ref="container"
class="emoji-grid"
:class="scrolledClass"
@scroll.passive="onScroll"
>
<div
:style="{
height: `${gridHeight}px`,
}"
>
<template v-for="item in visibleItems">
<h6
v-if="'title' in item && item.title.length"
:key="'title-' + item.id"
class="emoji-group-title"
:style="{
top: item.position.y + 'px',
left: item.position.x + 'px'
}"
>
{{ item.title }}
</h6>
<span
v-else-if="'emoji' in item"
:key="'emoji-' + item.id"
class="emoji-item"
:title="item.emoji.displayText"
:style="{
top: item.position.y + 'px',
left: item.position.x + 'px'
}"
@click.stop.prevent="onEmoji(item.emoji)"
>
<span v-if="!item.emoji.imageUrl">{{ item.emoji.replacement }}</span>
<img
v-else
:src="item.emoji.imageUrl"
>
</span>
</template>
</div>
</div>
</template>
<script src="./emoji_grid.js"></script>
<style lang="scss" src="./emoji_grid.scss"></style>

View file

@ -205,7 +205,6 @@ const EmojiInput = {
}, },
triggerShowPicker () { triggerShowPicker () {
this.showPicker = true this.showPicker = true
this.$refs.picker.startEmojiLoad()
this.$nextTick(() => { this.$nextTick(() => {
this.scrollIntoView() this.scrollIntoView()
this.focusPickerInput() this.focusPickerInput()
@ -223,7 +222,6 @@ const EmojiInput = {
this.showPicker = !this.showPicker this.showPicker = !this.showPicker
if (this.showPicker) { if (this.showPicker) {
this.scrollIntoView() this.scrollIntoView()
this.$refs.picker.startEmojiLoad()
this.$nextTick(this.focusPickerInput) this.$nextTick(this.focusPickerInput)
} }
}, },

View file

@ -18,6 +18,7 @@
<EmojiPicker <EmojiPicker
v-if="enableEmojiPicker" v-if="enableEmojiPicker"
ref="picker" ref="picker"
show-keep-open
:class="{ hide: !showPicker }" :class="{ hide: !showPicker }"
:enable-sticker-picker="enableStickerPicker" :enable-sticker-picker="enableStickerPicker"
class="emoji-picker-panel" class="emoji-picker-panel"

View file

@ -1,12 +1,13 @@
import { defineAsyncComponent } from 'vue' import { defineAsyncComponent } from 'vue'
import Checkbox from '../checkbox/checkbox.vue' import Checkbox from '../checkbox/checkbox.vue'
import EmojiGrid from '../emoji_grid/emoji_grid.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
faBoxOpen, faBoxOpen,
faStickyNote, faStickyNote,
faSmileBeam faSmileBeam
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { trim, escapeRegExp, startCase } from 'lodash' import { trim, escapeRegExp, startCase, debounce } from 'lodash'
library.add( library.add(
faBoxOpen, faBoxOpen,
@ -14,19 +15,17 @@ library.add(
faSmileBeam faSmileBeam
) )
// At widest, approximately 20 emoji are visible in a row,
// loading 3 rows, could be overkill for narrow picker
const LOAD_EMOJI_BY = 60
// When to start loading new batch emoji, in pixels
const LOAD_EMOJI_MARGIN = 64
const EmojiPicker = { const EmojiPicker = {
props: { props: {
enableStickerPicker: { enableStickerPicker: {
required: false, required: false,
type: Boolean, type: Boolean,
default: false default: false
},
showKeepOpen: {
required: false,
type: Boolean,
default: false
} }
}, },
data () { data () {
@ -34,18 +33,18 @@ const EmojiPicker = {
keyword: '', keyword: '',
activeGroup: 'standard', activeGroup: 'standard',
showingStickers: false, showingStickers: false,
groupsScrolledClass: 'scrolled-top', keepOpen: false
keepOpen: false,
customEmojiBufferSlice: LOAD_EMOJI_BY,
customEmojiTimeout: null,
customEmojiLoadAllConfirmed: false
} }
}, },
components: { components: {
StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')), StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')),
Checkbox Checkbox,
EmojiGrid
}, },
methods: { methods: {
debouncedSearch: debounce(function (e) {
this.keyword = e.target.value
}, 500),
onStickerUploaded (e) { onStickerUploaded (e) {
this.$emit('sticker-uploaded', e) this.$emit('sticker-uploaded', e)
}, },
@ -55,78 +54,21 @@ const EmojiPicker = {
onEmoji (emoji) { onEmoji (emoji) {
const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement
this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen }) this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen })
this.$store.commit('emojiUsed', emoji)
}, },
onScroll (e) { onWheel (e) {
const target = (e && e.target) || this.$refs['emoji-groups'] e.preventDefault()
this.updateScrolledClass(target) this.$refs['emoji-tabs'].scrollBy(e.deltaY, 0)
this.scrolledGroup(target)
this.triggerLoadMore(target)
}, },
highlight (key) { highlight (key) {
this.setShowStickers(false) this.setShowStickers(false)
this.activeGroup = key this.activeGroup = key
}, if (this.keyword.length) {
updateScrolledClass (target) { this.$refs.emojiGrid.scrollToItem(key)
if (target.scrollTop <= 5) {
this.groupsScrolledClass = 'scrolled-top'
} else if (target.scrollTop >= target.scrollTopMax - 5) {
this.groupsScrolledClass = 'scrolled-bottom'
} else {
this.groupsScrolledClass = 'scrolled-middle'
} }
}, },
triggerLoadMore (target) { onActiveGroup (group) {
const ref = this.$refs['group-end-custom'] this.activeGroup = group
if (!ref) return
const bottom = ref.offsetTop + ref.offsetHeight
const scrollerBottom = target.scrollTop + target.clientHeight
const scrollerTop = target.scrollTop
const scrollerMax = target.scrollHeight
// Loads more emoji when they come into view
const approachingBottom = bottom - scrollerBottom < LOAD_EMOJI_MARGIN
// Always load when at the very top in case there's no scroll space yet
const atTop = scrollerTop < 5
// Don't load when looking at unicode category or at the very bottom
const bottomAboveViewport = bottom < scrollerTop || scrollerBottom === scrollerMax
if (!bottomAboveViewport && (approachingBottom || atTop)) {
this.loadEmoji()
}
},
scrolledGroup (target) {
const top = target.scrollTop + 5
this.$nextTick(() => {
this.emojisView.forEach(group => {
const ref = this.$refs['group-' + group.id]
if (ref.offsetTop <= top) {
this.activeGroup = group.id
}
})
})
},
loadEmoji () {
const allLoaded = this.customEmojiBuffer.length === this.filteredEmoji.length
if (allLoaded) {
return
}
this.customEmojiBufferSlice += LOAD_EMOJI_BY
},
startEmojiLoad (forceUpdate = false) {
if (!forceUpdate) {
this.keyword = ''
}
this.$nextTick(() => {
this.$refs['emoji-groups'].scrollTop = 0
})
const bufferSize = this.customEmojiBuffer.length
const bufferPrefilledAll = bufferSize === this.filteredEmoji.length
if (bufferPrefilledAll && !forceUpdate) {
return
}
this.customEmojiBufferSlice = LOAD_EMOJI_BY
}, },
toggleStickers () { toggleStickers () {
this.showingStickers = !this.showingStickers this.showingStickers = !this.showingStickers
@ -138,36 +80,16 @@ const EmojiPicker = {
if (this.keyword === '') return list if (this.keyword === '') return list
const regex = new RegExp(escapeRegExp(trim(this.keyword)), 'i') const regex = new RegExp(escapeRegExp(trim(this.keyword)), 'i')
return list.filter(emoji => { return list.filter(emoji => {
return regex.test(emoji.displayText) return (regex.test(emoji.displayText) || (!emoji.imageUrl && emoji.replacement === this.keyword))
}) })
} }
}, },
watch: {
keyword () {
this.customEmojiLoadAllConfirmed = false
this.onScroll()
this.startEmojiLoad(true)
}
},
computed: { computed: {
activeGroupView () { activeGroupView () {
return this.showingStickers ? '' : this.activeGroup return this.showingStickers ? '' : this.activeGroup
}, },
stickersAvailable () {
if (this.$store.state.instance.stickers) {
return this.$store.state.instance.stickers.length > 0
}
return 0
},
filteredEmoji () {
return this.filterByKeyword(
this.$store.state.instance.customEmoji || []
)
},
customEmojiBuffer () {
return this.filteredEmoji.slice(0, this.customEmojiBufferSlice)
},
emojis () { emojis () {
const recentEmojis = this.$store.getters.recentEmojis
const standardEmojis = this.$store.state.instance.emoji || [] const standardEmojis = this.$store.state.instance.emoji || []
const customEmojis = this.sortedEmoji const customEmojis = this.sortedEmoji
const emojiPacks = [] const emojiPacks = []
@ -180,6 +102,15 @@ const EmojiPicker = {
}) })
}) })
return [ return [
{
id: 'recent',
text: this.$t('emoji.recent'),
first: {
imageUrl: '',
replacement: '🕒',
},
emojis: this.filterByKeyword(recentEmojis)
},
{ {
id: 'standard', id: 'standard',
text: this.$t('emoji.unicode'), text: this.$t('emoji.unicode'),

View file

@ -1,5 +1,16 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
// The worst query selector ever
// selects ONLY emojis pickers in replies in notifications
// who thought this was a good idea?
.notification > .Status > .status-container > .post-status-form > form > .form-group > .emoji-input > .emoji-picker {
max-width: 100%;
left: 0;
@media (min-width: 1300px) {
left: -30px;
}
}
.Notification { .Notification {
.emoji-picker { .emoji-picker {
min-width: 160%; min-width: 160%;
@ -7,7 +18,7 @@
overflow: hidden; overflow: hidden;
left: -70%; left: -70%;
max-width: 100%; max-width: 100%;
@media (min-width: 800px) and (max-width: 1300px) { @media (min-width: 800px) and (max-width: 1280px) {
left: -50%; left: -50%;
min-width: 50%; min-width: 50%;
max-width: 130%; max-width: 130%;
@ -18,6 +29,10 @@
min-width: 50%; min-width: 50%;
max-width: 130%; max-width: 130%;
} }
.Status > .emoji-picker {
z-index: 1000;
}
} }
} }
.emoji-picker { .emoji-picker {
@ -70,10 +85,6 @@
flex-grow: 1; flex-grow: 1;
} }
.emoji-groups {
min-height: 200px;
}
.additional-tabs { .additional-tabs {
border-left: 1px solid; border-left: 1px solid;
border-left-color: $fallback--icon; border-left-color: $fallback--icon;
@ -152,76 +163,12 @@
} }
} }
.emoji { .emoji-search {
&-search { padding: 5px;
padding: 5px; flex: 0 0 auto;
flex: 0 0 auto;
input { input {
width: 100%; width: 100%;
}
} }
&-groups {
flex: 1 1 1px;
position: relative;
overflow: auto;
user-select: none;
mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat,
linear-gradient(to bottom, white 0, transparent 100%) top no-repeat,
linear-gradient(to top, white, white);
transition: mask-size 150ms;
mask-size: 100% 20px, 100% 20px, auto;
// Autoprefixed seem to ignore this one, and also syntax is different
-webkit-mask-composite: xor;
mask-composite: exclude;
&.scrolled {
&-top {
mask-size: 100% 20px, 100% 0, auto;
}
&-bottom {
mask-size: 100% 0, 100% 20px, auto;
}
}
}
&-group {
display: flex;
align-items: center;
flex-wrap: wrap;
padding-left: 5px;
justify-content: left;
&-title {
font-size: 0.85em;
width: 100%;
margin: 0;
&.disabled {
display: none;
}
}
}
&-item {
width: 32px;
height: 32px;
box-sizing: border-box;
display: flex;
font-size: 32px;
align-items: center;
justify-content: center;
margin: 4px;
cursor: pointer;
img {
object-fit: contain;
max-width: 100%;
max-height: 100%;
}
}
} }
} }

View file

@ -1,7 +1,11 @@
<template> <template>
<div class="emoji-picker panel panel-default panel-body"> <div class="emoji-picker panel panel-default panel-body">
<div class="heading"> <div class="heading">
<span class="emoji-tabs"> <span
ref="emoji-tabs"
class="emoji-tabs"
@wheel="onWheel"
>
<span <span
v-for="group in emojis" v-for="group in emojis"
:key="group.id" :key="group.id"
@ -40,47 +44,22 @@
> >
<div class="emoji-search"> <div class="emoji-search">
<input <input
v-model="keyword"
type="text" type="text"
class="form-control" class="form-control"
:placeholder="$t('emoji.search_emoji')" :placeholder="$t('emoji.search_emoji')"
@input="$event.target.composing = false" @input="debouncedSearch"
> >
</div> </div>
<EmojiGrid
ref="emojiGrid"
:groups="emojisView"
@emoji="onEmoji"
@active-group="onActiveGroup"
/>
<div <div
ref="emoji-groups" v-if="showKeepOpen"
class="emoji-groups" class="keep-open"
:class="groupsScrolledClass"
@scroll="onScroll"
> >
<div
v-for="group in emojisView"
:key="group.id"
class="emoji-group"
>
<h6
:ref="'group-' + group.id"
class="emoji-group-title"
>
{{ group.text }}
</h6>
<span
v-for="emoji in group.emojis"
:key="group.id + emoji.displayText"
:title="emoji.displayText"
class="emoji-item"
@click.stop.prevent="onEmoji(emoji)"
>
<span v-if="!emoji.imageUrl">{{ emoji.replacement }}</span>
<img
v-else
:src="emoji.imageUrl"
>
</span>
<span :ref="'group-end-' + group.id" />
</div>
</div>
<div class="keep-open">
<Checkbox v-model="keepOpen"> <Checkbox v-model="keepOpen">
{{ $t('emoji.keep_open') }} {{ $t('emoji.keep_open') }}
</Checkbox> </Checkbox>

View file

@ -1,13 +1,20 @@
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import UserListPopover from '../user_list_popover/user_list_popover.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue'
import StillImage from '../still-image/still-image.vue'
const EMOJI_REACTION_COUNT_CUTOFF = 12 const EMOJI_REACTION_COUNT_CUTOFF = 12
const findEmojiByReplacement = (state, replacement) => {
const allEmojis = state.instance.emoji.concat(state.instance.customEmoji)
return allEmojis.find(emoji => emoji.replacement === replacement)
}
const EmojiReactions = { const EmojiReactions = {
name: 'EmojiReactions', name: 'EmojiReactions',
components: { components: {
UserAvatar, UserAvatar,
UserListPopover UserListPopover,
StillImage
}, },
props: ['status'], props: ['status'],
data: () => ({ data: () => ({
@ -54,6 +61,8 @@ const EmojiReactions = {
}, },
reactWith (emoji) { reactWith (emoji) {
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
const emojiObject = findEmojiByReplacement(this.$store.state, emoji)
this.$store.commit('emojiUsed', emojiObject)
}, },
unreact (emoji) { unreact (emoji) {
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })

View file

@ -14,12 +14,13 @@
<span <span
v-if="reaction.url !== null" v-if="reaction.url !== null"
> >
<img <StillImage
:src="reaction.url" :src="reaction.url"
:title="reaction.name" :title="reaction.name"
:alt="reaction.name"
class="reaction-emoji" class="reaction-emoji"
width="2.55em" height="2.55em"
> />
{{ reaction.count }} {{ reaction.count }}
</span> </span>
<span v-else> <span v-else>
@ -49,6 +50,7 @@
display: flex; display: flex;
margin-top: 0.25em; margin-top: 0.25em;
flex-wrap: wrap; flex-wrap: wrap;
container-type: inline-size;
} }
.unicode-emoji { .unicode-emoji {
@ -64,7 +66,9 @@
justify-content: center; justify-content: center;
box-sizing: border-box; box-sizing: border-box;
.reaction-emoji { .reaction-emoji {
width: 2.55em !important; width: auto;
max-width: 96cqw;
height: 2.55em !important;
margin-right: 0.25em; margin-right: 0.25em;
} }
&:focus { &:focus {

View file

@ -15,6 +15,7 @@ import {
faBookmark as faBookmarkReg, faBookmark as faBookmarkReg,
faFlag faFlag
} from '@fortawesome/free-regular-svg-icons' } from '@fortawesome/free-regular-svg-icons'
import { mapState } from 'vuex'
library.add( library.add(
faEllipsisH, faEllipsisH,
@ -135,17 +136,26 @@ const ExtraButtons = {
}, },
doRedraftStatus () { doRedraftStatus () {
this.$store.dispatch('fetchStatusSource', { id: this.status.id }) this.$store.dispatch('fetchStatusSource', { id: this.status.id })
.then(data => this.$store.dispatch('openPostStatusModal', { .then(data => {
isRedraft: true, let repliedUserId = this.status.in_reply_to_user_id;
statusId: this.status.id, let repliedUser = this.status.attentions.filter(user =>
subject: data.spoiler_text, user.id === repliedUserId);
statusText: data.text, this.$store.dispatch('openPostStatusModal', {
statusIsSensitive: this.status.nsfw, isRedraft: true,
statusPoll: this.status.poll, attentions: this.status.attentions,
statusFiles: [...this.status.attachments], statusId: this.status.id,
statusScope: this.status.visibility, subject: data.spoiler_text,
statusContentType: data.content_type statusText: data.text,
})) statusIsSensitive: this.status.nsfw,
statusPoll: this.status.poll,
statusFiles: [...this.status.attachments],
statusScope: this.status.visibility,
statusLanguage: this.status.language,
statusContentType: data.content_type,
replyTo: this.status.in_reply_to_status_id,
repliedUser: repliedUser
})
})
this.doDeleteStatus() this.doDeleteStatus()
}, },
showRedraftStatusConfirmDialog () { showRedraftStatusConfirmDialog () {
@ -190,7 +200,7 @@ const ExtraButtons = {
isEdited () { isEdited () {
return this.status.edited_at !== null return this.status.edited_at !== null
}, },
editingAvailable () { return this.$store.state.instance.editingAvailable } editingAvailable () { return this.$store.state.instance.editingAvailable },
} }
} }

View file

@ -55,6 +55,9 @@
.interactive { .interactive {
.svg-inline--fa { .svg-inline--fa {
@media (prefers-reduced-motion: reduce) {
animation: unset;
}
animation-duration: 0.6s; animation-duration: 0.6s;
} }

View file

@ -43,6 +43,7 @@ const FollowRequestCard = {
doApprove () { doApprove () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('decrementFollowRequestsCount')
const notifId = this.findFollowRequestNotificationId() const notifId = this.findFollowRequestNotificationId()
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId }) this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
@ -66,6 +67,7 @@ const FollowRequestCard = {
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => { .then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: notifId }) this.$store.dispatch('dismissNotificationLocal', { id: notifId })
this.$store.dispatch('decrementFollowRequestsCount')
this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('removeFollowRequest', this.user)
}) })
this.hideDenyConfirmDialog() this.hideDenyConfirmDialog()
@ -80,6 +82,11 @@ const FollowRequestCard = {
}, },
shouldConfirmDeny () { shouldConfirmDeny () {
return this.mergedConfig.modalOnDenyFollow return this.mergedConfig.modalOnDenyFollow
},
show () {
const notifId = this.$store.state.api.followRequests.find(req => req.id === this.user.id)
return notifId !== undefined
} }
} }
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<basic-user-card :user="user"> <basic-user-card :user="user" v-if="show">
<div class="follow-request-card-content-container"> <div class="follow-request-card-content-container">
<button <button
class="btn button-default" class="btn button-default"

View file

@ -1,10 +1,26 @@
import FollowRequestCard from '../follow_request_card/follow_request_card.vue' import FollowRequestCard from '../follow_request_card/follow_request_card.vue'
import withLoadMore from '../../hocs/with_load_more/with_load_more'
import List from '../list/list.vue'
import get from 'lodash/get'
const FollowRequestList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchFollowRequests'),
select: (props, $store) => get($store.state.api, 'followRequests', []).map(req => $store.getters.findUser(req.id)),
destroy: (props, $store) => $store.dispatch('clearFollowRequests'),
childPropName: 'items',
additionalPropNames: ['userId']
})(List);
const FollowRequests = { const FollowRequests = {
components: { components: {
FollowRequestCard FollowRequestCard,
FollowRequestList
}, },
computed: { computed: {
userId () {
return this.$store.state.users.currentUser.id
},
requests () { requests () {
return this.$store.state.api.followRequests return this.$store.state.api.followRequests
} }

View file

@ -6,12 +6,11 @@
</div> </div>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<FollowRequestCard <FollowRequestList :user-id="userId">
v-for="request in requests" <template #item="{item}">
:key="request.id" <FollowRequestCard :user="item" />
:user="request" </template>
class="list-item" </FollowRequestList>
/>
</div> </div>
</div> </div>
</template> </template>

View file

@ -0,0 +1,77 @@
<template>
<div class="followed-tag-card">
<span>
<router-link :to="{ name: 'tag-timeline', params: {tag: tag.name}}">
<span class="tag-link">#{{ tag.name }}</span>
</router-link>
<span class="unfollow-tag">
<button
v-if="isFollowing"
class="button-default unfollow-tag-button"
:title="$t('user_card.unfollow_tag')"
@click="unfollowTag(tag.name)"
>
{{ $t('user_card.unfollow_tag') }}
</button>
<button
v-else
class="button-default follow-tag-button"
:title="$t('user_card.follow_tag')"
@click="followTag(tag.name)"
>
{{ $t('user_card.follow_tag') }}
</button>
</span>
</span>
</div>
</template>
<script>
export default {
name: 'FollowedTagCard',
props: {
tag: {
type: Object,
required: true
},
},
// this is a hack to update the state of the button
// for some reason, List does not update on changes to the tag object
data: () => ({
isFollowing: true
}),
mounted () {
this.isFollowing = this.tag.following
},
methods: {
unfollowTag (tag) {
this.$store.dispatch('unfollowTag', tag)
this.isFollowing = false
},
followTag (tag) {
this.$store.dispatch('followTag', tag)
this.isFollowing = true
}
}
}
</script>
<style scoped>
.followed-tag-card {
margin-left: 1rem;
margin-top: 1rem;
margin-bottom: 1rem;
}
.unfollow-tag {
position: absolute;
right: 1rem;
}
.tag-link {
font-size: large;
}
.unfollow-tag-button, .follow-tag-button {
font-size: medium;
}
</style>

View file

@ -1,9 +1,13 @@
<template> <template>
<div class="list"> <div
class="list"
role="list"
>
<div <div
v-for="item in items" v-for="item in items"
:key="getKey(item)" :key="getKey(item)"
class="list-item" class="list-item"
role="listitem"
> >
<slot <slot
name="item" name="item"

View file

@ -93,9 +93,6 @@ const MentionLink = {
this.highlightType this.highlightType
] ]
}, },
useAtIcon () {
return this.mergedConfig.useAtIcon
},
isRemote () { isRemote () {
return this.userName !== this.userNameFull return this.userName !== this.userNameFull
}, },

View file

@ -157,6 +157,9 @@
box-shadow: var(--panelShadow); box-shadow: var(--panelShadow);
transition-property: transform; transition-property: transform;
transition-duration: 0.25s; transition-duration: 0.25s;
@media (prefers-reduced-motion: reduce) {
transition: unset;
}
transform: translateX(0); transform: translateX(0);
z-index: 1001; z-index: 1001;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;

View file

@ -33,11 +33,6 @@ library.add(
) )
const NavPanel = { const NavPanel = {
created () {
if (this.currentUser && this.currentUser.locked) {
this.$store.dispatch('startFetchingFollowRequests')
}
},
components: { components: {
TimelineMenuContent TimelineMenuContent
}, },
@ -54,11 +49,13 @@ const NavPanel = {
computed: { computed: {
...mapState({ ...mapState({
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
followRequestCount: state => state.api.followRequests.length,
privateMode: state => state.instance.private, privateMode: state => state.instance.private,
federating: state => state.instance.federating federating: state => state.instance.federating,
}), }),
...mapGetters(['unreadAnnouncementCount']) ...mapGetters(['unreadAnnouncementCount']),
followRequestCount () {
return this.$store.state.users.currentUser.follow_requests_count
}
} }
} }

View file

@ -151,7 +151,6 @@
> >
<Timeago <Timeago
:time="notification.created_at" :time="notification.created_at"
:with-direction="true"
:auto-update="240" :auto-update="240"
/> />
</router-link> </router-link>

View file

@ -105,9 +105,12 @@
flex: 1; flex: 1;
padding-left: 0.8em; padding-left: 0.8em;
min-width: 0; min-width: 0;
}
.heading-right, .notification-right {
.timeago { .timeago {
min-width: 3em; display: inline-block;
min-width: 6em;
text-align: right; text-align: right;
} }
} }

View file

@ -1,4 +1,4 @@
import PinchZoom from '@kazvmoe-infra/pinch-zoom-element' import PinchZoom from '@floatingghost/pinch-zoom-element'
export default { export default {
methods: { methods: {

View file

@ -103,9 +103,9 @@ export default {
convertExpiryFromUnit (unit, amount) { convertExpiryFromUnit (unit, amount) {
// Note: we want seconds and not milliseconds // Note: we want seconds and not milliseconds
switch (unit) { switch (unit) {
case 'minutes': return 0.001 * amount * DateUtils.MINUTE case 'minutes': return amount * DateUtils.MINUTE / 1000
case 'hours': return 0.001 * amount * DateUtils.HOUR case 'hours': return amount * DateUtils.HOUR / 1000
case 'days': return 0.001 * amount * DateUtils.DAY case 'days': return amount * DateUtils.DAY / 1000
} }
}, },
expiryAmountChange () { expiryAmountChange () {

View file

@ -114,7 +114,7 @@
svg { svg {
width: 22px; width: 22px;
margin-right: 0.75rem; margin-right: 0.75rem;
color: var(--menuPopoverIcon, $fallback--icon) color: var(--popoverIcon, $fallback--icon)
} }
} }

View file

@ -13,6 +13,7 @@ import suggestor from '../emoji_input/suggestor.js'
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue' import Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue' import Select from '../select/select.vue'
import iso6391 from 'iso-639-1'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -53,6 +54,14 @@ const pxStringToNumber = (str) => {
return Number(str.substring(0, str.length - 2)) return Number(str.substring(0, str.length - 2))
} }
const deleteDraft = (draftKey) => {
const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
delete draftData[draftKey];
localStorage.setItem('drafts', JSON.stringify(draftData));
}
const PostStatusForm = { const PostStatusForm = {
props: [ props: [
'statusId', 'statusId',
@ -63,6 +72,7 @@ const PostStatusForm = {
'statusMediaDescriptions', 'statusMediaDescriptions',
'statusScope', 'statusScope',
'statusContentType', 'statusContentType',
'statusLanguage',
'replyTo', 'replyTo',
'quoteId', 'quoteId',
'repliedUser', 'repliedUser',
@ -128,7 +138,7 @@ const PostStatusForm = {
statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
} }
const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject } = this.$store.getters.mergedConfig const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage, alwaysShowSubjectInput } = this.$store.getters.mergedConfig
let statusParams = { let statusParams = {
spoilerText: this.subject || '', spoilerText: this.subject || '',
@ -139,6 +149,7 @@ const PostStatusForm = {
poll: {}, poll: {},
mediaDescriptions: {}, mediaDescriptions: {},
visibility: this.suggestedVisibility(), visibility: this.suggestedVisibility(),
language: interfaceLanguage,
contentType contentType
} }
@ -153,10 +164,45 @@ const PostStatusForm = {
poll: this.statusPoll || {}, poll: this.statusPoll || {},
mediaDescriptions: this.statusMediaDescriptions || {}, mediaDescriptions: this.statusMediaDescriptions || {},
visibility: this.statusScope || this.suggestedVisibility(), visibility: this.statusScope || this.suggestedVisibility(),
language: this.statusLanguage || interfaceLanguage,
contentType: statusContentType contentType: statusContentType
} }
} }
if (!this.statusId) {
let draftKey = 'status';
if (this.replyTo) {
draftKey = 'reply:' + this.replyTo;
} else if (this.quoteId) {
draftKey = 'quote:' + this.quoteId;
}
const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey];
if (draft) {
statusParams = {
spoilerText: draft.data.spoilerText,
status: draft.data.status,
sensitiveIfSubject,
nsfw: draft.data.nsfw,
files: draft.data.files,
poll: draft.data.poll,
mediaDescriptions: draft.data.mediaDescriptions,
visibility: draft.data.visibility,
language: draft.data.language,
contentType: draft.data.contentType
}
if (draft.data.poll) {
this.togglePollForm();
}
}
}
// When first loading the form, hide the subject (CW) field if it's disabled or doesn't have a starting value.
// "disableSubject" seems to take priority over "alwaysShowSubjectInput".
const showSubject = !this.disableSubject && (statusParams.spoilerText || alwaysShowSubjectInput)
return { return {
dropFiles: [], dropFiles: [],
uploadingFiles: false, uploadingFiles: false,
@ -171,7 +217,10 @@ const PostStatusForm = {
preview: null, preview: null,
previewLoading: false, previewLoading: false,
emojiInputShown: false, emojiInputShown: false,
idempotencyKey: '' idempotencyKey: '',
activeEmojiInput: undefined,
activeTextInput: undefined,
subjectVisible: showSubject
} }
}, },
computed: { computed: {
@ -259,7 +308,10 @@ const PostStatusForm = {
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState({ ...mapState({
mobileLayout: state => state.interface.mobileLayout mobileLayout: state => state.interface.mobileLayout
}) }),
isoLanguages () {
return iso6391.getAllCodes();
}
}, },
watch: { watch: {
'newStatus': { 'newStatus': {
@ -273,6 +325,7 @@ const PostStatusForm = {
statusChanged () { statusChanged () {
this.autoPreview() this.autoPreview()
this.updateIdempotencyKey() this.updateIdempotencyKey()
this.saveDraft()
}, },
clearStatus () { clearStatus () {
const newStatus = this.newStatus const newStatus = this.newStatus
@ -282,6 +335,7 @@ const PostStatusForm = {
files: [], files: [],
visibility: newStatus.visibility, visibility: newStatus.visibility,
contentType: newStatus.contentType, contentType: newStatus.contentType,
language: newStatus.language,
poll: {}, poll: {},
mediaDescriptions: {} mediaDescriptions: {}
} }
@ -341,6 +395,7 @@ const PostStatusForm = {
inReplyToStatusId: this.replyTo, inReplyToStatusId: this.replyTo,
quoteId: this.quoteId, quoteId: this.quoteId,
contentType: newStatus.contentType, contentType: newStatus.contentType,
language: newStatus.language,
poll, poll,
idempotencyKey: this.idempotencyKey idempotencyKey: this.idempotencyKey
} }
@ -375,6 +430,7 @@ const PostStatusForm = {
inReplyToStatusId: this.replyTo, inReplyToStatusId: this.replyTo,
quoteId: this.quoteId, quoteId: this.quoteId,
contentType: newStatus.contentType, contentType: newStatus.contentType,
language: newStatus.language,
poll: {}, poll: {},
preview: true preview: true
}).then((data) => { }).then((data) => {
@ -391,8 +447,38 @@ const PostStatusForm = {
}).finally(() => { }).finally(() => {
this.previewLoading = false this.previewLoading = false
}) })
let draftKey = 'status';
if (this.replyTo) {
draftKey = 'reply:' + this.replyTo;
} else if (this.quoteId) {
draftKey = 'quote:' + this.quoteId;
}
deleteDraft(draftKey)
}, },
debouncePreviewStatus: debounce(function () { this.previewStatus() }, 500), debouncePreviewStatus: debounce(function () { this.previewStatus() }, 500),
saveDraft() {
const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
let draftKey = 'status';
if (this.replyTo) {
draftKey = 'reply:' + this.replyTo;
} else if (this.quoteId) {
draftKey = 'quote:' + this.quoteId;
}
if (this.newStatus.status || this.newStatus.spoilerText || this.newStatus.files.length > 0 || this.newStatus.poll.length > 0) {
draftData[draftKey] = {
updatedAt: new Date(),
data: this.newStatus,
};
localStorage.setItem('drafts', JSON.stringify(draftData));
} else {
deleteDraft(draftKey);
}
},
autoPreview () { autoPreview () {
if (!this.preview) return if (!this.preview) return
this.previewLoading = true this.previewLoading = true
@ -595,8 +681,33 @@ const PostStatusForm = {
this.$refs['emoji-input'].resize() this.$refs['emoji-input'].resize()
}, },
showEmojiPicker () { showEmojiPicker () {
this.$refs['textarea'].focus() if (!this.activeEmojiInput || !this.activeTextInput)
this.$refs['emoji-input'].triggerShowPicker() this.focusStatusInput()
this.$refs[this.activeTextInput].focus()
this.$refs[this.activeEmojiInput].triggerShowPicker()
},
focusStatusInput() {
this.activeEmojiInput = 'emoji-input'
this.activeTextInput = 'textarea'
},
focusSubjectInput() {
this.activeEmojiInput = 'subject-emoji-input'
this.activeTextInput = 'subject-input'
},
toggleSubjectVisible() {
// If hiding CW, then we need to clear the subject and reset focus
if (this.subjectVisible)
{
this.focusStatusInput()
// "nsfw" property is normally set by the @change listener, but this bypasses it.
// We need to clear it manually instead.
this.newStatus.spoilerText = ''
this.newStatus.nsfw = false
}
this.subjectVisible = !this.subjectVisible
}, },
clearError () { clearError () {
this.error = null this.error = null

View file

@ -118,13 +118,16 @@
/> />
</div> </div>
<EmojiInput <EmojiInput
v-if="!disableSubject && (newStatus.spoilerText || alwaysShowSubject)" ref="subject-emoji-input"
v-if="subjectVisible"
v-model="newStatus.spoilerText" v-model="newStatus.spoilerText"
enable-emoji-picker enable-emoji-picker
hide-emoji-button
:suggest="emojiSuggestor" :suggest="emojiSuggestor"
class="form-control" class="form-control"
> >
<input <input
ref="subject-input"
v-model="newStatus.spoilerText" v-model="newStatus.spoilerText"
type="text" type="text"
:placeholder="$t('post_status.content_warning')" :placeholder="$t('post_status.content_warning')"
@ -132,6 +135,7 @@
size="1" size="1"
class="form-post-subject" class="form-post-subject"
@input="onSubjectInput" @input="onSubjectInput"
@focus="focusSubjectInput()"
> >
</EmojiInput> </EmojiInput>
<i18n-t <i18n-t
@ -173,6 +177,7 @@
@input="resize" @input="resize"
@compositionupdate="resize" @compositionupdate="resize"
@paste="paste" @paste="paste"
@focus="focusStatusInput()"
/> />
<p <p
v-if="hasStatusLengthLimit" v-if="hasStatusLengthLimit"
@ -194,6 +199,23 @@
:on-scope-change="changeVis" :on-scope-change="changeVis"
/> />
<div
class="language-selector"
>
<Select
id="post-language"
v-model="newStatus.language"
class="form-control"
>
<option
v-for="language in isoLanguages"
:key="language"
:value="language"
>
{{ language }}
</option>
</Select>
</div>
<div <div
v-if="postFormats.length > 1" v-if="postFormats.length > 1"
class="text-format" class="text-format"
@ -259,6 +281,15 @@
> >
<FAIcon icon="poll-h" /> <FAIcon icon="poll-h" />
</button> </button>
<button
v-if="!disableSubject"
class="spoiler-icon button-unstyled"
:class="{ selected: subjectVisible }"
:title="$t('post_status.toggle_content_warning')"
@click="toggleSubjectVisible"
>
<FAIcon icon="eye-slash" />
</button>
</div> </div>
<button <button
v-if="posting" v-if="posting"
@ -274,12 +305,14 @@
> >
{{ $t('post_status.post') }} {{ $t('post_status.post') }}
</button> </button>
<!-- touchstart is used to keep the OSK at the same position after a message send --> <!-- To keep the OSK at the same position after a message send, -->
<!-- @touchstart.stop.prevent was used. But while OSK position is -->
<!-- quirky, accidental mobile posts caused by the workaround -->
<!-- when people tried to scroll were a more serious bug. -->
<button <button
v-else v-else
:disabled="uploadingFiles || disableSubmit" :disabled="uploadingFiles || disableSubmit"
class="btn button-default" class="btn button-default"
@touchstart.stop.prevent="postStatus($event, newStatus)"
@click.stop.prevent="postStatus($event, newStatus)" @click.stop.prevent="postStatus($event, newStatus)"
> >
{{ $t('post_status.post') }} {{ $t('post_status.post') }}
@ -437,7 +470,7 @@
} }
} }
.media-upload-icon, .poll-icon, .emoji-icon { .media-upload-icon, .poll-icon, .emoji-icon, .spoiler-icon {
font-size: 1.85em; font-size: 1.85em;
line-height: 1.1; line-height: 1.1;
flex: 1; flex: 1;
@ -480,6 +513,11 @@
.poll-icon { .poll-icon {
order: 3; order: 3;
justify-content: center;
}
.spoiler-icon {
order: 4;
justify-content: right; justify-content: right;
} }

View file

@ -79,8 +79,16 @@ const registration = {
if (!this.v$.$invalid) { if (!this.v$.$invalid) {
try { try {
await this.signUp(this.user) const data = await this.signUp(this.user)
this.$router.push({ name: 'friends' }) if (data.me) {
this.$router.push({ name: 'friends' })
} else if (data.identifier === 'awaiting_approval') {
this.$router.push({ name: 'registration-request-sent' })
} else if (data.identifier === 'missing_confirmed_email') {
this.$router.push({ name: 'awaiting-email-confirmation' })
} else {
console.warn('Unknown response from sign up', data)
}
} catch (error) { } catch (error) {
console.warn('Registration failed: ', error) console.warn('Registration failed: ', error)
this.setCaptcha() this.setCaptcha()

View file

@ -177,6 +177,7 @@
<div <div
v-if="accountApprovalRequired" v-if="accountApprovalRequired"
class="form-group" class="form-group"
:class="{ 'form-group--error': v$.user.reason.$error }"
> >
<label <label
class="form--label" class="form--label"

View file

@ -0,0 +1,4 @@
export default {
computed: {
}
}

View file

@ -0,0 +1,12 @@
<template>
<div class="panel panel-default">
<div class="panel-heading">
<h4>{{ $t('registration.request_sent_title') }}</h4>
</div>
<div class="panel-body">
<p>{{ $t('registration.request_sent') }}</p>
</div>
</div>
</template>
<script src="./registration_request_sent.js"></script>

View file

@ -74,6 +74,9 @@
.interactive { .interactive {
.svg-inline--fa { .svg-inline--fa {
@media (prefers-reduced-motion: reduce) {
animation: unset;
}
animation-duration: 0.6s; animation-duration: 0.6s;
} }

View file

@ -2,8 +2,6 @@ import { unescape, flattenDeep } from 'lodash'
import { getTagName, processTextForEmoji, getAttrs } from 'src/services/html_converter/utility.service.js' import { getTagName, processTextForEmoji, getAttrs } from 'src/services/html_converter/utility.service.js'
import { convertHtmlToTree } from 'src/services/html_converter/html_tree_converter.service.js' import { convertHtmlToTree } from 'src/services/html_converter/html_tree_converter.service.js'
import { convertHtmlToLines } from 'src/services/html_converter/html_line_converter.service.js' import { convertHtmlToLines } from 'src/services/html_converter/html_line_converter.service.js'
import { marked } from 'marked'
import markedMfm from 'marked-mfm'
import StillImage from 'src/components/still-image/still-image.vue' import StillImage from 'src/components/still-image/still-image.vue'
import MentionsLine, { MENTIONS_LIMIT } from 'src/components/mentions_line/mentions_line.vue' import MentionsLine, { MENTIONS_LIMIT } from 'src/components/mentions_line/mentions_line.vue'
import HashtagLink from 'src/components/hashtag_link/hashtag_link.vue' import HashtagLink from 'src/components/hashtag_link/hashtag_link.vue'
@ -190,7 +188,7 @@ export default {
break break
} }
case 'span': case 'span':
if (this.handleLinks && attrs['class'] && attrs['class'].includes('h-card')) { if (this.handleLinks && attrs?.['class']?.includes?.('h-card')) {
return ['', children.map(processItem), ''] return ['', children.map(processItem), '']
} }
} }

View file

@ -50,7 +50,6 @@
.emoji { .emoji {
display: inline-block; display: inline-block;
width: var(--emoji-size, 32px);
height: var(--emoji-size, 32px); height: var(--emoji-size, 32px);
} }

View file

@ -16,7 +16,6 @@
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
/> />
</button> </button>
{{ ' ' }}
<button <button
v-if="showPrivate" v-if="showPrivate"
class="button-unstyled scope" class="button-unstyled scope"
@ -30,7 +29,6 @@
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
/> />
</button> </button>
{{ ' ' }}
<button <button
v-if="showUnlisted" v-if="showUnlisted"
class="button-unstyled scope" class="button-unstyled scope"
@ -44,7 +42,6 @@
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
/> />
</button> </button>
{{ ' ' }}
<button <button
v-if="showPublic" v-if="showPublic"
class="button-unstyled scope" class="button-unstyled scope"
@ -87,6 +84,7 @@
min-width: 1.3em; min-width: 1.3em;
min-height: 1.3em; min-height: 1.3em;
text-align: center; text-align: center;
margin-right: 0.4em;
&.selected svg { &.selected svg {
color: $fallback--lightText; color: $fallback--lightText;

View file

@ -73,6 +73,7 @@
.search-bar-input { .search-bar-input {
flex: 1 0 auto; flex: 1 0 auto;
margin-left: 0.5em;
} }
.cancel-search { .cancel-search {

View file

@ -38,7 +38,7 @@ label.Select {
margin: 0; margin: 0;
padding: 0 2em 0 .2em; padding: 0 2em 0 .2em;
font-family: sans-serif; font-family: sans-serif;
font-family: var(--inputFont, sans-serif); font-family: var(--interfaceFont, sans-serif);
font-size: 1em; font-size: 1em;
width: 100%; width: 100%;
z-index: 1; z-index: 1;

View file

@ -76,6 +76,10 @@
position: absolute; position: absolute;
right: 20px; right: 20px;
padding-right: 10px; padding-right: 10px;
@media all and (max-width: 800px) {
display: none;
}
} }
} }
} }

View file

@ -44,6 +44,10 @@
<div class="panel-body"> <div class="panel-body">
<SettingsModalContent v-if="modalOpenedOnce" /> <SettingsModalContent v-if="modalOpenedOnce" />
</div> </div>
<span
id="unscrolled-content"
class="extra-content"
/>
<div class="panel-footer settings-footer"> <div class="panel-footer settings-footer">
<Popover <Popover
class="export" class="export"
@ -53,7 +57,7 @@
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
remove-padding remove-padding
> >
<template v-slot:trigger> <template #trigger>
<button <button
class="btn button-default" class="btn button-default"
:title="$t('general.close')" :title="$t('general.close')"
@ -65,7 +69,7 @@
/> />
</button> </button>
</template> </template>
<template v-slot:content="{close}"> <template #content="{close}">
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
class="button-default dropdown-item dropdown-item-icon" class="button-default dropdown-item dropdown-item-icon"
@ -103,14 +107,11 @@
<Checkbox <Checkbox
:model-value="!!expertLevel" :model-value="!!expertLevel"
class="expertMode"
@update:modelValue="expertLevel = Number($event)" @update:modelValue="expertLevel = Number($event)"
> >
{{ $t("settings.expert_mode") }} {{ $t("settings.expert_mode") }}
</Checkbox> </Checkbox>
<span
id="unscrolled-content"
class="extra-content"
/>
<button <button
v-if="currentUser" v-if="currentUser"
class="button-default logout-button" class="button-default logout-button"

View file

@ -21,7 +21,6 @@
> >
{{ $t('settings.settings_profile_force_sync') }} {{ $t('settings.settings_profile_force_sync') }}
</button> </button>
</p> </p>
<div <div
@click="toggleExpandedSettings" @click="toggleExpandedSettings"
@ -147,6 +146,11 @@
{{ $t('settings.show_wider_shortcuts') }} {{ $t('settings.show_wider_shortcuts') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="displayPageBackgrounds">
{{ $t('settings.show_page_backgrounds') }}
</BooleanSetting>
</li>
<li> <li>
<BooleanSetting path="stopGifs"> <BooleanSetting path="stopGifs">
{{ $t('settings.stop_gifs') }} {{ $t('settings.stop_gifs') }}
@ -407,6 +411,15 @@
{{ $t('settings.preload_images') }} {{ $t('settings.preload_images') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting
path="useBlurhash"
expert="1"
:disabled="!hideNsfw"
>
{{ $t('settings.use_blurhash') }}
</BooleanSetting>
</li>
<li> <li>
<BooleanSetting <BooleanSetting
path="useOneClickNsfw" path="useOneClickNsfw"
@ -475,14 +488,6 @@
</BooleanSetting> </BooleanSetting>
</li> </li>
</ul> </ul>
<li>
<BooleanSetting
path="useAtIcon"
expert="1"
>
{{ $t('settings.use_at_icon') }}
</BooleanSetting>
</li>
<li> <li>
<BooleanSetting path="mentionLinkShowAvatar"> <BooleanSetting path="mentionLinkShowAvatar">
{{ $t('settings.mention_link_show_avatar') }} {{ $t('settings.mention_link_show_avatar') }}

View file

@ -10,17 +10,20 @@ import SelectableList from 'src/components/selectable_list/selectable_list.vue'
import ProgressButton from 'src/components/progress_button/progress_button.vue' import ProgressButton from 'src/components/progress_button/progress_button.vue'
import withSubscription from 'src/components/../hocs/with_subscription/with_subscription' import withSubscription from 'src/components/../hocs/with_subscription/with_subscription'
import Checkbox from 'src/components/checkbox/checkbox.vue' import Checkbox from 'src/components/checkbox/checkbox.vue'
import withLoadMore from 'src/components/../hocs/with_load_more/with_load_more'
const BlockList = withSubscription({ const BlockList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchBlocks'), fetch: (props, $store) => $store.dispatch('fetchBlocks'),
select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []), select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),
childPropName: 'items' childPropName: 'items',
destroy: () => {}
})(SelectableList) })(SelectableList)
const MuteList = withSubscription({ const MuteList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchMutes'), fetch: (props, $store) => $store.dispatch('fetchMutes'),
select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []), select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),
childPropName: 'items' childPropName: 'items',
destroy: () => {}
})(SelectableList) })(SelectableList)
const DomainMuteList = withSubscription({ const DomainMuteList = withSubscription({

View file

@ -12,6 +12,7 @@ import InterfaceLanguageSwitcher from 'src/components/interface_language_switche
import BooleanSetting from '../helpers/boolean_setting.vue' import BooleanSetting from '../helpers/boolean_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js' import SharedComputedObject from '../helpers/shared_computed_object.js'
import localeService from 'src/services/locale/locale.service.js' import localeService from 'src/services/locale/locale.service.js'
import ChoiceSetting from '../helpers/choice_setting.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -32,6 +33,7 @@ const ProfileTab = {
newName: this.$store.state.users.currentUser.name_unescaped, newName: this.$store.state.users.currentUser.name_unescaped,
newBio: unescape(this.$store.state.users.currentUser.description), newBio: unescape(this.$store.state.users.currentUser.description),
newLocked: this.$store.state.users.currentUser.locked, newLocked: this.$store.state.users.currentUser.locked,
newPermitFollowback: this.$store.state.users.currentUser.permit_followback,
newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })), newFields: this.$store.state.users.currentUser.fields.map(field => ({ name: field.name, value: field.value })),
showRole: this.$store.state.users.currentUser.show_role, showRole: this.$store.state.users.currentUser.show_role,
role: this.$store.state.users.currentUser.role, role: this.$store.state.users.currentUser.role,
@ -43,10 +45,19 @@ const ProfileTab = {
bannerPreview: null, bannerPreview: null,
background: null, background: null,
backgroundPreview: null, backgroundPreview: null,
emailLanguage: this.$store.state.users.currentUser.language || '' emailLanguage: this.$store.state.users.currentUser.language || '',
newPostTTLDays: this.$store.state.users.currentUser.status_ttl_days,
expirePosts: this.$store.state.users.currentUser.status_ttl_days !== null,
userAcceptsDirectMessagesFrom: this.$store.state.users.currentUser.accepts_direct_messages_from,
userAcceptsDirectMessagesFromOptions: ["everybody", "nobody", "people_i_follow"].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.user_accepts_direct_messages_from_${mode}`)
}))
} }
}, },
components: { components: {
ChoiceSetting,
ScopeSelector, ScopeSelector,
ImageCropper, ImageCropper,
EmojiInput, EmojiInput,
@ -123,7 +134,10 @@ const ProfileTab = {
display_name: this.newName, display_name: this.newName,
fields_attributes: this.newFields.filter(el => el != null), fields_attributes: this.newFields.filter(el => el != null),
bot: this.bot, bot: this.bot,
show_role: this.showRole show_role: this.showRole,
status_ttl_days: this.expirePosts ? this.newPostTTLDays : -1,
permit_followback: this.permit_followback,
accepts_direct_messages_from: this.userAcceptsDirectMessagesFrom
/* eslint-enable camelcase */ /* eslint-enable camelcase */
} }

View file

@ -4,6 +4,10 @@
margin: 0; margin: 0;
} }
.expire-posts-days {
margin-left: 1em;
}
.visibility-tray { .visibility-tray {
padding-top: 5px; padding-top: 5px;
} }

View file

@ -88,6 +88,32 @@
<Checkbox v-model="bot"> <Checkbox v-model="bot">
{{ $t('settings.bot') }} {{ $t('settings.bot') }}
</Checkbox> </Checkbox>
</p>
<p>
<ChoiceSetting
id="userAcceptsDirectMessagesFrom"
path="userAcceptsDirectMessagesFrom"
:options="userAcceptsDirectMessagesFromOptions"
>
{{ $t('settings.user_accepts_direct_messages_from') }}
</ChoiceSetting>
</p>
<p>
<Checkbox v-model="expirePosts">
{{ $t('settings.expire_posts_enabled') }}
</Checkbox>
<input
v-model="newPostTTLDays"
:disabled="!expirePosts"
type="number"
min="1"
max="730"
class="expire-posts-days"
:placeholder="$t('settings.expire_posts_input_placeholder')"
/>
</p>
<p>
</p> </p>
<p> <p>
<interface-language-switcher <interface-language-switcher
@ -233,6 +259,19 @@
<BooleanSetting path="serverSide_locked"> <BooleanSetting path="serverSide_locked">
{{ $t('settings.lock_account_description') }} {{ $t('settings.lock_account_description') }}
</BooleanSetting> </BooleanSetting>
<ul
class="setting-list suboptions"
:class="[{disabled: !serverSide_locked}]"
>
<li>
<BooleanSetting
path="serverSide_permitFollowback"
:disabled="!serverSide_locked"
>
{{ $t('settings.permit_followback_description') }}
</BooleanSetting>
</li>
</ul>
</li> </li>
<li> <li>
<BooleanSetting path="serverSide_discoverable"> <BooleanSetting path="serverSide_discoverable">

View file

@ -89,6 +89,10 @@
margin: 1em 1em 0; margin: 1em 1em 0;
} }
.presets {
text-align: center;
}
.tab-header { .tab-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -284,7 +288,6 @@
box-shadow: none; box-shadow: none;
background: transparent; background: transparent;
color: var(--faint, $fallback--faint); color: var(--faint, $fallback--faint);
align-self: stretch;
} }
.theme-color-cl, .theme-color-cl,
@ -318,11 +321,11 @@
.extra-content { .extra-content {
.apply-container { .apply-container {
padding-left: 15vw;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-around; justify-content: space-evenly;
flex-grow: 1; flex-grow: 1;
.btn { .btn {
flex-grow: 1; flex-grow: 1;
min-height: 2em; min-height: 2em;

View file

@ -958,20 +958,22 @@
v-if="isActive" v-if="isActive"
to="#unscrolled-content" to="#unscrolled-content"
> >
<div class="apply-container"> <div class="panel-body settings-footer">
<button <div class="apply-container">
class="btn button-default submit" <button
:disabled="!themeValid" class="btn button-default submit"
@click="setCustomTheme" :disabled="!themeValid"
> @click="setCustomTheme"
{{ $t('general.apply') }} >
</button> {{ $t('general.apply') }}
<button </button>
class="btn button-default" <button
@click="clearAll" class="btn button-default"
> @click="clearAll"
{{ $t('settings.style.switcher.reset') }} >
</button> {{ $t('settings.style.switcher.reset') }}
</button>
</div>
</div> </div>
</teleport> </teleport>
</div> </div>

View file

@ -268,6 +268,10 @@
.side-drawer { .side-drawer {
overflow-x: hidden; overflow-x: hidden;
transition-timing-function: cubic-bezier(0, 1, 0.5, 1); transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
@media (prefers-reduced-motion: reduce) {
transition-timing-function: unset;
transition: unset;
}
transition: 0.35s; transition: 0.35s;
transition-property: transform; transition-property: transform;
margin: 0 0 0 -100px; margin: 0 0 0 -100px;

View file

@ -20,6 +20,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import { muteWordHits } from '../../services/status_parser/status_parser.js' import { muteWordHits } from '../../services/status_parser/status_parser.js'
import { unescape, uniqBy } from 'lodash' import { unescape, uniqBy } from 'lodash'
import StillImage from '../still-image/still-image.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -117,7 +118,8 @@ const Status = {
RichContent, RichContent,
MentionLink, MentionLink,
MentionsLine, MentionsLine,
QuoteButton QuoteButton,
StillImage
}, },
props: [ props: [
'statusoid', 'statusoid',

View file

@ -1,4 +1,4 @@
@import '../../_variables.scss'; @import "../../_variables.scss";
.Status { .Status {
min-width: 0; min-width: 0;
@ -42,6 +42,10 @@
display: flex; display: flex;
padding: var(--status-margin, $status-margin); padding: var(--status-margin, $status-margin);
.content {
overflow: hidden;
}
> * { > * {
min-width: 0; min-width: 0;
} }
@ -130,6 +134,15 @@
.heading-left { .heading-left {
display: flex; display: flex;
min-width: 0; min-width: 0;
flex-wrap: wrap;
img {
aspect-ratio: 1 / 1;
}
.nowrap {
white-space: nowrap;
}
} }
.heading-right { .heading-right {
@ -139,6 +152,7 @@
.button-unstyled { .button-unstyled {
padding: 5px; padding: 5px;
margin: -5px; margin: -5px;
height: min-content;
&:hover svg { &:hover svg {
color: $fallback--lightText; color: $fallback--lightText;
@ -185,7 +199,7 @@
.reply-to-popover { .reply-to-popover {
.reply-to:hover::before { .reply-to:hover::before {
content: ''; content: "";
display: block; display: block;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -195,13 +209,12 @@
} }
.faint-link:hover { .faint-link:hover {
// override default
text-decoration: none; text-decoration: none;
} }
&.-strikethrough { &.-strikethrough {
.reply-to::after { .reply-to::after {
content: ''; content: "";
display: block; display: block;
position: absolute; position: absolute;
top: 50%; top: 50%;
@ -293,10 +306,12 @@
position: relative; position: relative;
width: 100%; width: 100%;
display: flex; display: flex;
flex-wrap: wrap;
justify-content: left;
margin-top: var(--status-margin, $status-margin); margin-top: var(--status-margin, $status-margin);
> * { > * {
max-width: 4em; min-width: fit-content;
flex: 1; flex: 1;
} }
} }
@ -340,7 +355,7 @@
margin-left: 0.2em; margin-left: 0.2em;
&::before { &::before {
content: ' '; content: " ";
} }
} }
@ -387,7 +402,7 @@
align-items: center; align-items: center;
&::before { &::before {
content: ''; content: "";
position: absolute; position: absolute;
height: 100%; height: 100%;
width: 1px; width: 1px;

View file

@ -166,19 +166,21 @@
> >
{{ status.user.name }} {{ status.user.name }}
</h4> </h4>
<router-link <span class="nowrap">
class="account-name" <router-link
:title="status.user.screen_name_ui" class="account-name"
:to="userProfileLink" :title="status.user.screen_name_ui"
> :to="userProfileLink"
{{ status.user.screen_name_ui }} >
</router-link> @{{ status.user.screen_name_ui }}
<img </router-link>
v-if="!!(status.user && status.user.favicon)" <StillImage
class="status-favicon" v-if="!!(status.user && status.user.favicon)"
:src="status.user.favicon" class="status-favicon"
:title="faviconAlt(status)" :src="status.user.favicon"
> :title="faviconAlt(status)"
/>
</span>
</div> </div>
<span class="heading-right"> <span class="heading-right">
@ -188,7 +190,7 @@
> >
<Timeago <Timeago
:time="status.created_at" :time="status.created_at"
:with-direction="true" :with-direction="!compact"
:auto-update="60" :auto-update="60"
/> />
</router-link> </router-link>
@ -350,22 +352,25 @@
</div> </div>
</div> </div>
<StatusContent <div class="content">
ref="content" <StatusContent
:status="status" ref="content"
:no-heading="noHeading" class="status-content"
:highlight="highlight" :status="status"
:focused="isFocused" :no-heading="noHeading"
:controlled-showing-tall="controlledShowingTall" :highlight="highlight"
:controlled-expanding-subject="controlledExpandingSubject" :focused="isFocused"
:controlled-showing-long-subject="controlledShowingLongSubject" :controlled-showing-tall="controlledShowingTall"
:controlled-toggle-showing-tall="controlledToggleShowingTall" :controlled-expanding-subject="controlledExpandingSubject"
:controlled-toggle-expanding-subject="controlledToggleExpandingSubject" :controlled-showing-long-subject="controlledShowingLongSubject"
:controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject" :controlled-toggle-showing-tall="controlledToggleShowingTall"
@mediaplay="addMediaPlaying($event)" :controlled-toggle-expanding-subject="controlledToggleExpandingSubject"
@mediapause="removeMediaPlaying($event)" :controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject"
@parseReady="setHeadTailLinks" @mediaplay="addMediaPlaying($event)"
/> @mediapause="removeMediaPlaying($event)"
@parseReady="setHeadTailLinks"
/>
</div>
<div <div
v-if="inConversation && !isPreview && replies && replies.length" v-if="inConversation && !isPreview && replies && replies.length"
@ -532,6 +537,6 @@
</div> </div>
</template> </template>
<script src="./status.js" ></script> <script src="./status.js"></script>
<style src="./status.scss" lang="scss"></style> <style src="./status.scss" lang="scss"></style>

View file

@ -17,26 +17,26 @@
.emoji:hover { .emoji:hover {
transform: scale(1.4); transform: scale(1.4);
@media (prefers-reduced-motion: reduce) {
transition: unset;
}
transition: 0.05s; transition: 0.05s;
} }
._mfm_x2_ { ._mfm_x2_ {
.emoji { .emoji {
width: 100px;
height: 100px; height: 100px;
} }
} }
._mfm_x3_ { ._mfm_x3_ {
.emoji { .emoji {
width: 150px;
height: 150px; height: 150px;
} }
} }
._mfm_x4_ { ._mfm_x4_ {
.emoji { .emoji {
width: 200px;
height: 200px; height: 200px;
} }
} }

View file

@ -71,7 +71,7 @@
img, video { img, video {
&.emoji { &.emoji {
width: 50px; max-width: 100%;
height: 50px; height: 50px;
} }
} }
@ -89,7 +89,6 @@
animation: none !important; animation: none !important;
} }
.emoji { .emoji {
width: 32px !important;
height: 32px !important; height: 32px !important;
} }
} }

View file

@ -11,8 +11,9 @@ const StillImage = {
], ],
data () { data () {
return { return {
stopGifs: this.$store.getters.mergedConfig.stopGifs, stopGifs: this.$store.getters.mergedConfig.stopGifs || window.matchMedia('(prefers-reduced-motion: reduce)').matches,
isAnimated: false isAnimated: false,
imageTypeLabel: ''
} }
}, },
computed: { computed: {
@ -39,14 +40,24 @@ const StillImage = {
this.imageLoadError && this.imageLoadError() this.imageLoadError && this.imageLoadError()
}, },
detectAnimation (image) { detectAnimation (image) {
if (this.mimetype === 'image/gif' || this.src.endsWith('.gif')) { const mediaProxyAvailable = this.$store.state.instance.mediaProxyAvailable
this.isAnimated = true
return if (!mediaProxyAvailable) {
// It's a bit aggressive to assume all images we can't find the mimetype of is animated, but necessary for
// people in need of reduced motion accessibility. As such, we'll consider those images animated if the user
// agent is set to prefer reduced motion. Otherwise, it'll just be used as an early exit.
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
// Since the canvas and images are not pixel-perfect matching (due to scaling),
// It makes the images jiggle on hover, which is not ideal for accessibility, methinks
this.isAnimated = true
return
}
this.detectWithoutMediaProxy(image)
} else {
this.detectWithMediaProxy(image)
} }
// harmless CORS errors without-- clean console with },
if (!this.$store.state.instance.mediaProxyAvailable) return detectAnimationWithFetch (image) {
// Animated JPEGs?
if (!(this.src.endsWith('.webp') || this.src.endsWith('.png'))) return
// Browser Cache should ensure image doesn't get loaded twice if cache exists // Browser Cache should ensure image doesn't get loaded twice if cache exists
fetch(image.src, { fetch(image.src, {
referrerPolicy: 'same-origin' referrerPolicy: 'same-origin'
@ -55,12 +66,20 @@ const StillImage = {
// We don't need to read the whole file so only call it once // We don't need to read the whole file so only call it once
data.body.getReader().read() data.body.getReader().read()
.then(reader => { .then(reader => {
if (this.src.endsWith('.webp') && this.isAnimatedWEBP(reader.value)) { // Ordered from least to most intensive
if (this.isGIF(reader.value)) {
this.isAnimated = true this.isAnimated = true
this.setLabel('GIF')
return return
} }
if (this.src.endsWith('.png') && this.isAnimatedPNG(reader.value)) { if (this.isAnimatedWEBP(reader.value)) {
this.isAnimated = true this.isAnimated = true
this.setLabel('WEBP')
return
}
if (this.isAnimatedPNG(reader.value)) {
this.isAnimated = true
this.setLabel('APNG')
} }
}) })
}) })
@ -68,6 +87,53 @@ const StillImage = {
// this.imageLoadError && this.imageLoadError() // this.imageLoadError && this.imageLoadError()
}) })
}, },
detectWithMediaProxy (image) {
this.detectAnimationWithFetch(image)
},
detectWithoutMediaProxy (image) {
// We'll just assume that gifs and webp are animated
const extension = image.src.split('.').pop().toLowerCase()
if (extension === 'gif') {
this.isAnimated = true
this.setLabel('GIF')
return
}
if (extension === 'webp') {
this.isAnimated = true
this.setLabel('WEBP')
return
}
// Beware the apng! use this if ye dare
// if (extension === 'png') {
// this.isAnimated = true
// this.setLabel('PNG')
// return
// }
// Hail mary for extensionless
if (extension.includes('/')) {
// Don't mind the CORS error barrage
this.detectAnimationWithFetch(image)
}
},
setLabel (name) {
this.imageTypeLabel = name;
},
isGIF (data) {
// I am a perfectly sane individual
//
// GIF HEADER CHUNK
// === START HEADER ===
// 47 49 46 38 ("GIF8")
const gifHeader = [0x47, 0x49, 0x46];
for (let i = 0; i < 3; i++) {
if (data[i] !== gifHeader[i]) {
return false;
}
}
return true
},
isAnimatedWEBP (data) { isAnimatedWEBP (data) {
/** /**
* WEBP HEADER CHUNK * WEBP HEADER CHUNK
@ -101,15 +167,54 @@ const StillImage = {
const idatPos = str.indexOf('IDAT') const idatPos = str.indexOf('IDAT')
return (str.substring(0, idatPos > 0 ? idatPos : 0).indexOf('acTL') > 0) return (str.substring(0, idatPos > 0 ? idatPos : 0).indexOf('acTL') > 0)
}, },
drawThumbnail () { drawThumbnail() {
const canvas = this.$refs.canvas const canvas = this.$refs.canvas;
if (!this.$refs.canvas) return if (!canvas) return;
const image = this.$refs.src
const width = image.naturalWidth const context = canvas.getContext('2d');
const height = image.naturalHeight const image = this.$refs.src;
canvas.width = width const parentElement = canvas.parentElement;
canvas.height = height
canvas.getContext('2d').drawImage(image, 0, 0, width, height) // Draw the quick, unscaled version first
context.drawImage(image, 0, 0, parentElement.clientWidth, parentElement.clientHeight);
// Use requestAnimationFrame to schedule the scaling to the next frame
requestAnimationFrame(() => {
// Compute scaling ratio between the natural dimensions of the image and its display dimensions
const scalingRatioWidth = parentElement.clientWidth / image.naturalWidth;
const scalingRatioHeight = parentElement.clientHeight / image.naturalHeight;
// Adjust for high-DPI displays
const ratio = window.devicePixelRatio || 1;
canvas.width = image.naturalWidth * scalingRatioWidth * ratio;
canvas.height = image.naturalHeight * scalingRatioHeight * ratio;
canvas.style.width = `${parentElement.clientWidth}px`;
canvas.style.height = `${parentElement.clientHeight}px`;
context.scale(ratio, ratio);
// Maintain the aspect ratio of the image
const imgAspectRatio = image.naturalWidth / image.naturalHeight;
const canvasAspectRatio = parentElement.clientWidth / parentElement.clientHeight;
let drawWidth, drawHeight;
if (imgAspectRatio > canvasAspectRatio) {
drawWidth = parentElement.clientWidth;
drawHeight = parentElement.clientWidth / imgAspectRatio;
} else {
drawHeight = parentElement.clientHeight;
drawWidth = parentElement.clientHeight * imgAspectRatio;
}
context.clearRect(0, 0, canvas.width, canvas.height); // Clear the previous unscaled image
context.imageSmoothingEnabled = true;
context.imageSmoothingQuality = 'high';
// Draw the good one for realsies
const dx = (parentElement.clientWidth - drawWidth) / 2;
const dy = (parentElement.clientHeight - drawHeight) / 2;
context.drawImage(image, dx, dy, drawWidth, drawHeight);
});
} }
}, },
updated () { updated () {

View file

@ -1,9 +1,15 @@
<template> <template>
<div <div
ref="still-image"
class="still-image" class="still-image"
:class="{ animated: animated }" :class="{ animated: animated }"
:style="style" :style="style"
> >
<div
v-if="animated && imageTypeLabel"
class="image-type-label">
{{ imageTypeLabel }}
</div>
<canvas <canvas
v-if="animated" v-if="animated"
ref="canvas" ref="canvas"
@ -57,30 +63,26 @@
} }
} }
&.animated { .image-type-label {
&::before { position: absolute;
zoom: var(--_still_image-label-scale, 1); top: 0.25em;
content: 'gif'; left: 0.25em;
position: absolute; line-height: 1;
line-height: 1; font-size: 0.6em;
font-size: 0.7em; background: rgba(127, 127, 127, 0.5);
top: 0.5em; color: #fff;
left: 0.5em; padding: 2px 4px;
background: rgba(127, 127, 127, 0.5); border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
color: #fff; z-index: 2;
display: block; visibility: var(--_still-image-label-visibility, visible);
padding: 2px 4px; }
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
z-index: 2;
visibility: var(--_still-image-label-visibility, visible);
}
&.animated {
&:hover canvas { &:hover canvas {
display: none; display: none;
} }
&:hover::before { &:hover .image-type-label {
visibility: var(--_still-image-label-visibility, hidden); visibility: var(--_still-image-label-visibility, hidden);
} }

View file

@ -6,11 +6,13 @@ import TimelineMenuTabs from '../timeline_menu_tabs/timeline_menu_tabs.vue'
import TimelineQuickSettings from './timeline_quick_settings.vue' import TimelineQuickSettings from './timeline_quick_settings.vue'
import { debounce, throttle, keyBy } from 'lodash' import { debounce, throttle, keyBy } from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch, faCog } from '@fortawesome/free-solid-svg-icons' import { faCircleNotch, faCog, faPlus, faMinus } from '@fortawesome/free-solid-svg-icons'
library.add( library.add(
faCircleNotch, faCircleNotch,
faCog faCog,
faPlus,
faMinus
) )
const Timeline = { const Timeline = {
@ -90,6 +92,15 @@ const Timeline = {
}, },
showPanelNavShortcuts () { showPanelNavShortcuts () {
return this.$store.getters.mergedConfig.showPanelNavShortcuts return this.$store.getters.mergedConfig.showPanelNavShortcuts
},
currentUser () {
return this.$store.state.users.currentUser
},
tagData () {
return this.$store.state.tags.tags[this.tag]
},
tagFollowed () {
return this.$store.state.tags.tags[this.tag]?.following
} }
}, },
created () { created () {
@ -118,6 +129,10 @@ const Timeline = {
} }
window.addEventListener('keydown', this.handleShortKey) window.addEventListener('keydown', this.handleShortKey)
setTimeout(this.determineVisibleStatuses, 250) setTimeout(this.determineVisibleStatuses, 250)
if (this.tag) {
this.$store.dispatch('getTag', this.tag)
}
}, },
unmounted () { unmounted () {
window.removeEventListener('scroll', this.handleScroll) window.removeEventListener('scroll', this.handleScroll)
@ -232,6 +247,12 @@ const Timeline = {
}, 200), }, 200),
handleVisibilityChange () { handleVisibilityChange () {
this.unfocused = document.hidden this.unfocused = document.hidden
},
followTag (tag) {
return this.$store.dispatch('followTag', tag)
},
unfollowTag (tag) {
return this.$store.dispatch('unfollowTag', tag)
} }
}, },
watch: { watch: {

View file

@ -21,6 +21,36 @@
{{ $t('timeline.up_to_date') }} {{ $t('timeline.up_to_date') }}
</div> </div>
<TimelineQuickSettings v-if="!embedded" /> <TimelineQuickSettings v-if="!embedded" />
<div
v-if="currentUser && tag !== undefined && tagData && !tagFollowed"
class="followTag"
>
<button
class="button-default"
:title="$t('timeline.follow_tag')"
@click="followTag(tag)"
>
<FAIcon
size="sm"
icon="plus"
/>
</button>
</div>
<div
v-if="currentUser && tag !== undefined && tagData && tagFollowed"
class="followTag"
>
<button
class="button-default"
:title="$t('timeline.unfollow_tag')"
@click="unfollowTag(tag)"
>
<FAIcon
size="sm"
icon="minus"
/>
</button>
</div>
</div> </div>
<div :class="classes.body"> <div :class="classes.body">
<div <div

Some files were not shown because too many files have changed in this diff Show more