Porównaj commity

...

17 Commity

Autor SHA1 Wiadomość Data
Alyx 12d5563465
Merge fa196a2c94 into 7fb56d9f6c 2024-04-12 14:45:41 +02:00
Chee Aun 7fb56d9f6c
Merge pull request #493 from ultramookie/ultramookie-patch-1
Adding new self-hosted instance of Phanpy
2024-04-12 17:41:55 +08:00
steve mookie kong f7c69e56e9
Adding new self-hosted instance of Phanpy
Added new self-hosted instance of Phanpy, halo.mookiesplace.com
2024-04-11 21:28:38 -07:00
Lim Chee Aun c3bcf3d595 Try make Safari show video preview 2024-04-11 18:24:51 +08:00
Lim Chee Aun 0efa39b825 Sometimes it returns a preview image without dimenions 2024-04-11 17:45:19 +08:00
Lim Chee Aun a0d2037007 Early implementation of media-first UI experience 2024-04-11 17:18:17 +08:00
Lim Chee Aun 6e73728e2b Only show data-read-more when it's available 2024-04-11 17:16:04 +08:00
Lim Chee Aun 60920966d6 Special fallback handling when media object doesn't have enough info 2024-04-11 17:15:16 +08:00
Lim Chee Aun 5083463942 Show empty copy when no notifications at all 2024-04-11 17:13:34 +08:00
Lim Chee Aun 8b5fee3dfd Just sub it once 2024-04-10 17:31:26 +08:00
Lim Chee Aun c9124bf150 Change double-tap zoom to match mobile expectations 2024-04-10 15:03:02 +08:00
Lim Chee Aun b85174155c Make notifications settings icon less significant 2024-04-10 14:21:05 +08:00
Lim Chee Aun 5c9f6bae3c Fix followers list failing if familiar followers fail 2024-04-10 14:19:35 +08:00
Lim Chee Aun 4e5940900e Pixelfed-related fixes 2024-04-09 23:35:17 +08:00
Lim Chee Aun 7fa0b4f076 Upgrade dependencies 2024-04-05 17:13:29 +08:00
Lim Chee Aun ecfcc68f15 Add TheDesk 2024-04-05 17:13:13 +08:00
Alyx fa196a2c94
Add Dockerfile 2024-03-18 09:52:29 +01:00
19 zmienionych plików z 979 dodań i 406 usunięć

23
Dockerfile 100644
Wyświetl plik

@ -0,0 +1,23 @@
FROM busybox:1 AS build
ARG PHANPY_RELEASE_VERSION
WORKDIR /root/phanpy_release
RUN wget "https://github.com/cheeaun/phanpy/releases/download/${PHANPY_RELEASE_VERSION}/phanpy-dist.tar.gz" && \
tar -xvf "phanpy-dist.tar.gz" -C /root/phanpy_release && \
rm "phanpy-dist.tar.gz"
# ---
FROM busybox:1
# Create a non-root user to own the files and run our server
RUN adduser -D static
USER static
WORKDIR /home/static
# Copy the static website
# Use the .dockerignore file to control what ends up inside the image!
COPY --chown=static:static --from=build /root/phanpy_release /home/static
# Run BusyBox httpd
CMD ["httpd", "-f", "-v", "-p", "8080"]

Wyświetl plik

