diff --git a/README.md b/README.md
index 62500f20..bcf52dc4 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,10 @@ Everything is designed and engineered for my own use case, following my taste an
- 🌗 Light/dark/auto theme
- 🔔 Grouped notifications
- 🪺 Nested replies view
+- 📬 Unsent draft recovery
+- 🎠 Boosts Carousel™️
+- ⚡ Shortcuts™️ with view modes like multi-column or tab bar
+- #️⃣ Multi-hashtag timeline
## Design decisions
diff --git a/package-lock.json b/package-lock.json
index b3adf415..22d02159 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,9 +8,10 @@
"name": "phanpy",
"version": "0.1.0",
"dependencies": {
+ "@formatjs/intl-localematcher": "~0.2.32",
"@github/text-expander-element": "~2.3.0",
"@iconify-icons/mingcute": "~1.2.4",
- "@szhsin/react-menu": "~3.4.1",
+ "@szhsin/react-menu": "~3.5.1",
"dayjs": "~1.11.7",
"dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2",
@@ -21,9 +22,9 @@
"mem": "~9.0.2",
"p-retry": "~5.1.2",
"p-throttle": "~5.0.0",
- "preact": "~10.12.1",
+ "preact": "~10.13.0",
"react-hotkeys-hook": "~4.3.7",
- "react-intersection-observer": "~9.4.2",
+ "react-intersection-observer": "~9.4.3",
"react-router-dom": "6.6.2",
"string-length": "~5.0.1",
"swiped-events": "~1.1.7",
@@ -35,12 +36,13 @@
},
"devDependencies": {
"@preact/preset-vite": "~2.5.0",
- "@trivago/prettier-plugin-sort-imports": "~4.1.0",
+ "@trivago/prettier-plugin-sort-imports": "~4.1.1",
"postcss": "~8.4.21",
"postcss-dark-theme-class": "~0.7.3",
"postcss-preset-env": "~8.0.1",
"twitter-text": "~3.1.0",
"vite": "~4.1.4",
+ "vite-plugin-generate-file": "~0.0.4",
"vite-plugin-html-config": "~1.0.11",
"vite-plugin-html-env": "~1.2.7",
"vite-plugin-pwa": "~0.14.4",
@@ -2507,6 +2509,14 @@
"node": ">=12"
}
},
+ "node_modules/@formatjs/intl-localematcher": {
+ "version": "0.2.32",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz",
+ "integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@github/combobox-nav": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.1.5.tgz",
@@ -2811,9 +2821,9 @@
}
},
"node_modules/@szhsin/react-menu": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.4.1.tgz",
- "integrity": "sha512-Pxt7Kyp3yuX7zkT5tjdLRJGNFMa5Tx4BP+01gJ/dnMmHQpI1H2or9gEC0X+t3cLldO3LGmm4ViGypNCmQLv/4A==",
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.5.1.tgz",
+ "integrity": "sha512-bTCfVNBSReG4+mnbN8n2OQWZ3DRPlJgMIBJFepPfDLiRzNSe5lbZ8Z5Kjiv9nuPLHOu3jSaybxgYJj/Dn8n75Q==",
"dependencies": {
"prop-types": "^15.7.2",
"react-transition-state": "^1.1.5"
@@ -2824,9 +2834,9 @@
}
},
"node_modules/@trivago/prettier-plugin-sort-imports": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.1.0.tgz",
- "integrity": "sha512-aTr6QPFaPAAzPRFn9yWB/9yKi3ZAFqfGpxIGLPWuQfYJFGUed+W3KKwxntsoCiNvNE2iuKOg6haMo5KG8WXltg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.1.1.tgz",
+ "integrity": "sha512-dQ2r2uzNr1x6pJsuh/8x0IRA3CBUB+pWEW3J/7N98axqt7SQSm+2fy0FLNXvXGg77xEDC7KHxJlHfLYyi7PDcw==",
"dev": true,
"dependencies": {
"@babel/generator": "7.17.7",
@@ -3099,6 +3109,12 @@
"node": ">=4"
}
},
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
"node_modules/async": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
@@ -4541,6 +4557,18 @@
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
@@ -5630,9 +5658,9 @@
"dev": true
},
"node_modules/preact": {
- "version": "10.12.1",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
- "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
+ "version": "10.13.0",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.13.0.tgz",
+ "integrity": "sha512-ERdIdUpR6doqdaSIh80hvzebHB7O6JxycOhyzAeLEchqOq/4yueslQbfnPwXaNhAYacFTyCclhwkEbOumT0tHw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
@@ -5768,9 +5796,9 @@
}
},
"node_modules/react-intersection-observer": {
- "version": "9.4.2",
- "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.2.tgz",
- "integrity": "sha512-AdK+ryzZ7U9ZJYttDUZ8q2Am3nqE0exg5Ryl5Y124KeVsix/1hGZPbdu58EqA98TwnzwDNWHxg/kwNawmIiUig==",
+ "version": "9.4.3",
+ "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.3.tgz",
+ "integrity": "sha512-WNRqMQvKpupr6MzecAQI0Pj0+JQong307knLP4g/nBex7kYfIaZsPpXaIhKHR+oV8z+goUbH9e10j6lGRnTzlQ==",
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
@@ -6631,6 +6659,88 @@
}
}
},
+ "node_modules/vite-plugin-generate-file": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/vite-plugin-generate-file/-/vite-plugin-generate-file-0.0.4.tgz",
+ "integrity": "sha512-5cdsdSRGdtUxbAGdaXlW3Wiy46lK7LYm2FaTy42KCFT9fS6kiR+Ynjsjt7UEuE4nfStvCS9bVk9+YtsEIJ+Vhw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2",
+ "ejs": "^3.1.6",
+ "js-yaml": "^4.1.0",
+ "mime-types": "^2.1.35"
+ }
+ },
+ "node_modules/vite-plugin-generate-file/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/vite-plugin-generate-file/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/vite-plugin-generate-file/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/vite-plugin-generate-file/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/vite-plugin-generate-file/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/vite-plugin-generate-file/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/vite-plugin-html-config": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/vite-plugin-html-config/-/vite-plugin-html-config-1.0.11.tgz",
@@ -8593,6 +8703,14 @@
"dev": true,
"optional": true
},
+ "@formatjs/intl-localematcher": {
+ "version": "0.2.32",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz",
+ "integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==",
+ "requires": {
+ "tslib": "^2.4.0"
+ }
+ },
"@github/combobox-nav": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.1.5.tgz",
@@ -8836,18 +8954,18 @@
}
},
"@szhsin/react-menu": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.4.1.tgz",
- "integrity": "sha512-Pxt7Kyp3yuX7zkT5tjdLRJGNFMa5Tx4BP+01gJ/dnMmHQpI1H2or9gEC0X+t3cLldO3LGmm4ViGypNCmQLv/4A==",
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.5.1.tgz",
+ "integrity": "sha512-bTCfVNBSReG4+mnbN8n2OQWZ3DRPlJgMIBJFepPfDLiRzNSe5lbZ8Z5Kjiv9nuPLHOu3jSaybxgYJj/Dn8n75Q==",
"requires": {
"prop-types": "^15.7.2",
"react-transition-state": "^1.1.5"
}
},
"@trivago/prettier-plugin-sort-imports": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.1.0.tgz",
- "integrity": "sha512-aTr6QPFaPAAzPRFn9yWB/9yKi3ZAFqfGpxIGLPWuQfYJFGUed+W3KKwxntsoCiNvNE2iuKOg6haMo5KG8WXltg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.1.1.tgz",
+ "integrity": "sha512-dQ2r2uzNr1x6pJsuh/8x0IRA3CBUB+pWEW3J/7N98axqt7SQSm+2fy0FLNXvXGg77xEDC7KHxJlHfLYyi7PDcw==",
"dev": true,
"requires": {
"@babel/generator": "7.17.7",
@@ -9078,6 +9196,12 @@
"color-convert": "^1.9.0"
}
},
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
"async": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
@@ -10127,6 +10251,15 @@
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
"jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
@@ -10831,9 +10964,9 @@
"dev": true
},
"preact": {
- "version": "10.12.1",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
- "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg=="
+ "version": "10.13.0",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.13.0.tgz",
+ "integrity": "sha512-ERdIdUpR6doqdaSIh80hvzebHB7O6JxycOhyzAeLEchqOq/4yueslQbfnPwXaNhAYacFTyCclhwkEbOumT0tHw=="
},
"prettier": {
"version": "2.8.0",
@@ -10918,9 +11051,9 @@
"requires": {}
},
"react-intersection-observer": {
- "version": "9.4.2",
- "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.2.tgz",
- "integrity": "sha512-AdK+ryzZ7U9ZJYttDUZ8q2Am3nqE0exg5Ryl5Y124KeVsix/1hGZPbdu58EqA98TwnzwDNWHxg/kwNawmIiUig==",
+ "version": "9.4.3",
+ "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.3.tgz",
+ "integrity": "sha512-WNRqMQvKpupr6MzecAQI0Pj0+JQong307knLP4g/nBex7kYfIaZsPpXaIhKHR+oV8z+goUbH9e10j6lGRnTzlQ==",
"requires": {}
},
"react-is": {
@@ -11518,6 +11651,69 @@
"rollup": "^3.10.0"
}
},
+ "vite-plugin-generate-file": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/vite-plugin-generate-file/-/vite-plugin-generate-file-0.0.4.tgz",
+ "integrity": "sha512-5cdsdSRGdtUxbAGdaXlW3Wiy46lK7LYm2FaTy42KCFT9fS6kiR+Ynjsjt7UEuE4nfStvCS9bVk9+YtsEIJ+Vhw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.2",
+ "ejs": "^3.1.6",
+ "js-yaml": "^4.1.0",
+ "mime-types": "^2.1.35"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
"vite-plugin-html-config": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/vite-plugin-html-config/-/vite-plugin-html-config-1.0.11.tgz",
diff --git a/package.json b/package.json
index e457306e..2ee389b7 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,10 @@
"sourcemap": "npx source-map-explorer dist/assets/*.js"
},
"dependencies": {
+ "@formatjs/intl-localematcher": "~0.2.32",
"@github/text-expander-element": "~2.3.0",
"@iconify-icons/mingcute": "~1.2.4",
- "@szhsin/react-menu": "~3.4.1",
+ "@szhsin/react-menu": "~3.5.1",
"dayjs": "~1.11.7",
"dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2",
@@ -23,9 +24,9 @@
"mem": "~9.0.2",
"p-retry": "~5.1.2",
"p-throttle": "~5.0.0",
- "preact": "~10.12.1",
+ "preact": "~10.13.0",
"react-hotkeys-hook": "~4.3.7",
- "react-intersection-observer": "~9.4.2",
+ "react-intersection-observer": "~9.4.3",
"react-router-dom": "6.6.2",
"string-length": "~5.0.1",
"swiped-events": "~1.1.7",
@@ -37,12 +38,13 @@
},
"devDependencies": {
"@preact/preset-vite": "~2.5.0",
- "@trivago/prettier-plugin-sort-imports": "~4.1.0",
+ "@trivago/prettier-plugin-sort-imports": "~4.1.1",
"postcss": "~8.4.21",
"postcss-dark-theme-class": "~0.7.3",
"postcss-preset-env": "~8.0.1",
"twitter-text": "~3.1.0",
"vite": "~4.1.4",
+ "vite-plugin-generate-file": "~0.0.4",
"vite-plugin-html-config": "~1.0.11",
"vite-plugin-html-env": "~1.2.7",
"vite-plugin-pwa": "~0.14.4",
diff --git a/public/sw.js b/public/sw.js
index ddf9f42d..14514039 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -21,8 +21,8 @@ const imageRoute = new Route(
cacheName: 'remote-images',
plugins: [
new ExpirationPlugin({
- maxEntries: 100,
- maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
+ maxEntries: 50,
+ maxAgeSeconds: 3 * 24 * 60 * 60, // 3 days
purgeOnQuotaError: true,
}),
new CacheableResponsePlugin({
diff --git a/src/app.css b/src/app.css
index 7a53f6c9..5a5e10b9 100644
--- a/src/app.css
+++ b/src/app.css
@@ -109,9 +109,8 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
border-bottom: var(--hairline-width) solid var(--divider-color);
min-height: 3em;
display: grid;
- grid-template-columns: 1fr max-content 1fr;
+ grid-template-columns: 1fr minmax(0, max-content) 1fr;
align-items: center;
- text-overflow: ellipsis;
white-space: nowrap;
}
.deck > header .header-grid > .header-side:last-of-type {
@@ -126,6 +125,9 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
padding: 0;
font-size: 1.2em;
text-align: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.deck > header .header-grid.header-grid-2 {
grid-template-columns: 1fr max-content;
@@ -749,6 +751,13 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
.updates-button .icon {
vertical-align: top;
}
+@media (pointer: coarse) {
+ .updates-button:after {
+ content: '';
+ position: absolute;
+ inset: -16px;
+ }
+}
/* BOX */
@@ -842,7 +851,6 @@ button.carousel-dot {
backdrop-filter: blur(12px) invert(0.25);
}
button.carousel-dot {
- color: var(--text-insignificant-color) !important;
font-weight: bold;
backdrop-filter: none !important;
transition: all 0.2s;
@@ -850,8 +858,11 @@ button.carousel-dot {
button.carousel-dot[disabled] {
pointer-events: none;
}
-button.carousel-dot:is(:hover, :focus, .active, [disabled].active) {
- color: var(--button-text-color) !important;
+button.carousel-dot:not(.active) {
+ opacity: 0.5;
+}
+button.carousel-dot:not(.active):is(:hover, :focus) {
+ opacity: 1;
}
button.carousel-dot:is(.active, [disabled].active) {
opacity: 1;
@@ -936,6 +947,7 @@ body:has(.status-deck) .media-post-link {
transform: translateY(200%);
pointer-events: none;
user-select: none;
+ opacity: 0;
}
#compose-button .icon {
transition: transform 0.3s ease-in-out;
@@ -954,6 +966,12 @@ body:has(.status-deck) .media-post-link {
#compose-button .icon {
filter: drop-shadow(0 1px 2px var(--button-bg-color));
}
+@media (max-width: calc(40em - 1px)) {
+ #app:has(#shortcuts .tab-bar) #compose-button {
+ bottom: calc(16px + 52px);
+ bottom: calc(16px + env(safe-area-inset-bottom) + 52px);
+ }
+}
/* SHEET */
@@ -1026,6 +1044,11 @@ body:has(.status-deck) .media-post-link {
/* MENU POPUP */
+.szh-menu-container:has(.szh-menu--state-open) {
+ inset: 0;
+ inset: env(safe-area-inset-top) env(safe-area-inset-right)
+ env(safe-area-inset-bottom) env(safe-area-inset-left);
+}
.szh-menu {
padding: 8px 0;
margin: 0;
@@ -1033,7 +1056,7 @@ body:has(.status-deck) .media-post-link {
background-color: var(--bg-color);
border: 1px solid var(--outline-color);
border-radius: 8px;
- box-shadow: 0 3px 6px var(--drop-shadow-color);
+ box-shadow: 0 3px 16px -3px var(--drop-shadow-color);
text-align: left;
animation: appear-smooth 0.15s ease-in-out;
width: 16em;
@@ -1043,25 +1066,45 @@ body:has(.status-deck) .media-post-link {
.szh-menu__item--focusable {
background-color: transparent;
}
+.szh-menu__header {
+ margin: -8px 0 8px;
+ padding: 8px 16px;
+ color: var(--text-insignificant-color);
+ font-size: 90%;
+ background-color: var(--bg-faded-color);
+ /* background-image: linear-gradient(to top, var(--bg-faded-color), transparent); */
+ text-shadow: 0 1px 0 var(--bg-color);
+ line-height: 1.2;
+ /* border-bottom: 1px solid var(--outline-color); */
+}
+.szh-menu__header * {
+ vertical-align: middle;
+}
.szh-menu .szh-menu__item {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ line-height: 1;
padding: 8px 16px !important;
transition: all 0.1s ease-in-out;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ text-decoration: none;
}
.szh-menu .szh-menu__item * {
vertical-align: middle;
}
.szh-menu .szh-menu__item a {
+ flex: 1;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
+ gap: 8px;
color: inherit;
text-decoration: none;
padding: 8px 16px !important;
margin: -8px -16px !important;
- gap: 8px;
}
.szh-menu .szh-menu__item a.is-active {
font-weight: bold;
@@ -1073,7 +1116,7 @@ body:has(.status-deck) .media-post-link {
.szh-menu__item:not(.szh-menu__item--disabled, .szh-menu__item--hover) {
color: var(--text-color);
}
-.szh-menu .szh-menu__item--hover {
+.szh-menu .szh-menu__item--hover:not(.menu-field) {
color: var(--button-text-color);
background-color: var(--button-bg-color);
}
@@ -1082,11 +1125,27 @@ body:has(.status-deck) .media-post-link {
}
.szh-menu .szh-menu__item .menu-grow {
flex-grow: 1;
+ text-overflow: ellipsis;
+ overflow: hidden;
}
.szh-menu .szh-menu__item .menu-shortcut {
opacity: 0.5;
font-weight: normal;
}
+.szh-menu .szh-menu__item form {
+ display: flex;
+ flex: 1;
+ gap: 8px;
+ align-items: center;
+}
+.szh-menu .szh-menu__item form > input[type='text'] {
+ flex-grow: 1;
+ width: 100%;
+}
+.szh-menu .szh-menu__item--hover .danger-icon {
+ color: var(--red-color);
+ opacity: 1;
+}
/* GLASS MENU */
@@ -1161,7 +1220,7 @@ meter.donut:is(.danger, .explode):after {
.shiny-pill {
color: var(--button-text-color);
- text-shadow: 0 -1px var(--drop-shadow-color);
+ text-shadow: 0 calc(var(--hairline-width) * -1) var(--drop-shadow-color);
background-color: var(--button-bg-color);
background-image: linear-gradient(
160deg,
@@ -1228,43 +1287,6 @@ meter.donut:is(.danger, .explode):after {
/* content-visibility: hidden; */
}
-/* TAB BAR */
-
-#tab-bar:not([hidden]) {
- position: fixed;
- bottom: 16px;
- bottom: max(16px, env(safe-area-inset-bottom));
- width: calc(100% - 32px);
- max-width: calc(var(--main-width) - 32px);
- z-index: 100;
- display: flex;
- background-color: var(--bg-blur-color);
- backdrop-filter: blur(16px) saturate(3);
- border: var(--hairline-width) solid var(--outline-color);
- border-radius: 16px;
- box-shadow: 0 8px 32px var(--outline-color);
-}
-#tab-bar li {
- flex-grow: 1;
- margin: 0;
- padding: 0;
- list-style: none;
-}
-#tab-bar li a {
- text-align: center;
- padding: 16px 0;
- display: block;
- color: var(--text-insignificant-color);
-}
-#tab-bar li a.is-active {
- color: var(--link-color);
- background-image: radial-gradient(
- closest-side at 50% 50%,
- var(--bg-blur-color),
- transparent 75%
- );
-}
-
/* 404 */
#not-found-page {
@@ -1483,15 +1505,19 @@ ul.link-list li a .icon {
margin-top: 24px;
}
.timeline:not(.flat) > li {
+ --item-radius: 16px;
border: 1px solid var(--divider-color);
margin: 16px 0;
background-color: var(--bg-color);
- border-radius: 16px;
+ border-radius: var(--item-radius);
overflow: hidden;
box-shadow: 0px 1px var(--bg-blur-color);
transition: transform 0.4s var(--timing-function);
--back-transition: transform 0.4s ease-out;
}
+ .timeline:not(.flat) > li > a {
+ border-radius: var(--item-radius);
+ }
.timeline:not(.flat) > li:not(:has(.status-carousel)) {
transform: translate3d(0, 0, 0);
}
diff --git a/src/app.jsx b/src/app.jsx
index 113b74ae..ebbce5dd 100644
--- a/src/app.jsx
+++ b/src/app.jsx
@@ -14,14 +14,11 @@ import {
useLocation,
useNavigate,
} from 'react-router-dom';
-import Toastify from 'toastify-js';
import { useSnapshot } from 'valtio';
import Account from './components/account';
import Compose from './components/compose';
import Drafts from './components/drafts';
-import Icon from './components/icon';
-import Link from './components/link';
import Loader from './components/loader';
import MediaModal from './components/media-modal';
import Modal from './components/modal';
@@ -53,9 +50,11 @@ import {
initPreferences,
} from './utils/api';
import { getAccessToken } from './utils/auth';
+import showToast from './utils/show-toast';
import states, { getStatus, saveStatus } from './utils/states';
import store from './utils/store';
import { getCurrentAccount } from './utils/store-utils';
+import useInterval from './utils/useInterval';
import usePageVisibility from './utils/usePageVisibility';
window.__STATES__ = states;
@@ -139,11 +138,24 @@ function App() {
};
const focusDeck = () => {
let timer = setTimeout(() => {
- const page = document.getElementById(locationDeckMap[location.pathname]);
- console.debug('FOCUS', location.pathname, page);
- if (page) {
- page.focus();
+ const columns = document.getElementById('columns');
+ if (columns) {
+ // Focus first column
+ columns.querySelector('.deck-container')?.focus?.();
+ } else {
+ // Focus last deck
+ const pages = document.querySelectorAll('.deck-container');
+ const page = pages[pages.length - 1]; // last one
+ if (page && page.tabIndex === -1) {
+ console.log('FOCUS', page);
+ page.focus();
+ }
}
+ // const page = document.getElementById(locationDeckMap[location.pathname]);
+ // console.debug('FOCUS', location.pathname, page);
+ // if (page) {
+ // page.focus();
+ // }
}, 100);
return () => clearTimeout(timer);
};
@@ -175,7 +187,7 @@ function App() {
const notificationStream = useRef();
useEffect(() => {
if (isLoggedIn && visible) {
- const { masto } = api();
+ const { masto, instance } = api();
(async () => {
// 1. Get the latest notification
if (states.notificationsLast) {
@@ -200,6 +212,11 @@ function App() {
notificationStream.current.on('notification', (notification) => {
console.log('🔔🔔 Notification', notification);
+ if (notification.status) {
+ saveStatus(notification.status, instance, {
+ skipThreading: true,
+ });
+ }
states.notificationsShowNew = true;
});
@@ -236,6 +253,18 @@ function App() {
return !/^\/(login|welcome)/.test(pathname);
}, [location]);
+ useInterval(() => {
+ console.log('✨ Check app update');
+ fetch('./version.json')
+ .then((r) => r.json())
+ .then((info) => {
+ if (info) states.appVersion = info;
+ })
+ .catch((e) => {
+ console.error(e);
+ });
+ }, visible && 1000 * 60 * 60); // 1 hour
+
return (
<>
@@ -281,24 +310,12 @@ function App() {
} />
-
- {!snapStates.settings.shortcutsColumnsMode && }
+
+ {!snapStates.settings.shortcutsColumnsMode &&
+ snapStates.settings.shortcutsViewMode !== 'multi-column' && (
+
+ )}
+
{!!snapStates.showCompose && (
{
- const toast = Toastify({
- className: 'shiny-pill',
- text: 'Status posted. Check it out.',
- duration: 10_000, // 10 seconds
- gravity: 'bottom',
- position: 'center',
- // destination: `/#/s/${newStatus.id}`,
- onClick: () => {
- toast.hideToast();
- states.prevLocation = location;
- navigate(
- instance
- ? `/${instance}/s/${newStatus.id}`
- : `/s/${newStatus.id}`,
- );
- },
- });
- toast.showToast();
- }, 1000);
+ showToast({
+ text: 'Status posted. Check it out.',
+ delay: 1000,
+ duration: 10_000, // 10 seconds
+ onClick: (toast) => {
+ toast.hideToast();
+ states.prevLocation = location;
+ navigate(
+ instance
+ ? `/${instance}/s/${newStatus.id}`
+ : `/s/${newStatus.id}`,
+ );
+ },
+ });
}
}}
/>
diff --git a/src/components/account.jsx b/src/components/account.jsx
index 73ed46be..76dc18c7 100644
--- a/src/components/account.jsx
+++ b/src/components/account.jsx
@@ -7,6 +7,7 @@ import { api } from '../utils/api';
import emojifyText from '../utils/emojify-text';
import enhanceContent from '../utils/enhance-content';
import handleContentLinks from '../utils/handle-content-links';
+import niceDateTime from '../utils/nice-date-time';
import shortenNumber from '../utils/shorten-number';
import states, { hideAllModals } from '../utils/states';
import store from '../utils/store';
@@ -205,11 +206,9 @@ function Account({ account, instance: propInstance, onClose }) {
diff --git a/src/components/compose.css b/src/components/compose.css
index 8c0d0956..a56bc469 100644
--- a/src/components/compose.css
+++ b/src/components/compose.css
@@ -60,7 +60,7 @@
animation: appear-up 1s ease-in-out;
overflow: auto;
}
-#compose-container .status-preview .hashtag {
+#compose-container .status-preview :is(.hashtag, .time) {
/* Prevent hashtags from being clickable */
/* TODO: maybe use a different solution? */
pointer-events: none;
diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index 848e8c8f..0eafecf0 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -1,5 +1,6 @@
import './compose.css';
+import { match } from '@formatjs/intl-localematcher';
import '@github/text-expander-element';
import equal from 'fast-deep-equal';
import { forwardRef } from 'preact/compat';
@@ -84,7 +85,11 @@ const observer = new IntersectionObserver((entries) => {
});
observer.observe(menu);
-const DEFAULT_LANG = 'en';
+const DEFAULT_LANG = match(
+ [new Intl.DateTimeFormat().resolvedOptions().locale, ...navigator.languages],
+ supportedLanguages.map((l) => l[0]),
+ 'en',
+);
// https://github.com/mastodon/mastodon/blob/c4a429ed47e85a6bbf0d470a41cc2f64cf120c19/app/javascript/mastodon/features/compose/util/counter.js
const urlRegexObj = new RegExp(urlRegex.source, urlRegex.flags);
@@ -772,7 +777,7 @@ function Compose({
editStatus.id,
params,
);
- saveStatus(newStatus, {
+ saveStatus(newStatus, instance, {
skipThreading: true,
});
} else {
@@ -1014,7 +1019,7 @@ function Compose({
onChange={(e) => {
const { value } = e.target;
setLanguage(value || DEFAULT_LANG);
- store.session.set('language', value);
+ store.session.set('currentLanguage', value || DEFAULT_LANG);
}}
disabled={uiState === 'loading'}
>
diff --git a/src/components/drafts.jsx b/src/components/drafts.jsx
index 28c7c35d..445add1b 100644
--- a/src/components/drafts.jsx
+++ b/src/components/drafts.jsx
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useReducer, useState } from 'react';
import { api } from '../utils/api';
import db from '../utils/db';
+import niceDateTime from '../utils/nice-date-time';
import states from '../utils/states';
import { getCurrentAccountNS } from '../utils/store-utils';
@@ -67,7 +68,6 @@ function Drafts() {
{drafts.map((draft) => {
const { updatedAt, key, draftStatus, replyTo } = draft;
- const currentYear = new Date().getFullYear();
const updatedAtDate = new Date(updatedAt);
return (
-
@@ -81,19 +81,7 @@ function Drafts() {
>
)}
- {Intl.DateTimeFormat('en', {
- // Show year if not current year
- year:
- updatedAtDate.getFullYear() === currentYear
- ? undefined
- : 'numeric',
- month: 'short',
- day: 'numeric',
- weekday: 'short',
- hour: 'numeric',
- minute: '2-digit',
- second: '2-digit',
- }).format(updatedAtDate)}
+ {niceDateTime(updatedAtDate)}
}
>
+ {!!snapStates.appVersion?.commitHash &&
+ __COMMIT_HASH__ !== snapStates.appVersion.commitHash && (
+ <>
+
+
+ >
+ )}
Home
diff --git a/src/components/shortcuts-settings.css b/src/components/shortcuts-settings.css
index bec8246a..0758f967 100644
--- a/src/components/shortcuts-settings.css
+++ b/src/components/shortcuts-settings.css
@@ -26,44 +26,34 @@
#shortcuts-settings-container .shortcuts-list li .shortcut-text {
flex-grow: 1;
}
+#shortcuts-settings-container .shortcuts-list li .shortcut-actions {
+ flex-shrink: 0;
+}
+
+#shortcuts-settings-container .shortcuts-view-mode {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
#shortcuts-settings-container summary {
cursor: pointer;
}
-#shortcuts-settings-container form {
- display: flex;
- gap: 8px;
- flex-wrap: wrap;
- align-items: center;
- padding: 16px;
- background-color: var(--bg-faded-color);
- border-radius: 16px;
+#shortcut-settings-form form > * {
}
-#shortcuts-settings-container form header {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-#shortcuts-settings-container form > * {
- flex-basis: max(320px, 100%);
- margin: 0;
- padding: 0;
-}
-
-#shortcuts-settings-container form label {
+#shortcut-settings-form label {
display: flex;
flex-direction: row;
gap: 8px;
align-items: center;
}
-#shortcuts-settings-container form label > span:first-child {
+#shortcut-settings-form label > span:first-child {
flex-basis: 5em;
text-align: right;
}
-#shortcuts-settings-container form :is(input[type='text'], select) {
+#shortcut-settings-form :is(input[type='text'], select) {
flex-grow: 1;
flex-basis: 70%;
flex-shrink: 1;
diff --git a/src/components/shortcuts-settings.jsx b/src/components/shortcuts-settings.jsx
index d933ce27..ecf39c3d 100644
--- a/src/components/shortcuts-settings.jsx
+++ b/src/components/shortcuts-settings.jsx
@@ -9,6 +9,7 @@ import states from '../utils/states';
import AsyncText from './AsyncText';
import Icon from './icon';
+import Modal from './modal';
const SHORTCUTS_LIMIT = 9;
@@ -75,22 +76,26 @@ const TYPE_PARAMS = {
text: '#',
name: 'hashtag',
type: 'text',
- placeholder: 'e.g PixelArt',
+ placeholder: 'e.g. PixelArt (Max 5, space-separated)',
+ pattern: '[^#]+',
},
],
};
export const SHORTCUTS_META = {
following: {
- title: 'Home / Following',
- path: (_, index) => (index === 0 ? '/' : '/following'),
+ id: 'home',
+ title: (_, index) => (index === 0 ? 'Home' : 'Following'),
+ path: '/',
icon: 'home',
},
notifications: {
+ id: 'notifications',
title: 'Notifications',
path: '/notifications',
icon: 'notification',
},
list: {
+ id: 'list',
title: mem(
async ({ id }) => {
const list = await api().masto.v1.lists.fetch(id);
@@ -104,17 +109,20 @@ export const SHORTCUTS_META = {
icon: 'list',
},
public: {
+ id: 'public',
title: ({ local, instance }) =>
`${local ? 'Local' : 'Federated'} (${instance})`,
path: ({ local, instance }) => `/${instance}/p${local ? '/l' : ''}`,
icon: ({ local }) => (local ? 'group' : 'earth'),
},
search: {
+ id: 'search',
title: ({ query }) => query,
path: ({ query }) => `/search?q=${query}`,
icon: 'search',
},
'account-statuses': {
+ id: 'account-statuses',
title: mem(
async ({ id }) => {
const account = await api().masto.v1.accounts.fetch(id);
@@ -128,18 +136,21 @@ export const SHORTCUTS_META = {
icon: 'user',
},
bookmarks: {
+ id: 'bookmarks',
title: 'Bookmarks',
path: '/b',
icon: 'bookmark',
},
favourites: {
+ id: 'favourites',
title: 'Favourites',
path: '/f',
icon: 'heart',
},
hashtag: {
+ id: 'hashtag',
title: ({ hashtag }) => hashtag,
- path: ({ hashtag }) => `/t/${hashtag}`,
+ path: ({ hashtag }) => `/t/${hashtag.split(/\s+/).join('+')}`,
icon: 'hashtag',
},
};
@@ -151,6 +162,7 @@ function ShortcutsSettings() {
const [lists, setLists] = useState([]);
const [followedHashtags, setFollowedHashtags] = useState([]);
+ const [showForm, setShowForm] = useState(false);
useEffect(() => {
(async () => {
@@ -196,10 +208,21 @@ function ShortcutsSettings() {
- Specify a list of shortcuts that'll appear in the floating Shortcuts
- button.
+
-
+ {/*
Experimental Multi-column mode
@@ -215,7 +238,7 @@ function ShortcutsSettings() {
Show shortcuts in multiple columns instead of the floating button.
-
+
*/}
{shortcuts.length > 0 ? (
{shortcuts.map((shortcut, i) => {
@@ -224,10 +247,10 @@ function ShortcutsSettings() {
if (!SHORTCUTS_META[type]) return null;
let { icon, title } = SHORTCUTS_META[type];
if (typeof title === 'function') {
- title = title(shortcut);
+ title = title(shortcut, i);
}
if (typeof icon === 'function') {
- icon = icon(shortcut);
+ icon = icon(shortcut, i);
}
return (
-
@@ -235,7 +258,7 @@ function ShortcutsSettings() {
{title}
-
+
+
+ }
+ >
+ {StatusMenuItems}
+
>
)}
@@ -951,6 +1173,7 @@ function Poll({
choices.push(value);
}
});
+ if (!choices.length) return;
setUIState('loading');
await votePoll(choices);
setUIState('default');
@@ -1049,8 +1272,6 @@ function EditedAtModal({
})();
}, []);
- const currentYear = new Date().getFullYear();
-
return (
@@ -1074,21 +1295,7 @@ function EditedAtModal({
return (
-
-
+
{
+ if (status?.id) {
+ const statusURL = `/${domain}/s/${id}`;
+ const result = {
+ id,
+ url: statusURL,
+ };
+ console.debug('🦦 Unfurled URL', url, id, statusURL);
+ states.unfurledLinks[url] = result;
+ return result;
+ } else {
+ failedUnfurls[url] = true;
+ throw new Error('No results');
+ }
+ })
+ .catch((e) => {
+ failedUnfurls[url] = true;
+ });
+ }
+
const { masto } = api({ instance });
- return masto.v2
+ const mastoSearchFetch = masto.v2
.search({
q: url,
type: 'statuses',
@@ -1224,6 +1464,8 @@ function _unfurlMastodonLink(instance, url) {
// console.warn(e);
// Silently fail
});
+
+ return Promise.any([remoteInstanceFetch, mastoSearchFetch]);
}
const unfurlMastodonLink = throttle(_unfurlMastodonLink);
diff --git a/src/components/timeline.jsx b/src/components/timeline.jsx
index 53b0a60d..491b72be 100644
--- a/src/components/timeline.jsx
+++ b/src/components/timeline.jsx
@@ -168,7 +168,7 @@ function Timeline({
reachStart,
reachEnd,
} = useScroll({
- scrollableElement: scrollableRef.current,
+ scrollableRef,
distanceFromEnd: 2,
scrollThresholdStart: 44,
});
@@ -262,7 +262,7 @@ function Timeline({
{headerStart !== null && headerStart !== undefined ? (
headerStart
) : (
-
+
)}
@@ -477,7 +477,7 @@ function groupBoosts(values) {
function StatusCarousel({ title, class: className, children }) {
const carouselRef = useRef();
const { reachStart, reachEnd, init } = useScroll({
- scrollableElement: carouselRef.current,
+ scrollableRef: carouselRef,
direction: 'horizontal',
});
useEffect(() => {
diff --git a/src/index.css b/src/index.css
index 479d565e..796ec971 100644
--- a/src/index.css
+++ b/src/index.css
@@ -84,6 +84,10 @@
box-sizing: border-box;
}
+html {
+ text-size-adjust: 100%;
+}
+
body {
font-family: ui-rounded, system-ui;
font-size: 16px;
@@ -297,6 +301,9 @@ code {
.insignificant {
color: var(--text-insignificant-color);
}
+.more-insignificant {
+ opacity: 0.5;
+}
.hide-until-focus-visible {
display: none;
diff --git a/src/pages/account-statuses.jsx b/src/pages/account-statuses.jsx
index 346d4fe2..5561849f 100644
--- a/src/pages/account-statuses.jsx
+++ b/src/pages/account-statuses.jsx
@@ -97,7 +97,7 @@ function AccountStatuses() {
}
- id="account_statuses"
+ id="account-statuses"
instance={instance}
emptyText="Nothing to see here yet."
errorText="Unable to load statuses"
diff --git a/src/pages/followed-hashtags.jsx b/src/pages/followed-hashtags.jsx
index 4c627583..21e0f290 100644
--- a/src/pages/followed-hashtags.jsx
+++ b/src/pages/followed-hashtags.jsx
@@ -39,7 +39,7 @@ function FollowedHashtags() {
}, []);
return (
-