Porównaj commity

...

15 Commity

Autor SHA1 Wiadomość Data
Helge 9ad60db82d
Merge 99e5c1036d into c3bcf3d595 2024-04-11 17:28:36 +02: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
Helge 99e5c1036d Enable running using http
This means that by adding PHANPY_SCHEME=http to the file ".env"
phanpy will use http instead of https to connect to remote instances.

This is useful to test local versions of various Fediverse applications.
These can be created following the instructions on

https://funfedi.dev/quickstart/#running-an-application-from-the-fediverse-pasture
2024-03-09 17:31:36 +01:00
21 zmienionych plików z 974 dodań i 416 usunięć

Wyświetl plik

@ -256,6 +256,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

@ -12,7 +12,7 @@ import { getAuthorizationURL, registerApplication } from '../utils/auth';
import store from '../utils/store';
import useTitle from '../utils/useTitle';
const { PHANPY_DEFAULT_INSTANCE: DEFAULT_INSTANCE } = import.meta.env;
const { PHANPY_DEFAULT_INSTANCE: DEFAULT_INSTANCE, PHANPY_SCHEME: SCHEME = 'https' } = import.meta.env;
function Login() {
useTitle('Log in');
@ -85,9 +85,11 @@ function Login() {
.replace(/^@?[^@]+@/, '') // Remove @?acct@
.trim()
: null;
const instanceTextLooksLikeDomain =
/[^\s\r\n\t\/\\]+\.[^\s\r\n\t\/\\]+/.test(cleanInstanceText) &&
!/[\s\/\\@]/.test(cleanInstanceText);
const instanceTextLooksLikeDomain =
(/[^\s\r\n\t\/\\]+\.[^\s\r\n\t\/\\]+/.test(cleanInstanceText) &&
!/[\s\/\\@]/.test(cleanInstanceText)) || SCHEME === "http";
console.log(SCHEME)
const instancesSuggestions = cleanInstanceText
? instancesList

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

@ -9,6 +9,8 @@ import {
saveAccount,
} from './store-utils';
const { PHANPY_SCHEME: SCHEME = 'https' } = import.meta.env;
// Default *fallback* instance
const DEFAULT_INSTANCE = 'mastodon.social';
@ -36,7 +38,9 @@ export function initClient({ instance, accessToken }) {
.replace(/\/+$/, '')
.toLowerCase();
}
const url = instance ? `https://${instance}` : `https://${DEFAULT_INSTANCE}`;
const url = instance
? `${SCHEME}://${instance}`
: `${SCHEME}://${DEFAULT_INSTANCE}`;
const masto = createRestAPIClient({
url,

Wyświetl plik

@ -1,5 +1,8 @@
const { PHANPY_CLIENT_NAME: CLIENT_NAME, PHANPY_WEBSITE: WEBSITE } = import.meta
.env;
const {
PHANPY_CLIENT_NAME: CLIENT_NAME,
PHANPY_WEBSITE: WEBSITE,
PHANPY_SCHEME: SCHEME = 'https',
} = import.meta.env;
const SCOPES = 'read write follow push';
@ -11,7 +14,7 @@ export async function registerApplication({ instanceURL }) {
website: WEBSITE,
});
const registrationResponse = await fetch(
`https://${instanceURL}/api/v1/apps`,
`${SCHEME}://${instanceURL}/api/v1/apps`,
{
method: 'POST',
headers: {
@ -33,7 +36,7 @@ export async function getAuthorizationURL({ instanceURL, client_id }) {
// redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
response_type: 'code',
});
const authorizationURL = `https://${instanceURL}/oauth/authorize?${authorizationParams.toString()}`;
const authorizationURL = `${SCHEME}://${instanceURL}/oauth/authorize?${authorizationParams.toString()}`;
return authorizationURL;
}
@ -51,7 +54,7 @@ export async function getAccessToken({
code,
scope: SCOPES,
});
const tokenResponse = await fetch(`https://${instanceURL}/oauth/token`, {
const tokenResponse = await fetch(`${SCHEME}://${instanceURL}/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',

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);
}