@ -205,6 +205,7 @@ These are self-hosted by other wonderful folks.
- [phanpy.hear-me.social](https://phanpy.hear-me.social) by [@admin@hear-me.social](https://hear-me.social/@admin)
- [phanpy.fulda.social](https://phanpy.fulda.social) by [@Ganneff@fulda.social](https://fulda.social/@Ganneff)
- [phanpy.crmbl.uk](https://phanpy.crmbl.uk) by [@snail@crmbl.uk](https://mstdn.crmbl.uk/@snail)
- [halo.mookiesplace.com](https://halo.mookiesplace.com) by [@mookie@mookiesplace.com](https://mookiesplace.com/@mookie)
> Note: Add yours by creating a pull request.
@ -256,6 +257,7 @@ And here I am. Building a Mastodon web client.
- [Tusked](https://tusked.app/)
- [Mastodon Glitch Edition (standalone frontend)](https://iceshrimp.dev/iceshrimp/masto-fe-standalone)
- [Mangane](https://github.com/BDX-town/Mangane)
- [TheDesk](https://github.com/cutls/TheDesk)
- [More...](https://github.com/hueyy/awesome-mastodon/#clients)
## 💁‍♂️ Notice to all other social media client developers

256
package-lock.json wygenerowano
Wyświetl plik

@ -49,9 +49,9 @@
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
"postcss": "~8.4.38",
"postcss-dark-theme-class": "~1.2.1",
"postcss-preset-env": "~9.5.2",
"postcss-preset-env": "~9.5.4",
"twitter-text": "~3.1.0",
"vite": "~5.2.6",
"vite": "~5.2.8",
"vite-plugin-generate-file": "~0.1.1",
"vite-plugin-html-config": "~1.0.11",
"vite-plugin-pwa": "~0.19.7",
@ -1974,9 +1974,9 @@
}
},
"node_modules/@csstools/color-helpers": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.0.0.tgz",
"integrity": "sha512-wjyXB22/h2OvxAr3jldPB7R7kjTUEzopvjitS8jWtyd8fN6xJ8vy1HnHu0ZNfEkqpBJgQ76Q+sBDshWcMvTa/w==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.1.0.tgz",
"integrity": "sha512-pWRKF6cDwget8HowIIf2MqEmqIca/cf8/jO4b3PRtUF5EfQXYMtBIKycXB4yXTCUmwLKOoRZAzh/hjnc7ywOIg==",
"dev": true,
"funding": [
{
@ -2016,9 +2016,9 @@
}
},
"node_modules/@csstools/css-color-parser": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-1.6.2.tgz",
"integrity": "sha512-mlt0PomBlDXMGcbPAqCG36Fw35LZTtaSgCQCHEs4k8QTv1cUKe0rJDlFSJMHtqrgQiLC7LAAS9+s9kKQp2ou/Q==",
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-1.6.3.tgz",
"integrity": "sha512-pQPUPo32HW3/NuZxrwr3VJHE+vGqSTVI5gK4jGbuJ7eOFUrsTmZikXcVdInCVWOvuxK5xbCzwDWoTlZUCAKN+A==",
"dev": true,
"funding": [
{
@ -2031,7 +2031,7 @@
}
],
"dependencies": {
"@csstools/color-helpers": "^4.0.0",
"@csstools/color-helpers": "^4.1.0",
"@csstools/css-calc": "^1.2.0"
},
"engines": {
@ -2107,9 +2107,9 @@
}
},
"node_modules/@csstools/postcss-cascade-layers": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-4.0.3.tgz",
"integrity": "sha512-RbkQoOH23yGhWVetgBTwFgIOHEyU2tKMN7blTz/YAKKabR6tr9pP7mYS23Q9snFY2hr8WSaV8Le64KdM9BtUSA==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-4.0.4.tgz",
"integrity": "sha512-MKErv8lpEwVmAcAwidY1Kfd3oWrh2Q14kxHs9xn26XzjP/PrcdngWq63lJsZeMlBY7o+WlEOeE+FP6zPzeY2uw==",
"dev": true,
"funding": [
{
@ -2122,7 +2122,7 @@
}
],
"dependencies": {
"@csstools/selector-specificity": "^3.0.2",
"@csstools/selector-specificity": "^3.0.3",
"postcss-selector-parser": "^6.0.13"
},
"engines": {
@ -2133,9 +2133,9 @@
}
},
"node_modules/@csstools/postcss-color-function": {
"version": "3.0.12",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-3.0.12.tgz",
"integrity": "sha512-amPGGDI4Xmgu7VN2ciKQe0pP/j5raaETT50nzbnkydp9FMw7imKxSUnXdVQU4NmRgpLKIc5Q7jox0MFhMBImIg==",
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-3.0.13.tgz",
"integrity": "sha512-gM24cIPU45HSPJ2zllz7VKjS1OKQS1sKOMI7Wsw8gFyXSGAGrxhYo++McylOqOXd8ecMaKxKQMUJqJVibvJYig==",
"dev": true,
"funding": [
{
@ -2148,10 +2148,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -2162,9 +2162,9 @@
}
},
"node_modules/@csstools/postcss-color-mix-function": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-2.0.12.tgz",
"integrity": "sha512-qpAEGwVVqHSa88i3gLb43IMpT4/LyZEE8HzZylQKKXFVJ7XykXaORTmXySxyH6H+flT+NyCnutKG2fegCVyCug==",
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-2.0.13.tgz",
"integrity": "sha512-mD8IIfGVeWkN1H1wfCqYePOg4cDnVrOXm4P0OlYcvKriq6sImGCGShv/2D88q6s3iUlLXfUBES+DUjLVjDMhnw==",
"dev": true,
"funding": [
{
@ -2177,10 +2177,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -2244,9 +2244,9 @@
}
},
"node_modules/@csstools/postcss-gamut-mapping": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-1.0.5.tgz",
"integrity": "sha512-AJ74/4nHXgghLWY4/ydEhu3mzwN8c56EjIGrJsoEhKaNuGBAOtUfE5qbkc9XQQ0G2FMhHggqE+9eRrApeK7ebQ==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-1.0.6.tgz",
"integrity": "sha512-qGFpHU9cRf9qqkbHh9cWMTlBtGi/ujPgP/znQdwkbB4TgDR1ddI5wRRrksBsx64sfoUSlIEd70bxXzD9FtfdLg==",
"dev": true,
"funding": [
{
@ -2259,7 +2259,7 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4"
},
@ -2271,9 +2271,9 @@
}
},
"node_modules/@csstools/postcss-gradients-interpolation-method": {
"version": "4.0.13",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.13.tgz",
"integrity": "sha512-dBbyxs9g+mrIzmEH+UtrqJUmvcJB/60j0ijhBcVJMHCgl/rKjj8ey6r/pJOI0EhkVsckOu3Prc9AGzH88C+1pQ==",
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.14.tgz",
"integrity": "sha512-VMWC3xtpchHJoRBb/fs1gJR/5nHopX+0GwwmgdCI1DjROtfWUKIW0nv8occ922Gv0/Lk93XBtYBv8JttVBMZUQ==",
"dev": true,
"funding": [
{
@ -2286,10 +2286,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -2300,9 +2300,9 @@
}
},
"node_modules/@csstools/postcss-hwb-function": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-3.0.11.tgz",
"integrity": "sha512-c36FtMFptwGn5CmsfdONA40IlWG2lHeoC/TDyED/7lwiTht5okxe6iLAa9t2LjBBo5AHQSHfeMvOASdXk/SHog==",
"version": "3.0.12",
"resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-3.0.12.tgz",
"integrity": "sha512-90kIs+FsM6isAXLVoFHTTl4h0J6g1J1M6ahpIjAs6/k7a2A9FB/q+l0MHpLre0ZiPlBf2y3e1j4L+79vml7kJw==",
"dev": true,
"funding": [
{
@ -2315,10 +2315,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -2329,9 +2329,9 @@
}
},
"node_modules/@csstools/postcss-ic-unit": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-3.0.5.tgz",
"integrity": "sha512-9CriM/zvKXa/lDARlxs/MgeyKE6ZmmX4V77VLD7VUxKLVSt0Go3NCy/gRMbwGzxbrk3iaHFXnFbc2lNw+/7jcg==",
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-3.0.6.tgz",
"integrity": "sha512-fHaU9C/sZPauXMrzPitZ/xbACbvxbkPpHoUgB9Kw5evtsBWdVkVrajOyiT9qX7/c+G1yjApoQjP1fQatldsy9w==",
"dev": true,
"funding": [
{
@ -2344,7 +2344,7 @@
}
],
"dependencies": {
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0",
"postcss-value-parser": "^4.2.0"
},
@ -2378,9 +2378,9 @@
}
},
"node_modules/@csstools/postcss-is-pseudo-class": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-4.0.5.tgz",
"integrity": "sha512-qG3MI7IN3KY9UwdaE9E7G7sFydscVW7nAj5OGwaBP9tQPEEVdxXTGI+l1ZW5EUpZFSj+u3q/22fH5+8HI72+Bg==",
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-4.0.6.tgz",
"integrity": "sha512-HilOhAsMpFheMYkuaREZx+CGa4hsG6kQdzwXSsuqKDFzYz2eIMP213+3dH/vUbPXaWrzqLKr8m3i0dgYPoh7vg==",
"dev": true,
"funding": [
{
@ -2393,7 +2393,7 @@
}
],
"dependencies": {
"@csstools/selector-specificity": "^3.0.2",
"@csstools/selector-specificity": "^3.0.3",
"postcss-selector-parser": "^6.0.13"
},
"engines": {
@ -2404,9 +2404,9 @@
}
},
"node_modules/@csstools/postcss-light-dark-function": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-1.0.1.tgz",
"integrity": "sha512-CJOcp+m7Njbu91HtYMMoYuZznsvNSpJtLiR/7BO8/bHTXYPiuAZfxunh7wXLkMbHd5dRBgAVAQZ+H4iFqrvWZw==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-1.0.3.tgz",
"integrity": "sha512-izW8hvhOqJlarLcGXO5PSylW9pQS3fytmhRdx2/e1oZFi15vs7ZShOHcREHJ3FfGdYqDA10cP9uhH0A3hmm1Rw==",
"dev": true,
"funding": [
{
@ -2421,7 +2421,7 @@
"dependencies": {
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -2655,9 +2655,9 @@
}
},
"node_modules/@csstools/postcss-oklab-function": {
"version": "3.0.12",
"resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.12.tgz",
"integrity": "sha512-RNitTHamFvUUh8x+MJuPd2tCekYexUrylGKfUoor5D2GGcgzY1WB6Bl3pIj9t8bAq5h/lcacKaB2wmvUOTfGgQ==",
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.13.tgz",
"integrity": "sha512-xbzMmukDFAwCt2+279io7ZiamZj87s6cnU3UgKB3G+NMpRX9A6uvN8xlnTLCe384hqg6hix5vlOmwkxqACb5pg==",
"dev": true,
"funding": [
{
@ -2670,10 +2670,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -2684,9 +2684,9 @@
}
},
"node_modules/@csstools/postcss-progressive-custom-properties": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-3.1.1.tgz",
"integrity": "sha512-cx/bZgj+MK8SpRZNTu2zGeVFMCQfhsaeuDhukAhfA53yykvIXaTIwLi5shW9hfkvPrkqBeFoiRAzq/qogxeHTA==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-3.2.0.tgz",
"integrity": "sha512-BZlirVxCRgKlE7yVme+Xvif72eTn1MYXj8oZ4Knb+jwaH4u3AN1DjbhM7j86RP5vvuAOexJ4JwfifYYKWMN/QQ==",
"dev": true,
"funding": [
{
@ -2709,9 +2709,9 @@
}
},
"node_modules/@csstools/postcss-relative-color-syntax": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.12.tgz",
"integrity": "sha512-VreDGDgE634niwCytLtkoE5kRxfva7bnMzSoyok7Eh9VPYFOm8CK/oJXt9y3df71Bxc9PG4KC8RA3CxTknudnw==",
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.13.tgz",
"integrity": "sha512-mENWPNcHdiEYtjHFfZP9U1jNukQgFpSQ7wvTvwiadK3qgNBiSl0vMSinM9kKsGsJLTHQ0LEAqWLHurU52I4Jeg==",
"dev": true,
"funding": [
{
@ -2724,10 +2724,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -2790,9 +2790,9 @@
}
},
"node_modules/@csstools/postcss-text-decoration-shorthand": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-3.0.4.tgz",
"integrity": "sha512-yUZmbnUemgQmja7SpOZeU45+P49wNEgQguRdyTktFkZsHf7Gof+ZIYfvF6Cm+LsU1PwSupy4yUeEKKjX5+k6cQ==",
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-3.0.5.tgz",
"integrity": "sha512-qKxXpD0TYINkUtWDN1RHdeWKtZCzEv5j3UMT/ZGqyY27icwCFw7iKO0bUeLSHjYFBqhurCWvoOsa9REqLdrNDw==",
"dev": true,
"funding": [
{
@ -2805,7 +2805,7 @@
}
],
"dependencies": {
"@csstools/color-helpers": "^4.0.0",
"@csstools/color-helpers": "^4.1.0",
"postcss-value-parser": "^4.2.0"
},
"engines": {
@ -2887,9 +2887,9 @@
}
},
"node_modules/@csstools/selector-specificity": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.2.tgz",
"integrity": "sha512-RpHaZ1h9LE7aALeQXmXrJkRG84ZxIsctEN2biEUmFyKpzFM3zZ35eUMcIzZFsw/2olQE6v69+esEqU2f1MKycg==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.3.tgz",
"integrity": "sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==",
"dev": true,
"funding": [
{
@ -4091,9 +4091,9 @@
}
},
"node_modules/autoprefixer": {
"version": "10.4.18",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",
"integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==",
"version": "10.4.19",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
"integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
"dev": true,
"funding": [
{
@ -4111,7 +4111,7 @@
],
"dependencies": {
"browserslist": "^4.23.0",
"caniuse-lite": "^1.0.30001591",
"caniuse-lite": "^1.0.30001599",
"fraction.js": "^4.3.7",
"normalize-range": "^0.1.2",
"picocolors": "^1.0.0",
@ -4306,9 +4306,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001597",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
"integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
"version": "1.0.30001605",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz",
"integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==",
"dev": true,
"funding": [
{
@ -4494,9 +4494,9 @@
}
},
"node_modules/css-has-pseudo": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-6.0.2.tgz",
"integrity": "sha512-Z2Qm5yyOvJRTy6THdUlnGIX6PW/1wOc4FHWlfkcBkfkpZ3oz6lPdG+h+J7t1HZHT4uSSVR8XatXiMpqMUADXow==",
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-6.0.3.tgz",
"integrity": "sha512-qIsDxK/z0byH/mpNsv5hzQ5NOl8m1FRmOLgZpx4bG5uYHnOlO2XafeMI4mFIgNSViHwoUWcxSJZyyijaAmbs+A==",
"dev": true,
"funding": [
{
@ -4509,7 +4509,7 @@
}
],
"dependencies": {
"@csstools/selector-specificity": "^3.0.2",
"@csstools/selector-specificity": "^3.0.3",
"postcss-selector-parser": "^6.0.13",
"postcss-value-parser": "^4.2.0"
},
@ -4571,9 +4571,9 @@
}
},
"node_modules/cssdb": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz",
"integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.0.0.tgz",
"integrity": "sha512-hfpm8VXc7/dhcEWpLvKDLwImOSk1sa2DxL36OEiY/4h2MGfKjPYIMZo4hnEEl+TCJr2GwcX46jF5TafRASDe9w==",
"dev": true,
"funding": [
{
@ -6424,9 +6424,9 @@
}
},
"node_modules/postcss-color-functional-notation": {
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.7.tgz",
"integrity": "sha512-VwzaVfu1kEYDK2yM8ixeKA/QbgQ8k0uxpRevLH9Wam+R3C1sg68vnRB7m2AMhYfjqb5khp4p0EQk5aO90ASAkw==",
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.8.tgz",
"integrity": "sha512-BilFPTHcfWEnuQeqL83nbSPVK3tcU57S60aOrqgditarNDzOojyF0Gdc2Ur5L+zox366QjrCe0rOBLDO2pNvRQ==",
"dev": true,
"funding": [
{
@ -6439,10 +6439,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -6637,9 +6637,9 @@
}
},
"node_modules/postcss-double-position-gradients": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-5.0.5.tgz",
"integrity": "sha512-26Tx4BfoxMNO9C89Nk56bfGv4jAwdDVgrQOyHZOP/6/D+xuOBf306KzTjHC2oBzaIIVtX+famOWHv4raxMjJMQ==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-5.0.6.tgz",
"integrity": "sha512-QJ+089FKMaqDxOhhIHsJrh4IP7h4PIHNC5jZP5PMmnfUScNu8Hji2lskqpFWCvu+5sj+2EJFyzKd13sLEWOZmQ==",
"dev": true,
"funding": [
{
@ -6652,7 +6652,7 @@
}
],
"dependencies": {
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0",
"postcss-value-parser": "^4.2.0"
},
@ -6772,9 +6772,9 @@
}
},
"node_modules/postcss-lab-function": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.12.tgz",
"integrity": "sha512-flHW2jdRCRe8ClhMgrylR1BCiyyqLLvp1qKfO5wuAclUihldfRsoDIFQWFVW7rJbruil9/LCoHNUvY9JwTlLPw==",
"version": "6.0.13",
"resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.13.tgz",
"integrity": "sha512-tzEThi3prSyomnVqaAU+k/YJib4rxeeTKVfMt+mPcEugFgp0t6xRjoc7fzaWCoEwYLC6GxGLD8/Ugx8COCqabw==",
"dev": true,
"funding": [
{
@ -6787,10 +6787,10 @@
}
],
"dependencies": {
"@csstools/css-color-parser": "^1.6.2",
"@csstools/css-color-parser": "^1.6.3",
"@csstools/css-parser-algorithms": "^2.6.1",
"@csstools/css-tokenizer": "^2.2.4",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@ -6826,9 +6826,9 @@
}
},
"node_modules/postcss-nesting": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.0.tgz",
"integrity": "sha512-QOYnosaZ+mlP6plQrAxFw09UUp2Sgtxj1BVHN+rSVbtV0Yx48zRt9/9F/ZOoxOKBBEsaJk2MYhhVRjeRRw5yuw==",
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.1.tgz",
"integrity": "sha512-qc74KvIAQNa5ujZKG1UV286dhaDW6basbUy2i9AzNU/T8C9hpvGu9NZzm1SfePe2yP7sPYgpA8d4sPVopn2Hhw==",
"dev": true,
"funding": [
{
@ -6842,7 +6842,7 @@
],
"dependencies": {
"@csstools/selector-resolve-nested": "^1.1.0",
"@csstools/selector-specificity": "^3.0.2",
"@csstools/selector-specificity": "^3.0.3",
"postcss-selector-parser": "^6.0.13"
},
"engines": {
@ -6936,9 +6936,9 @@
}
},
"node_modules/postcss-preset-env": {
"version": "9.5.2",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.5.2.tgz",
"integrity": "sha512-/KIAHELdg5BxsKA/Vc6Nok/66EM7lps8NulKcQWX2S52HdzxAqh+6HcuAFj7trRSW587vlOA4zCjlRFgR+W6Ag==",
"version": "9.5.4",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.5.4.tgz",
"integrity": "sha512-o/jOlJjhm4f6rI5q1f+4Og3tz1cjaO50er9ndk7ZdcXHjWOH49kMAhqDC/nQifypQkOAiAmF46dPt3pZM+Cwbg==",
"dev": true,
"funding": [
{
@ -6951,18 +6951,18 @@
}
],
"dependencies": {
"@csstools/postcss-cascade-layers": "^4.0.3",
"@csstools/postcss-color-function": "^3.0.12",
"@csstools/postcss-color-mix-function": "^2.0.12",
"@csstools/postcss-cascade-layers": "^4.0.4",
"@csstools/postcss-color-function": "^3.0.13",
"@csstools/postcss-color-mix-function": "^2.0.13",
"@csstools/postcss-exponential-functions": "^1.0.5",
"@csstools/postcss-font-format-keywords": "^3.0.2",
"@csstools/postcss-gamut-mapping": "^1.0.5",
"@csstools/postcss-gradients-interpolation-method": "^4.0.13",
"@csstools/postcss-hwb-function": "^3.0.11",
"@csstools/postcss-ic-unit": "^3.0.5",
"@csstools/postcss-gamut-mapping": "^1.0.6",
"@csstools/postcss-gradients-interpolation-method": "^4.0.14",
"@csstools/postcss-hwb-function": "^3.0.12",
"@csstools/postcss-ic-unit": "^3.0.6",
"@csstools/postcss-initial": "^1.0.1",
"@csstools/postcss-is-pseudo-class": "^4.0.5",
"@csstools/postcss-light-dark-function": "^1.0.1",
"@csstools/postcss-is-pseudo-class": "^4.0.6",
"@csstools/postcss-light-dark-function": "^1.0.3",
"@csstools/postcss-logical-float-and-clear": "^2.0.1",
"@csstools/postcss-logical-overflow": "^1.0.1",
"@csstools/postcss-logical-overscroll-behavior": "^1.0.1",
@ -6972,38 +6972,38 @@
"@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.7",
"@csstools/postcss-nested-calc": "^3.0.2",
"@csstools/postcss-normalize-display-values": "^3.0.2",
"@csstools/postcss-oklab-function": "^3.0.12",
"@csstools/postcss-progressive-custom-properties": "^3.1.1",
"@csstools/postcss-relative-color-syntax": "^2.0.12",
"@csstools/postcss-oklab-function": "^3.0.13",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/postcss-relative-color-syntax": "^2.0.13",
"@csstools/postcss-scope-pseudo-class": "^3.0.1",
"@csstools/postcss-stepped-value-functions": "^3.0.6",
"@csstools/postcss-text-decoration-shorthand": "^3.0.4",
"@csstools/postcss-text-decoration-shorthand": "^3.0.5",
"@csstools/postcss-trigonometric-functions": "^3.0.6",
"@csstools/postcss-unset-value": "^3.0.1",
"autoprefixer": "^10.4.18",
"autoprefixer": "^10.4.19",
"browserslist": "^4.22.3",
"css-blank-pseudo": "^6.0.1",
"css-has-pseudo": "^6.0.2",
"css-has-pseudo": "^6.0.3",
"css-prefers-color-scheme": "^9.0.1",
"cssdb": "^7.11.1",
"cssdb": "^8.0.0",
"postcss-attribute-case-insensitive": "^6.0.3",
"postcss-clamp": "^4.1.0",
"postcss-color-functional-notation": "^6.0.7",
"postcss-color-functional-notation": "^6.0.8",
"postcss-color-hex-alpha": "^9.0.4",
"postcss-color-rebeccapurple": "^9.0.3",
"postcss-custom-media": "^10.0.4",
"postcss-custom-properties": "^13.3.6",
"postcss-custom-selectors": "^7.1.8",
"postcss-dir-pseudo-class": "^8.0.1",
"postcss-double-position-gradients": "^5.0.5",
"postcss-double-position-gradients": "^5.0.6",
"postcss-focus-visible": "^9.0.1",
"postcss-focus-within": "^8.0.1",
"postcss-font-variant": "^5.0.0",
"postcss-gap-properties": "^5.0.1",
"postcss-image-set-function": "^6.0.3",
"postcss-lab-function": "^6.0.12",
"postcss-lab-function": "^6.0.13",
"postcss-logical": "^7.0.1",
"postcss-nesting": "^12.1.0",
"postcss-nesting": "^12.1.1",
"postcss-opacity-percentage": "^2.0.0",
"postcss-overflow-shorthand": "^5.0.1",
"postcss-page-break": "^3.0.4",
@ -8207,13 +8207,13 @@
}
},
"node_modules/vite": {
"version": "5.2.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz",
"integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==",
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
"integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
"dev": true,
"dependencies": {
"esbuild": "^0.20.1",
"postcss": "^8.4.36",
"postcss": "^8.4.38",
"rollup": "^4.13.0"
},
"bin": {

Wyświetl plik

@ -51,9 +51,9 @@
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
"postcss": "~8.4.38",
"postcss-dark-theme-class": "~1.2.1",
"postcss-preset-env": "~9.5.2",
"postcss-preset-env": "~9.5.4",
"twitter-text": "~3.1.0",
"vite": "~5.2.6",
"vite": "~5.2.8",
"vite-plugin-generate-file": "~0.1.1",
"vite-plugin-html-config": "~1.0.11",
"vite-plugin-pwa": "~0.19.7",

Wyświetl plik

@ -301,6 +301,34 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
}
}
}
.deck-container-media-first {
.timeline {
> li:not(.timeline-item-carousel, .timeline-item-container) {
&:has(.status-media-first) {
width: fit-content;
background-color: transparent !important;
border: 0 !important;
box-shadow: none !important;
max-width: min(480px, 100%);
margin-inline: auto !important;
&:has(.skeleton) {
width: 100%;
}
}
&:has(.media[data-orientation='landscape']) {
max-width: 100%;
}
}
.status-link:has(.status-media-first):hover {
background-color: transparent;
}
}
}
.timeline.grow {
/* min-height: 100vh;
min-height: 100dvh; */

Wyświetl plik

@ -254,12 +254,13 @@ function AccountInfo({
// On first load, fetch familiar followers, merge to top of results' `value`
// Remove dups on every fetch
if (firstLoad) {
const familiarFollowers = await masto.v1.accounts.familiarFollowers.fetch(
{
let familiarFollowers = [];
try {
familiarFollowers = await masto.v1.accounts.familiarFollowers.fetch({
id: [id],
},
);
familiarFollowersCache.current = familiarFollowers[0].accounts;
});
} catch (e) {}
familiarFollowersCache.current = familiarFollowers?.[0]?.accounts || [];
newValue = [
...familiarFollowersCache.current,
...value.filter(

Wyświetl plik

@ -74,7 +74,7 @@ function Media({
altIndex,
onClick = () => {},
}) {
const {
let {
blurhash,
description,
meta,
@ -84,15 +84,27 @@ function Media({
url,
type,
} = media;
if (/no\-preview\./i.test(previewUrl)) {
previewUrl = null;
}
const { original = {}, small, focus } = meta || {};
const width = showOriginal ? original?.width : small?.width;
const height = showOriginal ? original?.height : small?.height;
const width = showOriginal
? original?.width
: small?.width || original?.width;
const height = showOriginal
? original?.height
: small?.height || original?.height;
const mediaURL = showOriginal ? url : previewUrl || url;
const remoteMediaURL = showOriginal
? remoteUrl
: previewRemoteUrl || remoteUrl;
const orientation = width >= height ? 'landscape' : 'portrait';
const hasDimensions = width && height;
const orientation = hasDimensions
? width > height
? 'landscape'
: 'portrait'
: null;
const rgbAverageColor = blurhash ? getBlurHashAverageColor(blurhash) : null;
@ -133,7 +145,8 @@ function Media({
enabled: pinchZoomEnabled,
draggableUnZoomed: false,
inertiaFriction: 0.9,
doubleTapZoomOutOnMaxScale: true,
tapZoomFactor: 2,
doubleTapToggleZoom: true,
containerProps: {
className: 'media-zoom',
style: {
@ -290,7 +303,11 @@ function Media({
}}
onError={(e) => {
const { src } = e.target;
if (src === mediaURL && mediaURL !== remoteMediaURL) {
if (
src === mediaURL &&
remoteMediaURL &&
mediaURL !== remoteMediaURL
) {
e.target.src = remoteMediaURL;
}
}}
@ -321,6 +338,18 @@ function Media({
onLoad={(e) => {
// e.target.closest('.media-image').style.backgroundImage = '';
e.target.dataset.loaded = true;
if (!hasDimensions) {
const $media = e.target.closest('.media');
if ($media) {
$media.dataset.orientation =
e.target.naturalWidth > e.target.naturalHeight
? 'landscape'
: 'portrait';
$media.style['--width'] = `${e.target.naturalWidth}px`;
$media.style['--height'] = `${e.target.naturalHeight}px`;
$media.style.aspectRatio = `${e.target.naturalWidth}/${e.target.naturalHeight}`;
}
}
}}
onError={(e) => {
const { src } = e.target;
@ -338,6 +367,7 @@ function Media({
</Figure>
);
} else if (type === 'gifv' || type === 'video' || isVideoMaybe) {
const hasDuration = original.duration > 0;
const shortDuration = original.duration < 31;
const isGIF = type === 'gifv' && shortDuration;
// If GIF is too long, treat it as a video
@ -473,14 +503,55 @@ function Media({
/>
) : (
<>
<img
src={previewUrl}
alt={showInlineDesc ? '' : description}
width={width}
height={height}
data-orientation={orientation}
loading="lazy"
/>
{previewUrl ? (
<img
src={previewUrl}
alt={showInlineDesc ? '' : description}
width={width}
height={height}
data-orientation={orientation}
loading="lazy"
onLoad={(e) => {
if (!hasDimensions) {
const $media = e.target.closest('.media');
if ($media) {
$media.dataset.orientation =
e.target.naturalWidth > e.target.naturalHeight
? 'landscape'
: 'portrait';
$media.style['--width'] = `${e.target.naturalWidth}px`;
$media.style[
'--height'
] = `${e.target.naturalHeight}px`;
$media.style.aspectRatio = `${e.target.naturalWidth}/${e.target.naturalHeight}`;
}
}
}}
/>
) : (
<video
src={url + '#t=0.1'} // Make Safari show 1st-frame preview
width={width}
height={height}
data-orientation={orientation}
preload="metadata"
muted
disablePictureInPicture
onLoadedMetadata={(e) => {
if (!hasDuration) {
const { duration } = e.target;
if (duration) {
const formattedDuration = formatDuration(duration);
const container = e.target.closest('.media-video');
if (container) {
container.dataset.formattedDuration =
formattedDuration;
}
}
}
}}
/>
)}
<div class="media-play">
<Icon icon="play" size="xl" />
</div>

Wyświetl plik

@ -160,7 +160,7 @@
display: block;
position: relative;
&:after {
&[data-read-more]:after {
content: attr(data-read-more);
line-height: 1;
display: inline-block;
@ -618,6 +618,7 @@
~ *:not(
.content.truncated,
.media-container,
.media-first-container,
.card,
.media-figure-multiple,
.spoiler-media-button
@ -638,6 +639,7 @@
~ *:not(
.media-container,
.media-first-container,
.card,
.media-figure-multiple,
.spoiler-media-button
@ -708,11 +710,12 @@
}
}
~ :is(.media-container, .media-figure-multiple) .media {
~ :is(.media-container, .media-first-container, .media-figure-multiple)
.media {
background-image: radial-gradient(
circle at 50% 50%,
var(--average-color, var(--bg-faded-color)),
var(--bg-color) 20em
var(--bg-color) 25em
);
> *:not(.media-play, .alt-badge) {
@ -790,7 +793,9 @@
black 1.5em
);
}
.timeline-deck .status:not(.truncated .status) .content.truncated:after {
.timeline-deck
.status:not(.truncated .status)
.content.truncated[data-read-more]:after {
content: attr(data-read-more);
line-height: 1;
display: inline-block;
@ -1314,6 +1319,227 @@ body:has(#modal-container .carousel) .status .media img:hover {
background-blend-mode: multiply;
}
.status.skeleton .media-first-container {
min-height: 3em;
background-color: var(--outline-color);
}
.status-media-first {
.meta-name {
opacity: 0.65;
transition: opacity 0.5s ease-in-out;
b + i {
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
}
:is(:hover, :focus) > & .meta-name {
opacity: 1;
b + i {
opacity: 0.5;
}
}
.media-first-spoiler-content {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
transition: opacity 0.5s ease-in-out;
opacity: 0.5;
}
&:hover .media-first-spoiler-content {
opacity: 1;
}
.media-first-spoiler-button {
display: inline-flex !important;
}
.media-first-container {
margin-top: 8px;
display: flex;
max-height: 80vh;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
user-select: none;
margin-inline: -16px;
position: relative;
scrollbar-width: none;
/* border: var(--hairline-width) solid var(--outline-color);
border-inline-width: 0;
background-color: var(--bg-faded-color); */
@media (min-width: 40em) {
margin-inline: 0;
/* border-radius: 4px; */
border-inline-width: var(--hairline-width);
}
&::-webkit-scrollbar {
display: none;
}
> .media-first-item {
scroll-snap-align: center;
scroll-snap-stop: always;
flex-shrink: 0;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
&:not(:only-child) {
background-color: var(--bg-blur-color);
box-shadow: inset 0 0 0 var(--hairline-width) var(--outline-color);
}
.media {
/* background-color: var(--average-color, var(--bg-faded-color)); */
width: var(--width);
max-width: 100%;
max-height: 100%;
min-height: var(--min-dimension);
/* max-height: min(var(--height), 80vh); */
&:active {
transform: none;
}
img,
video {
object-fit: scale-down;
animation: none;
&:not([data-loaded='true']) {
background-color: var(--bg-color);
}
}
}
}
.media-carousel-controls {
flex-shrink: 0;
width: 100%;
position: sticky;
right: 0;
left: 0;
pointer-events: none;
display: flex;
justify-content: space-between;
}
.carousel-indexer {
z-index: 1;
position: absolute;
top: 8px;
right: 8px;
color: var(--media-fg-color);
background-color: var(--media-bg-color);
padding: 2px 8px;
border-radius: 16px;
font-size: 0.8em;
font-variant-numeric: tabular-nums;
opacity: 0.6;
transition: opacity 1.5s ease-in-out;
border: var(--hairline-width) solid var(--media-outline-color);
}
.media-carousel-button {
display: flex;
flex-shrink: 0;
padding-inline: 8px;
margin-block: 3em;
pointer-events: auto;
cursor: pointer;
align-items: center;
justify-content: center;
}
.carousel-button {
@media (pointer: coarse) {
display: none;
}
+ .carousel-button {
left: auto;
right: 8px;
}
}
@media (hover: hover) and (pointer: fine) {
.carousel-button {
filter: opacity(0);
}
&:hover .carousel-button {
filter: opacity(1);
}
}
}
:is(:hover, :focus) > & .carousel-indexer {
opacity: 0;
}
.media-carousel-dots {
pointer-events: none;
display: flex;
gap: 5px;
justify-content: center;
margin-top: 8px;
padding: 8px;
.carousel-dot {
display: inline-block;
width: 5px;
height: 5px;
border-radius: 50%;
background-color: var(--text-color);
transition: all 0.3s ease-in-out;
opacity: 0.3;
&.active {
opacity: 1;
background-color: var(--text-color);
transform: scale(1.5);
}
}
}
.media-first-content {
margin-top: 8px;
height: 1.75em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.9em;
mask-image: linear-gradient(to bottom, black 1.5em, transparent 1.75em);
opacity: 0.5;
transition: opacity 0.5s ease-in-out;
@media (min-width: 40em) {
margin-inline: 16px;
}
* {
text-align: center;
/* Brute force ellipsis */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap !important;
pointer-events: none;
}
a {
filter: grayscale(0.5);
}
}
:is(:hover, :focus) > & .media-first-content {
opacity: 1;
}
}
.status:not(.large) .hashtag-stuffing {
opacity: 0.75;
transition: opacity 0.2s ease-in-out;
@ -2289,7 +2515,7 @@ a.card:is(:hover, :focus):visited {
mask-image: linear-gradient(to bottom, #000 80px, transparent);
}
&:after {
&[data-read-more]:after {
content: attr(data-read-more);
line-height: 1;
display: inline-block;

Wyświetl plik

@ -169,15 +169,19 @@ function Status({
allowContextMenu,
showActionsBar,
showReplyParent,
mediaFirst,
}) {
if (skeleton) {
return (
<div class="status skeleton">
<Avatar size="xxl" />
<div class={`status skeleton ${mediaFirst ? 'status-media-first' : ''}`}>
{!mediaFirst && <Avatar size="xxl" />}
<div class="container">
<div class="meta"> </div>
<div class="meta">
{(size === 's' || mediaFirst) && <Avatar size="m" />}
</div>
<div class="content-container">
<div class="content">
{mediaFirst && <div class="media-first-container" />}
<div class={`content ${mediaFirst ? 'media-first-content' : ''}`}>
<p> </p>
</div>
</div>
@ -247,6 +251,10 @@ function Status({
emojiReactions,
} = status;
// if (!mediaAttachments?.length) mediaFirst = false;
const hasMediaAttachments = !!mediaAttachments?.length;
if (mediaFirst && hasMediaAttachments) size = 's';
const currentAccount = useMemo(() => {
return store.session.get('currentAccount');
}, []);
@ -354,6 +362,7 @@ function Status({
size={size}
contentTextWeight={contentTextWeight}
readOnly={readOnly}
mediaFirst={mediaFirst}
/>
</div>
);
@ -378,6 +387,7 @@ function Status({
contentTextWeight={contentTextWeight}
readOnly={readOnly}
enableCommentHint
mediaFirst={mediaFirst}
/>
</div>
);
@ -411,6 +421,7 @@ function Status({
contentTextWeight={contentTextWeight}
readOnly={readOnly}
enableCommentHint
mediaFirst={mediaFirst}
/>
</div>
);
@ -848,56 +859,62 @@ function Status({
</MenuItem>
</>
)}
{(enableTranslate || !language || differentLanguage) && <MenuDivider />}
{enableTranslate ? (
<div class={supportsTTS ? 'menu-horizontal' : ''}>
<MenuItem
disabled={forceTranslate}
onClick={() => {
setForceTranslate(true);
}}
>
<Icon icon="translate" />
<span>Translate</span>
</MenuItem>
{supportsTTS && (
<MenuItem
onClick={() => {
const postText = getPostText(status);
if (postText) {
speak(postText, language);
}
}}
>
<Icon icon="speak" />
<span>Speak</span>
</MenuItem>
{!mediaFirst && (
<>
{(enableTranslate || !language || differentLanguage) && (
<MenuDivider />
)}
</div>
) : (
(!language || differentLanguage) && (
<div class={supportsTTS ? 'menu-horizontal' : ''}>
<MenuLink
to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`}
>
<Icon icon="translate" />
<span>Translate</span>
</MenuLink>
{supportsTTS && (
{enableTranslate ? (
<div class={supportsTTS ? 'menu-horizontal' : ''}>
<MenuItem
disabled={forceTranslate}
onClick={() => {
const postText = getPostText(status);
if (postText) {
speak(postText, language);
}
setForceTranslate(true);
}}
>
<Icon icon="speak" />
<span>Speak</span>
<Icon icon="translate" />
<span>Translate</span>
</MenuItem>
)}
</div>
)
{supportsTTS && (
<MenuItem
onClick={() => {
const postText = getPostText(status);
if (postText) {
speak(postText, language);
}
}}
>
<Icon icon="speak" />
<span>Speak</span>
</MenuItem>
)}
</div>
) : (
(!language || differentLanguage) && (
<div class={supportsTTS ? 'menu-horizontal' : ''}>
<MenuLink
to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`}
>
<Icon icon="translate" />
<span>Translate</span>
</MenuLink>
{supportsTTS && (
<MenuItem
onClick={() => {
const postText = getPostText(status);
if (postText) {
speak(postText, language);
}
}}
>
<Icon icon="speak" />
<span>Speak</span>
</MenuItem>
)}
</div>
)
)}
</>
)}
{((!isSizeLarge && sameInstance) ||
enableTranslate ||
@ -1384,7 +1401,7 @@ function Status({
}[size]
} ${_deleted ? 'status-deleted' : ''} ${quoted ? 'status-card' : ''} ${
isContextMenuOpen ? 'status-menu-open' : ''
}`}
} ${mediaFirst && hasMediaAttachments ? 'status-media-first' : ''}`}
onMouseEnter={debugHover}
onContextMenu={(e) => {
if (!showContextMenu) return;
@ -1712,188 +1729,253 @@ function Status({
}
}
>
{!!spoilerText && (
{mediaFirst && hasMediaAttachments ? (
<>
<div
class="content spoiler-content"
lang={language}
dir="auto"
ref={spoilerContentRef}
data-read-more={readMoreText}
>
<p>
<EmojiText text={spoilerText} emojis={emojis} />
</p>
</div>
{readingExpandSpoilers || previewMode ? (
<div class="spoiler-divider">
<Icon icon="eye-open" /> Content warning
{(!!spoilerText || !!sensitive) && !readingExpandSpoilers && (
<>
{!!spoilerText && (
<span
class="spoiler-content media-first-spoiler-content"
lang={language}
dir="auto"
ref={spoilerContentRef}
data-read-more={readMoreText}
>
<EmojiText text={spoilerText} emojis={emojis} />{' '}
</span>
)}
<button
class={`light spoiler-button media-first-spoiler-button ${
showSpoiler ? 'spoiling' : ''
}`}
type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (showSpoiler) {
delete states.spoilers[id];
if (!readingExpandSpoilers) {
delete states.spoilersMedia[id];
}
} else {
states.spoilers[id] = true;
if (!readingExpandSpoilers) {
states.spoilersMedia[id] = true;
}
}
}}
>
<Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
{showSpoiler ? 'Show less' : 'Show content'}
</button>
</>
)}
<MediaFirstContainer
mediaAttachments={mediaAttachments}
language={language}
postID={id}
instance={instance}
/>
{!!content && (
<div class="media-first-content content" ref={contentRef}>
<PostContent
post={status}
instance={instance}
previewMode={previewMode}
/>
</div>
) : (
<button
class={`light spoiler-button ${
showSpoiler ? 'spoiling' : ''
}`}
type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (showSpoiler) {
delete states.spoilers[id];
if (!readingExpandSpoilers) {
delete states.spoilersMedia[id];
}
} else {
states.spoilers[id] = true;
if (!readingExpandSpoilers) {
states.spoilersMedia[id] = true;
}
}
}}
>
<Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
{showSpoiler ? 'Show less' : 'Show content'}
</button>
)}
</>
)}
{!!content && (
<div
class="content"
ref={contentRef}
data-read-more={readMoreText}
>
<PostContent
post={status}
instance={instance}
previewMode={previewMode}
/>
<QuoteStatuses id={id} instance={instance} level={quoted} />
</div>
)}
{!!poll && (
<Poll
lang={language}
poll={poll}
readOnly={readOnly || !sameInstance || !authenticated}
onUpdate={(newPoll) => {
states.statuses[sKey].poll = newPoll;
}}
refresh={() => {
return masto.v1.polls
.$select(poll.id)
.fetch()
.then((pollResponse) => {
states.statuses[sKey].poll = pollResponse;
})
.catch((e) => {}); // Silently fail
}}
votePoll={(choices) => {
return masto.v1.polls
.$select(poll.id)
.votes.create({
choices,
})
.then((pollResponse) => {
states.statuses[sKey].poll = pollResponse;
})
.catch((e) => {}); // Silently fail
}}
/>
)}
{(((enableTranslate || inlineTranslate) &&
!!content.trim() &&
!!getHTMLText(emojifyText(content, emojis)) &&
differentLanguage) ||
forceTranslate) && (
<TranslationBlock
forceTranslate={forceTranslate || inlineTranslate}
mini={!isSizeLarge && !withinContext}
sourceLanguage={language}
text={getPostText(status)}
/>
)}
{!previewMode &&
sensitive &&
!!mediaAttachments.length &&
readingExpandMedia !== 'show_all' && (
<button
class={`plain spoiler-media-button ${
showSpoilerMedia ? 'spoiling' : ''
}`}
type="button"
hidden={!readingExpandSpoilers && !!spoilerText}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (showSpoilerMedia) {
delete states.spoilersMedia[id];
} else {
states.spoilersMedia[id] = true;
}
}}
>
<Icon icon={showSpoilerMedia ? 'eye-open' : 'eye-close'} />{' '}
{showSpoilerMedia ? 'Show less' : 'Show media'}
</button>
)}
{!!mediaAttachments.length && (
<MultipleMediaFigure
lang={language}
enabled={showMultipleMediaCaptions}
captionChildren={captionChildren}
>
<div
ref={mediaContainerRef}
class={`media-container media-eq${mediaAttachments.length} ${
mediaAttachments.length > 2 ? 'media-gt2' : ''
} ${mediaAttachments.length > 4 ? 'media-gt4' : ''}`}
>
{displayedMediaAttachments.map((media, i) => (
<Media
key={media.id}
media={media}
autoAnimate={isSizeLarge}
showCaption={mediaAttachments.length === 1}
allowLongerCaption={
!content && mediaAttachments.length === 1
}
) : (
<>
{!!spoilerText && (
<>
<div
class="content spoiler-content"
lang={language}
altIndex={
showMultipleMediaCaptions &&
!!media.description &&
i + 1
}
to={`/${instance}/s/${id}?${
withinContext ? 'media' : 'media-only'
}=${i + 1}`}
onClick={
onMediaClick
? (e) => {
onMediaClick(e, i, media, status);
dir="auto"
ref={spoilerContentRef}
data-read-more={readMoreText}
>
<p>
<EmojiText text={spoilerText} emojis={emojis} />
</p>
</div>
{readingExpandSpoilers || previewMode ? (
<div class="spoiler-divider">
<Icon icon="eye-open" /> Content warning
</div>
) : (
<button
class={`light spoiler-button ${
showSpoiler ? 'spoiling' : ''
}`}
type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (showSpoiler) {
delete states.spoilers[id];
if (!readingExpandSpoilers) {
delete states.spoilersMedia[id];
}
: undefined
}
} else {
states.spoilers[id] = true;
if (!readingExpandSpoilers) {
states.spoilersMedia[id] = true;
}
}
}}
>
<Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
{showSpoiler ? 'Show less' : 'Show content'}
</button>
)}
</>
)}
{!!content && (
<div
class="content"
ref={contentRef}
data-read-more={readMoreText}
>
<PostContent
post={status}
instance={instance}
previewMode={previewMode}
/>
))}
</div>
</MultipleMediaFigure>
<QuoteStatuses id={id} instance={instance} level={quoted} />
</div>
)}
{!!poll && (
<Poll
lang={language}
poll={poll}
readOnly={readOnly || !sameInstance || !authenticated}
onUpdate={(newPoll) => {
states.statuses[sKey].poll = newPoll;
}}
refresh={() => {
return masto.v1.polls
.$select(poll.id)
.fetch()
.then((pollResponse) => {
states.statuses[sKey].poll = pollResponse;
})
.catch((e) => {}); // Silently fail
}}
votePoll={(choices) => {
return masto.v1.polls
.$select(poll.id)
.votes.create({
choices,
})
.then((pollResponse) => {
states.statuses[sKey].poll = pollResponse;
})
.catch((e) => {}); // Silently fail
}}
/>
)}
{(((enableTranslate || inlineTranslate) &&
!!content.trim() &&
!!getHTMLText(emojifyText(content, emojis)) &&
differentLanguage) ||
forceTranslate) && (
<TranslationBlock
forceTranslate={forceTranslate || inlineTranslate}
mini={!isSizeLarge && !withinContext}
sourceLanguage={language}
text={getPostText(status)}
/>
)}
{!previewMode &&
sensitive &&
!!mediaAttachments.length &&
readingExpandMedia !== 'show_all' && (
<button
class={`plain spoiler-media-button ${
showSpoilerMedia ? 'spoiling' : ''
}`}
type="button"
hidden={!readingExpandSpoilers && !!spoilerText}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (showSpoilerMedia) {
delete states.spoilersMedia[id];
} else {
states.spoilersMedia[id] = true;
}
}}
>
<Icon
icon={showSpoilerMedia ? 'eye-open' : 'eye-close'}
/>{' '}
{showSpoilerMedia ? 'Show less' : 'Show media'}
</button>
)}
{!!mediaAttachments.length && (
<MultipleMediaFigure
lang={language}
enabled={showMultipleMediaCaptions}
captionChildren={captionChildren}
>
<div
ref={mediaContainerRef}
class={`media-container media-eq${
mediaAttachments.length
} ${mediaAttachments.length > 2 ? 'media-gt2' : ''} ${
mediaAttachments.length > 4 ? 'media-gt4' : ''
}`}
>
{displayedMediaAttachments.map((media, i) => (
<Media
key={media.id}
media={media}
autoAnimate={isSizeLarge}
showCaption={mediaAttachments.length === 1}
allowLongerCaption={
!content && mediaAttachments.length === 1
}
lang={language}
altIndex={
showMultipleMediaCaptions &&
!!media.description &&
i + 1
}
to={`/${instance}/s/${id}?${
withinContext ? 'media' : 'media-only'
}=${i + 1}`}
onClick={
onMediaClick
? (e) => {
onMediaClick(e, i, media, status);
}
: undefined
}
/>
))}
</div>
</MultipleMediaFigure>
)}
{!!card &&
/^https/i.test(card?.url) &&
!sensitive &&
!spoilerText &&
!poll &&
!mediaAttachments.length &&
!snapStates.statusQuotes[sKey] && (
<Card
card={card}
selfReferential={
card?.url === status.url || card?.url === status.uri
}
instance={currentInstance}
/>
)}
</>
)}
{!!card &&
/^https/i.test(card?.url) &&
!sensitive &&
!spoilerText &&
!poll &&
!mediaAttachments.length &&
!snapStates.statusQuotes[sKey] && (
<Card
card={card}
selfReferential={
card?.url === status.url || card?.url === status.uri
}
instance={currentInstance}
/>
)}
</div>
{!isSizeLarge && showCommentCount && (
<div class="content-comment-hint insignificant">
@ -2171,6 +2253,101 @@ function MultipleMediaFigure(props) {
);
}
function MediaFirstContainer(props) {
const { mediaAttachments, language, postID, instance } = props;
const moreThanOne = mediaAttachments.length > 1;
const carouselRef = useRef();
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
let handleScroll = () => {
const { clientWidth, scrollLeft } = carouselRef.current;
const index = Math.round(scrollLeft / clientWidth);
setCurrentIndex(index);
};
if (carouselRef.current) {
carouselRef.current.addEventListener('scroll', handleScroll, {
passive: true,
});
}
return () => {
if (carouselRef.current) {
carouselRef.current.removeEventListener('scroll', handleScroll);
}
};
}, []);
return (
<>
<div class="media-first-container" ref={carouselRef}>
{mediaAttachments.map((media, i) => (
<div class="media-first-item" key={media.id}>
<Media
media={media}
lang={language}
to={`/${instance}/s/${postID}?media-only=${i + 1}`}
/>
</div>
))}
{moreThanOne && (
<div class="media-carousel-controls">
<div class="carousel-indexer">
{currentIndex + 1}/{mediaAttachments.length}
</div>
<label class="media-carousel-button">
<button
type="button"
class="carousel-button"
hidden={currentIndex === 0}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
carouselRef.current.focus();
carouselRef.current.scrollTo({
left: carouselRef.current.clientWidth * (currentIndex - 1),
behavior: 'smooth',
});
}}
>
<Icon icon="arrow-left" />
</button>
</label>
<label class="media-carousel-button">
<button
type="button"
class="carousel-button"
hidden={currentIndex === mediaAttachments.length - 1}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
carouselRef.current.focus();
carouselRef.current.scrollTo({
left: carouselRef.current.clientWidth * (currentIndex + 1),
behavior: 'smooth',
});
}}
>
<Icon icon="arrow-right" />
</button>
</label>
</div>
)}
</div>
{moreThanOne && (
<div class="media-carousel-dots">
{mediaAttachments.map((media, i) => (
<span
key={media.id}
class={`carousel-dot ${i === currentIndex ? 'active' : ''}`}
/>
))}
</div>
)}
</>
);
}
function Card({ card, selfReferential, instance }) {
const snapStates = useSnapshot(states);
const {

Wyświetl plik

@ -1,5 +1,11 @@
import { memo } from 'preact/compat';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'preact/hooks';
import { useHotkeys } from 'react-hotkeys-hook';
import { InView } from 'react-intersection-observer';
import { useDebouncedCallback } from 'use-debounce';
@ -9,6 +15,7 @@ import FilterContext from '../utils/filter-context';
import { filteredItems, isFiltered } from '../utils/filters';
import states, { statusKey } from '../utils/states';
import statusPeek from '../utils/status-peek';
import { isMediaFirstInstance } from '../utils/store-utils';
import { groupBoosts, groupContext } from '../utils/timeline-utils';
import useInterval from '../utils/useInterval';
import usePageVisibility from '../utils/usePageVisibility';
@ -59,6 +66,8 @@ function Timeline({
console.debug('RENDER Timeline', id, refresh);
const mediaFirst = useMemo(() => isMediaFirstInstance(), []);
const allowGrouping = view !== 'media';
const loadItems = useDebouncedCallback(
(firstLoad) => {
@ -355,7 +364,9 @@ function Timeline({
<FilterContext.Provider value={filterContext}>
<div
id={`${id}-page`}
class="deck-container"
class={`deck-container ${
mediaFirst ? 'deck-container-media-first' : ''
}`}
ref={(node) => {
scrollableRef.current = node;
jRef.current = node;
@ -432,6 +443,7 @@ function Timeline({
view={view}
showFollowedTags={showFollowedTags}
showReplyParent={showReplyParent}
mediaFirst={mediaFirst}
/>
))}
{showMore &&
@ -443,14 +455,14 @@ function Timeline({
height: '20vh',
}}
>
<Status skeleton />
<Status skeleton mediaFirst={mediaFirst} />
</li>
<li
style={{
height: '25vh',
}}
>
<Status skeleton />
<Status skeleton mediaFirst={mediaFirst} />
</li>
</>
))}
@ -490,7 +502,7 @@ function Timeline({
/>
) : (
<li key={i}>
<Status skeleton />
<Status skeleton mediaFirst={mediaFirst} />
</li>
),
)}
@ -525,6 +537,7 @@ const TimelineItem = memo(
view,
showFollowedTags,
showReplyParent,
mediaFirst,
}) => {
console.debug('RENDER TimelineItem', status.id);
const { id: statusID, reblog, items, type, _pinned } = status;
@ -533,6 +546,7 @@ const TimelineItem = memo(
const url = instance
? `/${instance}/s/${actualStatusID}`
: `/s/${actualStatusID}`;
if (items) {
const fItems = filteredItems(items, filterContext);
let title = '';
@ -585,6 +599,7 @@ const TimelineItem = memo(
contentTextWeight
enableCommentHint
// allowFilters={allowFilters}
mediaFirst={mediaFirst}
/>
) : (
<Status
@ -594,6 +609,7 @@ const TimelineItem = memo(
contentTextWeight
enableCommentHint
// allowFilters={allowFilters}
mediaFirst={mediaFirst}
/>
)}
</Link>
@ -689,6 +705,7 @@ const TimelineItem = memo(
showFollowedTags={showFollowedTags}
showReplyParent={showReplyParent}
// allowFilters={allowFilters}
mediaFirst={mediaFirst}
/>
) : (
<Status
@ -698,6 +715,7 @@ const TimelineItem = memo(
showFollowedTags={showFollowedTags}
showReplyParent={showReplyParent}
// allowFilters={allowFilters}
mediaFirst={mediaFirst}
/>
)}
</Link>

Wyświetl plik

@ -151,7 +151,7 @@ function AccountStatuses() {
}
}
const results = [];
let results = [];
if (firstLoad) {
const { value } = await masto.v1.accounts
.$select(id)
@ -192,6 +192,26 @@ function AccountStatuses() {
}
const { value, done } = await accountStatusesIterator.current.next();
if (value?.length) {
// Check if value is same as pinned post (results)
// If the index for every post is the same, means API might not support pinned posts
if (results.length) {
let pinnedStatusesIds = [];
if (results[0]?.type === 'pinned') {
pinnedStatusesIds = results[0].id;
} else {
pinnedStatusesIds = results
.filter((status) => status._pinned)
.map((status) => status.id);
}
const containsAllPinned = pinnedStatusesIds.every((postId) =>
value.some((status) => status.id === postId),
);
if (containsAllPinned) {
// Remove pinned posts
results = [];
}
}
results.push(...value);
value.forEach((item) => {

Wyświetl plik

@ -71,7 +71,8 @@ function Following({ title, path, id, ...props }) {
.next();
let { value } = results;
console.log('checkForUpdates', latestItem.current, value);
if (value?.length) {
const valueContainsLatestItem = value[0]?.id === latestItem.current; // since_id might not be supported
if (value?.length && !valueContainsLatestItem) {
latestItem.current = value[0].id;
value = dedupeBoosts(value, instance);
value = filteredItems(value, 'home');

Wyświetl plik

@ -109,8 +109,9 @@ function Hashtags({ media: mediaView, columnMode, ...props }) {
})
.next();
let { value } = results;
value = filteredItems(value, 'public');
if (value?.length) {
const valueContainsLatestItem = value[0]?.id === latestItem.current; // since_id might not be supported
if (value?.length && !valueContainsLatestItem) {
value = filteredItems(value, 'public');
return true;
}
return false;

Wyświetl plik

@ -63,8 +63,9 @@ function List(props) {
since_id: latestItem.current,
});
let { value } = results;
value = filteredItems(value, 'home');
if (value?.length) {
const valueContainsLatestItem = value[0]?.id === latestItem.current; // since_id might not be supported
if (value?.length && !valueContainsLatestItem) {
value = filteredItems(value, 'home');
return true;
}
return false;

Wyświetl plik

@ -95,7 +95,9 @@ function Mentions({ columnMode, ...props }) {
latestConversationItem.current,
value,
);
if (value?.length) {
const valueContainsLatestItem =
value[0]?.id === latestConversationItem.current; // since_id might not be supported
if (value?.length && !valueContainsLatestItem) {
latestConversationItem.current = value[0].lastStatus.id;
return true;
}

Wyświetl plik

@ -247,7 +247,6 @@ function Notifications({ columnMode }) {
const lastHiddenTime = useRef();
usePageVisibility((visible) => {
let unsub;
if (visible) {
const timeDiff = Date.now() - lastHiddenTime.current;
if (!lastHiddenTime.current || timeDiff > 1000 * 3) {
@ -258,20 +257,16 @@ function Notifications({ columnMode }) {
} else {
lastHiddenTime.current = Date.now();
}
unsub = subscribeKey(states, 'notificationsShowNew', (v) => {
if (uiState === 'loading') {
return;
}
if (v) {
loadUpdates();
}
setShowNew(v);
});
}
return () => {
unsub?.();
};
});
useEffect(() => {
let unsub = subscribeKey(states, 'notificationsShowNew', (v) => {
if (uiState === 'loading') return;
if (v) loadUpdates();
setShowNew(v);
});
return () => unsub?.();
}, []);
const todayDate = new Date();
const yesterdayDate = new Date(todayDate - 24 * 60 * 60 * 1000);
@ -418,7 +413,7 @@ function Notifications({ columnMode }) {
{supportsFilteredNotifications && (
<button
type="button"
class="button plain"
class="button plain4"
onClick={() => {
setShowNotificationsSettings(true);
}}
@ -613,7 +608,7 @@ function Notifications({ columnMode }) {
</label>
</div>
<h2 class="timeline-header">Today</h2>
{showTodayEmpty && !!snapStates.notifications.length && (
{showTodayEmpty && (
<p class="ui-state insignificant">
{uiState === 'default' ? "You're all caught up." : <>&hellip;</>}
</p>

Wyświetl plik

@ -63,8 +63,9 @@ function Public({ local, columnMode, ...props }) {
})
.next();
let { value } = results;
value = filteredItems(value, 'public');
if (value?.length) {
const valueContainsLatestItem = value[0]?.id === latestItem.current; // since_id might not be supported
if (value?.length && !valueContainsLatestItem) {
value = filteredItems(value, 'public');
return true;
}
return false;

Wyświetl plik

@ -6,6 +6,7 @@ const statusPostRegexes = [
/\/notes\/([^\/]+)/i, // Misskey, Firefish
/^\/(?:notice|objects)\/([a-z0-9-]+)/i, // Pleroma
/\/@[^@\/]+@?[^\/]+?\/([^\/]+)/i, // Mastodon
/^\/p\/[^\/]+\/([^\/]+)/i, // Pixelfed
];
export function getInstanceStatusObject(url) {

Wyświetl plik

@ -126,3 +126,8 @@ export function getCurrentInstanceConfiguration() {
const instance = getCurrentInstance();
return getInstanceConfiguration(instance);
}
export function isMediaFirstInstance() {
const instance = getCurrentInstance();
return /pixelfed/i.test(instance?.version);
}