Compare commits
61 commits
6229acea20
...
c6824b9b0d
Author | SHA1 | Date | |
---|---|---|---|
|
c6824b9b0d | ||
|
36f38230c4 | ||
|
a66a4e238e | ||
|
aa7fb4441f | ||
|
f1dbb9ec42 | ||
|
a59668ea9a | ||
|
6581bc2881 | ||
|
28bb66f185 | ||
|
46d7cba1ea | ||
|
ff35c458c3 | ||
|
26d445af7d | ||
|
3470b9adec | ||
|
f3d77dd04e | ||
|
14f5c37721 | ||
|
94c59c47d1 | ||
|
a66307b757 | ||
|
9792700f30 | ||
|
36e852bebb | ||
|
6075542071 | ||
|
0386357688 | ||
|
3f0b933654 | ||
|
9cac63c37d | ||
|
5cfcfdc98b | ||
|
a2d995ec07 | ||
|
4ca9a802e3 | ||
|
990f2b2e29 | ||
|
725da37063 | ||
|
1b41d39032 | ||
|
23dd7f5a7a | ||
|
7d95c50c7a | ||
|
a352f94c2c | ||
|
38e2b176bc | ||
|
6b4c1c8505 | ||
|
46dfd9aab0 | ||
|
59d0138ca8 | ||
|
3fbd5b8622 | ||
|
b6c4045cb4 | ||
|
37c784dad2 | ||
|
04d431cf71 | ||
|
97458b66eb | ||
|
fadfc6052d | ||
|
0ca92e7509 | ||
|
b8484eff79 | ||
|
1017d1d270 | ||
|
04179340f6 | ||
|
9b0889fe23 | ||
|
79e87b7d89 | ||
|
0ebc0fa64c | ||
|
35974cc89c | ||
|
00675c827f | ||
|
2b3f65f28c | ||
|
2912d2e2e6 | ||
|
500f877d4b | ||
|
4b9ff0ca5b | ||
|
07f927d4ff | ||
|
8c6563a671 | ||
|
ffabd6188d | ||
|
d71b1a7e36 | ||
|
c47687e2e4 | ||
|
5b0d6dd58b | ||
|
ecd5c7b91e |
211
package-lock.json
generated
211
package-lock.json
generated
|
@ -8,7 +8,7 @@
|
||||||
"name": "phanpy",
|
"name": "phanpy",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/intl-localematcher": "~0.5.2",
|
"@formatjs/intl-localematcher": "~0.5.4",
|
||||||
"@formkit/auto-animate": "~0.8.1",
|
"@formkit/auto-animate": "~0.8.1",
|
||||||
"@github/text-expander-element": "~2.6.1",
|
"@github/text-expander-element": "~2.6.1",
|
||||||
"@iconify-icons/mingcute": "~1.2.9",
|
"@iconify-icons/mingcute": "~1.2.9",
|
||||||
|
@ -23,12 +23,12 @@
|
||||||
"idb-keyval": "~6.2.1",
|
"idb-keyval": "~6.2.1",
|
||||||
"just-debounce-it": "~3.2.0",
|
"just-debounce-it": "~3.2.0",
|
||||||
"lz-string": "~1.5.0",
|
"lz-string": "~1.5.0",
|
||||||
"masto": "~6.5.1",
|
"masto": "~6.5.2",
|
||||||
"moize": "~6.1.6",
|
"moize": "~6.1.6",
|
||||||
"p-retry": "~6.2.0",
|
"p-retry": "~6.2.0",
|
||||||
"p-throttle": "~6.1.0",
|
"p-throttle": "~6.1.0",
|
||||||
"preact": "~10.19.3",
|
"preact": "~10.19.3",
|
||||||
"react-hotkeys-hook": "~4.4.1",
|
"react-hotkeys-hook": "~4.4.4",
|
||||||
"react-intersection-observer": "~9.5.3",
|
"react-intersection-observer": "~9.5.3",
|
||||||
"react-quick-pinch-zoom": "~5.1.0",
|
"react-quick-pinch-zoom": "~5.1.0",
|
||||||
"react-router-dom": "6.6.2",
|
"react-router-dom": "6.6.2",
|
||||||
|
@ -43,16 +43,16 @@
|
||||||
"valtio": "1.9.0"
|
"valtio": "1.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "~2.7.0",
|
"@preact/preset-vite": "~2.8.1",
|
||||||
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
|
||||||
"postcss": "~8.4.32",
|
"postcss": "~8.4.33",
|
||||||
"postcss-dark-theme-class": "~1.1.0",
|
"postcss-dark-theme-class": "~1.1.0",
|
||||||
"postcss-preset-env": "~9.3.0",
|
"postcss-preset-env": "~9.3.0",
|
||||||
"twitter-text": "~3.1.0",
|
"twitter-text": "~3.1.0",
|
||||||
"vite": "~5.0.10",
|
"vite": "~5.0.12",
|
||||||
"vite-plugin-generate-file": "~0.1.1",
|
"vite-plugin-generate-file": "~0.1.1",
|
||||||
"vite-plugin-html-config": "~1.0.11",
|
"vite-plugin-html-config": "~1.0.11",
|
||||||
"vite-plugin-pwa": "~0.17.4",
|
"vite-plugin-pwa": "~0.17.5",
|
||||||
"vite-plugin-remove-console": "~2.2.0",
|
"vite-plugin-remove-console": "~2.2.0",
|
||||||
"workbox-cacheable-response": "~7.0.0",
|
"workbox-cacheable-response": "~7.0.0",
|
||||||
"workbox-expiration": "~7.0.0",
|
"workbox-expiration": "~7.0.0",
|
||||||
|
@ -2899,10 +2899,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@formatjs/intl-localematcher": {
|
"node_modules/@formatjs/intl-localematcher": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
|
||||||
"integrity": "sha512-txaaE2fiBMagLrR4jYhxzFO6wEdEG4TPMqrzBAcbr4HFUYzH/YC+lg6OIzKCHm8WgDdyQevxbAAV1OgcXctuGw==",
|
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
|
@ -3081,11 +3080,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@preact/preset-vite": {
|
"node_modules/@preact/preset-vite": {
|
||||||
"version": "2.7.0",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.8.1.tgz",
|
||||||
"integrity": "sha512-m5N0FVtxbCCDxNk55NGhsRpKJChYcupcuQHzMJc/Bll07IKZKn8amwYciyKFS9haU6AgzDAJ/ewvApr6Qg1DHw==",
|
"integrity": "sha512-a9KV4opdj17X2gOFuGup0aE+sXYABX/tJi/QDptOrleX4FlnoZgDWvz45tHOdVfrZX+3uvVsIYPHxRsTerkDNA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-react-jsx": "^7.22.15",
|
"@babel/plugin-transform-react-jsx": "^7.22.15",
|
||||||
"@babel/plugin-transform-react-jsx-development": "^7.22.5",
|
"@babel/plugin-transform-react-jsx-development": "^7.22.5",
|
||||||
|
@ -3094,6 +3092,8 @@
|
||||||
"babel-plugin-transform-hook-names": "^1.0.2",
|
"babel-plugin-transform-hook-names": "^1.0.2",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"kolorist": "^1.8.0",
|
"kolorist": "^1.8.0",
|
||||||
|
"magic-string": "0.30.5",
|
||||||
|
"node-html-parser": "^6.1.10",
|
||||||
"resolve": "^1.22.8"
|
"resolve": "^1.22.8"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -3101,6 +3101,24 @@
|
||||||
"vite": "2.x || 3.x || 4.x || 5.x"
|
"vite": "2.x || 3.x || 4.x || 5.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@preact/preset-vite/node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.4.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||||
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@preact/preset-vite/node_modules/magic-string": {
|
||||||
|
"version": "0.30.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
|
||||||
|
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@prefresh/babel-plugin": {
|
"node_modules/@prefresh/babel-plugin": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prefresh/babel-plugin/-/babel-plugin-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prefresh/babel-plugin/-/babel-plugin-0.5.0.tgz",
|
||||||
|
@ -3752,6 +3770,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/boolbase": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
@ -4103,6 +4127,34 @@
|
||||||
"postcss": "^8.4"
|
"postcss": "^8.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-select": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"boolbase": "^1.0.0",
|
||||||
|
"css-what": "^6.1.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"domutils": "^3.0.1",
|
||||||
|
"nth-check": "^2.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/css-what": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cssdb": {
|
"node_modules/cssdb": {
|
||||||
"version": "7.9.0",
|
"version": "7.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.9.0.tgz",
|
||||||
|
@ -4193,6 +4245,61 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dom-serializer": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"entities": "^4.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/domelementtype": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/domhandler": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "^2.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/domutils": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dom-serializer": "^2.0.0",
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dot-case": {
|
"node_modules/dot-case": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
||||||
|
@ -4232,6 +4339,18 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-abstract": {
|
"node_modules/es-abstract": {
|
||||||
"version": "1.21.2",
|
"version": "1.21.2",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
|
||||||
|
@ -4811,6 +4930,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/he": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"he": "bin/he"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/header-case": {
|
"node_modules/header-case": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz",
|
||||||
|
@ -5479,9 +5607,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/masto": {
|
"node_modules/masto": {
|
||||||
"version": "6.5.1",
|
"version": "6.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/masto/-/masto-6.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/masto/-/masto-6.5.2.tgz",
|
||||||
"integrity": "sha512-jQTWSNmwtKPQ/H9gW6dIvX4cYIQZE5tKwFFwv6/hcuwqHuYaNHMMU51Qt9pqC1y9NZshivwsMijC9QKUKIiHhg==",
|
"integrity": "sha512-JfnG7MSQmhszWnLsvdBuxXc2tcVUyCvlTxnSH/5S+In4dU1tvc1wGhFR87kO+YW8gfDsDw9CHh+AD/z+DbTTfQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"change-case": "^4.1.2",
|
"change-case": "^4.1.2",
|
||||||
"events-to-async": "^2.0.1",
|
"events-to-async": "^2.0.1",
|
||||||
|
@ -5613,6 +5741,16 @@
|
||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-html-parser": {
|
||||||
|
"version": "6.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.12.tgz",
|
||||||
|
"integrity": "sha512-/bT/Ncmv+fbMGX96XG9g05vFt43m/+SYKIs9oAemQVYyVcZmDAI2Xq/SbNcpOA35eF0Zk2av3Ksf+Xk8Vt8abA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"css-select": "^5.1.0",
|
||||||
|
"he": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.13",
|
"version": "2.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||||
|
@ -5630,6 +5768,18 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nth-check": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"boolbase": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
@ -5783,9 +5933,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.32",
|
"version": "8.4.33",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
||||||
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
|
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -6646,10 +6796,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-hotkeys-hook": {
|
"node_modules/react-hotkeys-hook": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.4.4.tgz",
|
||||||
"integrity": "sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==",
|
"integrity": "sha512-wzZmqb/Obr0ds9Myc1sIFPJ52GA/Eeg/vXBWV0HA1LvHlVAW5Va3KB0q6EZNlNSHQWscWZ2K8+6w0GYSie2o7A==",
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=16.8.1",
|
"react": ">=16.8.1",
|
||||||
"react-dom": ">=16.8.1"
|
"react-dom": ">=16.8.1"
|
||||||
|
@ -7624,9 +7773,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.0.10",
|
"version": "5.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
|
||||||
"integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==",
|
"integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.19.3",
|
"esbuild": "^0.19.3",
|
||||||
|
@ -7705,9 +7854,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-plugin-pwa": {
|
"node_modules/vite-plugin-pwa": {
|
||||||
"version": "0.17.4",
|
"version": "0.17.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.17.4.tgz",
|
"resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.17.5.tgz",
|
||||||
"integrity": "sha512-j9iiyinFOYyof4Zk3Q+DtmYyDVBDAi6PuMGNGq6uGI0pw7E+LNm9e+nQ2ep9obMP/kjdWwzilqUrlfVRj9OobA==",
|
"integrity": "sha512-UxRNPiJBzh4tqU/vc8G2TxmrUTzT6BqvSzhszLk62uKsf+npXdvLxGDz9C675f4BJi6MbD2tPnJhi5txlMzxbQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
|
14
package.json
14
package.json
|
@ -10,7 +10,7 @@
|
||||||
"sourcemap": "npx source-map-explorer dist/assets/*.js"
|
"sourcemap": "npx source-map-explorer dist/assets/*.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/intl-localematcher": "~0.5.2",
|
"@formatjs/intl-localematcher": "~0.5.4",
|
||||||
"@formkit/auto-animate": "~0.8.1",
|
"@formkit/auto-animate": "~0.8.1",
|
||||||
"@github/text-expander-element": "~2.6.1",
|
"@github/text-expander-element": "~2.6.1",
|
||||||
"@iconify-icons/mingcute": "~1.2.9",
|
"@iconify-icons/mingcute": "~1.2.9",
|
||||||
|
@ -25,12 +25,12 @@
|
||||||
"idb-keyval": "~6.2.1",
|
"idb-keyval": "~6.2.1",
|
||||||
"just-debounce-it": "~3.2.0",
|
"just-debounce-it": "~3.2.0",
|
||||||
"lz-string": "~1.5.0",
|
"lz-string": "~1.5.0",
|
||||||
"masto": "~6.5.1",
|
"masto": "~6.5.2",
|
||||||
"moize": "~6.1.6",
|
"moize": "~6.1.6",
|
||||||
"p-retry": "~6.2.0",
|
"p-retry": "~6.2.0",
|
||||||
"p-throttle": "~6.1.0",
|
"p-throttle": "~6.1.0",
|
||||||
"preact": "~10.19.3",
|
"preact": "~10.19.3",
|
||||||
"react-hotkeys-hook": "~4.4.1",
|
"react-hotkeys-hook": "~4.4.4",
|
||||||
"react-intersection-observer": "~9.5.3",
|
"react-intersection-observer": "~9.5.3",
|
||||||
"react-quick-pinch-zoom": "~5.1.0",
|
"react-quick-pinch-zoom": "~5.1.0",
|
||||||
"react-router-dom": "6.6.2",
|
"react-router-dom": "6.6.2",
|
||||||
|
@ -45,16 +45,16 @@
|
||||||
"valtio": "1.9.0"
|
"valtio": "1.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "~2.7.0",
|
"@preact/preset-vite": "~2.8.1",
|
||||||
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
|
||||||
"postcss": "~8.4.32",
|
"postcss": "~8.4.33",
|
||||||
"postcss-dark-theme-class": "~1.1.0",
|
"postcss-dark-theme-class": "~1.1.0",
|
||||||
"postcss-preset-env": "~9.3.0",
|
"postcss-preset-env": "~9.3.0",
|
||||||
"twitter-text": "~3.1.0",
|
"twitter-text": "~3.1.0",
|
||||||
"vite": "~5.0.10",
|
"vite": "~5.0.12",
|
||||||
"vite-plugin-generate-file": "~0.1.1",
|
"vite-plugin-generate-file": "~0.1.1",
|
||||||
"vite-plugin-html-config": "~1.0.11",
|
"vite-plugin-html-config": "~1.0.11",
|
||||||
"vite-plugin-pwa": "~0.17.4",
|
"vite-plugin-pwa": "~0.17.5",
|
||||||
"vite-plugin-remove-console": "~2.2.0",
|
"vite-plugin-remove-console": "~2.2.0",
|
||||||
"workbox-cacheable-response": "~7.0.0",
|
"workbox-cacheable-response": "~7.0.0",
|
||||||
"workbox-expiration": "~7.0.0",
|
"workbox-expiration": "~7.0.0",
|
||||||
|
|
24
src/app.css
24
src/app.css
|
@ -645,6 +645,16 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
||||||
var(--bg-faded-color)
|
var(--bg-faded-color)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@keyframes summary-fade {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
.timeline.contextual > li .replies[open] > .replies-summary {
|
.timeline.contextual > li .replies[open] > .replies-summary {
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
|
|
||||||
|
@ -659,6 +669,10 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
||||||
.replies-summary-chevron {
|
.replies-summary-chevron {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ * {
|
||||||
|
animation: summary-fade 0.3s ease-out both;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.timeline.contextual > li .replies .replies-summary[hidden] {
|
.timeline.contextual > li .replies .replies-summary[hidden] {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -933,7 +947,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom right,
|
to bottom right,
|
||||||
var(--carousel-faded-color),
|
var(--carousel-faded-color),
|
||||||
transparent 150%
|
transparent
|
||||||
);
|
);
|
||||||
position: relative;
|
position: relative;
|
||||||
container-type: inline-size;
|
container-type: inline-size;
|
||||||
|
@ -948,7 +962,7 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
||||||
var(--carousel-faded-color),
|
var(--carousel-faded-color),
|
||||||
transparent
|
transparent
|
||||||
),
|
),
|
||||||
linear-gradient(to top, var(--bg-color), transparent 64px);
|
linear-gradient(to top, var(--bg-color) 8px, transparent 64px);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: bottom center;
|
background-position: bottom center;
|
||||||
}
|
}
|
||||||
|
@ -1059,6 +1073,10 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
||||||
.ui-state {
|
.ui-state {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-carousel-link {
|
.status-carousel-link {
|
||||||
|
@ -1659,6 +1677,8 @@ body:has(.media-modal-container + .status-deck) .media-post-link {
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
contain: none;
|
contain: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { subscribe } from 'valtio';
|
||||||
|
|
||||||
import BackgroundService from './components/background-service';
|
import BackgroundService from './components/background-service';
|
||||||
import ComposeButton from './components/compose-button';
|
import ComposeButton from './components/compose-button';
|
||||||
import { ICONS } from './components/icon';
|
import { ICONS } from './components/ICONS';
|
||||||
import KeyboardShortcutsHelp from './components/keyboard-shortcuts-help';
|
import KeyboardShortcutsHelp from './components/keyboard-shortcuts-help';
|
||||||
import Loader from './components/loader';
|
import Loader from './components/loader';
|
||||||
import Modals from './components/modals';
|
import Modals from './components/modals';
|
||||||
|
|
103
src/components/ICONS.jsx
Normal file
103
src/components/ICONS.jsx
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
export const ICONS = {
|
||||||
|
x: () => import('@iconify-icons/mingcute/close-line'),
|
||||||
|
heart: () => import('@iconify-icons/mingcute/heart-line'),
|
||||||
|
bookmark: () => import('@iconify-icons/mingcute/bookmark-line'),
|
||||||
|
'check-circle': () => import('@iconify-icons/mingcute/check-circle-line'),
|
||||||
|
'x-circle': () => import('@iconify-icons/mingcute/close-circle-line'),
|
||||||
|
transfer: () => import('@iconify-icons/mingcute/transfer-4-line'),
|
||||||
|
rocket: () => import('@iconify-icons/mingcute/rocket-line'),
|
||||||
|
'arrow-left': () => import('@iconify-icons/mingcute/arrow-left-line'),
|
||||||
|
'arrow-right': () => import('@iconify-icons/mingcute/arrow-right-line'),
|
||||||
|
'arrow-up': () => import('@iconify-icons/mingcute/arrow-up-line'),
|
||||||
|
'arrow-down': () => import('@iconify-icons/mingcute/arrow-down-line'),
|
||||||
|
earth: () => import('@iconify-icons/mingcute/earth-line'),
|
||||||
|
lock: () => import('@iconify-icons/mingcute/lock-line'),
|
||||||
|
unlock: () => import('@iconify-icons/mingcute/unlock-line'),
|
||||||
|
'eye-close': () => import('@iconify-icons/mingcute/eye-close-line'),
|
||||||
|
'eye-open': () => import('@iconify-icons/mingcute/eye-2-line'),
|
||||||
|
message: () => import('@iconify-icons/mingcute/mail-line'),
|
||||||
|
comment: () => import('@iconify-icons/mingcute/chat-3-line'),
|
||||||
|
comment2: () => import('@iconify-icons/mingcute/comment-2-line'),
|
||||||
|
home: () => import('@iconify-icons/mingcute/home-3-line'),
|
||||||
|
notification: () => import('@iconify-icons/mingcute/notification-line'),
|
||||||
|
follow: () => import('@iconify-icons/mingcute/user-follow-line'),
|
||||||
|
'follow-add': () => import('@iconify-icons/mingcute/user-add-line'),
|
||||||
|
poll: [() => import('@iconify-icons/mingcute/chart-bar-line'), '90deg'],
|
||||||
|
pencil: () => import('@iconify-icons/mingcute/pencil-line'),
|
||||||
|
quill: () => import('@iconify-icons/mingcute/quill-pen-line'),
|
||||||
|
at: () => import('@iconify-icons/mingcute/at-line'),
|
||||||
|
attachment: () => import('@iconify-icons/mingcute/attachment-line'),
|
||||||
|
upload: () => import('@iconify-icons/mingcute/upload-3-line'),
|
||||||
|
gear: () => import('@iconify-icons/mingcute/settings-3-line'),
|
||||||
|
more: () => import('@iconify-icons/mingcute/more-3-line'),
|
||||||
|
more2: () => import('@iconify-icons/mingcute/more-1-fill'),
|
||||||
|
external: () => import('@iconify-icons/mingcute/external-link-line'),
|
||||||
|
popout: () => import('@iconify-icons/mingcute/external-link-line'),
|
||||||
|
popin: [() => import('@iconify-icons/mingcute/external-link-line'), '180deg'],
|
||||||
|
plus: () => import('@iconify-icons/mingcute/add-circle-line'),
|
||||||
|
'chevron-left': () => import('@iconify-icons/mingcute/left-line'),
|
||||||
|
'chevron-right': () => import('@iconify-icons/mingcute/right-line'),
|
||||||
|
'chevron-down': () => import('@iconify-icons/mingcute/down-line'),
|
||||||
|
reply: [
|
||||||
|
() => import('@iconify-icons/mingcute/share-forward-line'),
|
||||||
|
'180deg',
|
||||||
|
'horizontal',
|
||||||
|
],
|
||||||
|
thread: () => import('@iconify-icons/mingcute/route-line'),
|
||||||
|
group: () => import('@iconify-icons/mingcute/group-line'),
|
||||||
|
bot: () => import('@iconify-icons/mingcute/android-2-line'),
|
||||||
|
menu: () => import('@iconify-icons/mingcute/rows-4-line'),
|
||||||
|
list: () => import('@iconify-icons/mingcute/list-check-line'),
|
||||||
|
search: () => import('@iconify-icons/mingcute/search-2-line'),
|
||||||
|
hashtag: () => import('@iconify-icons/mingcute/hashtag-line'),
|
||||||
|
info: () => import('@iconify-icons/mingcute/information-line'),
|
||||||
|
shortcut: () => import('@iconify-icons/mingcute/lightning-line'),
|
||||||
|
user: () => import('@iconify-icons/mingcute/user-4-line'),
|
||||||
|
following: () => import('@iconify-icons/mingcute/walk-line'),
|
||||||
|
pin: () => import('@iconify-icons/mingcute/pin-line'),
|
||||||
|
unpin: [() => import('@iconify-icons/mingcute/pin-line'), '180deg'],
|
||||||
|
bus: () => import('@iconify-icons/mingcute/bus-2-line'),
|
||||||
|
link: () => import('@iconify-icons/mingcute/link-2-line'),
|
||||||
|
history: () => import('@iconify-icons/mingcute/history-line'),
|
||||||
|
share: () => import('@iconify-icons/mingcute/share-2-line'),
|
||||||
|
sparkles: () => import('@iconify-icons/mingcute/sparkles-line'),
|
||||||
|
sparkles2: () => import('@iconify-icons/mingcute/sparkles-2-line'),
|
||||||
|
exit: () => import('@iconify-icons/mingcute/exit-line'),
|
||||||
|
translate: () => import('@iconify-icons/mingcute/translate-line'),
|
||||||
|
play: () => import('@iconify-icons/mingcute/play-fill'),
|
||||||
|
trash: () => import('@iconify-icons/mingcute/delete-2-line'),
|
||||||
|
mute: () => import('@iconify-icons/mingcute/volume-mute-line'),
|
||||||
|
unmute: () => import('@iconify-icons/mingcute/volume-line'),
|
||||||
|
block: () => import('@iconify-icons/mingcute/forbid-circle-line'),
|
||||||
|
unblock: [
|
||||||
|
() => import('@iconify-icons/mingcute/forbid-circle-line'),
|
||||||
|
'180deg',
|
||||||
|
],
|
||||||
|
flag: () => import('@iconify-icons/mingcute/flag-4-line'),
|
||||||
|
time: () => import('@iconify-icons/mingcute/time-line'),
|
||||||
|
refresh: () => import('@iconify-icons/mingcute/refresh-2-line'),
|
||||||
|
emoji2: () => import('@iconify-icons/mingcute/emoji-2-line'),
|
||||||
|
filter: () => import('@iconify-icons/mingcute/filter-2-line'),
|
||||||
|
chart: () => import('@iconify-icons/mingcute/chart-line-line'),
|
||||||
|
react: () => import('@iconify-icons/mingcute/react-line'),
|
||||||
|
layout4: () => import('@iconify-icons/mingcute/layout-4-line'),
|
||||||
|
layout5: () => import('@iconify-icons/mingcute/layout-5-line'),
|
||||||
|
announce: () => import('@iconify-icons/mingcute/announcement-line'),
|
||||||
|
alert: () => import('@iconify-icons/mingcute/alert-line'),
|
||||||
|
round: () => import('@iconify-icons/mingcute/round-fill'),
|
||||||
|
'arrow-up-circle': () =>
|
||||||
|
import('@iconify-icons/mingcute/arrow-up-circle-line'),
|
||||||
|
'arrow-down-circle': () =>
|
||||||
|
import('@iconify-icons/mingcute/arrow-down-circle-line'),
|
||||||
|
clipboard: () => import('@iconify-icons/mingcute/clipboard-line'),
|
||||||
|
'account-edit': () => import('@iconify-icons/mingcute/user-edit-line'),
|
||||||
|
'account-warning': () => import('@iconify-icons/mingcute/user-warning-line'),
|
||||||
|
keyboard: () => import('@iconify-icons/mingcute/keyboard-line'),
|
||||||
|
cloud: () => import('@iconify-icons/mingcute/cloud-line'),
|
||||||
|
month: () => import('@iconify-icons/mingcute/calendar-month-line'),
|
||||||
|
media: () => import('@iconify-icons/mingcute/photo-album-line'),
|
||||||
|
speak: () => import('@iconify-icons/mingcute/radar-line'),
|
||||||
|
building: () => import('@iconify-icons/mingcute/building-5-line'),
|
||||||
|
history: () => import('@iconify-icons/mingcute/history-2-line'),
|
||||||
|
document: () => import('@iconify-icons/mingcute/document-line'),
|
||||||
|
};
|
|
@ -61,6 +61,7 @@ function AccountBlock({
|
||||||
note,
|
note,
|
||||||
group,
|
group,
|
||||||
followersCount,
|
followersCount,
|
||||||
|
createdAt,
|
||||||
} = account;
|
} = account;
|
||||||
let [_, acct1, acct2] = acct.match(/([^@]+)(@.+)/i) || [, acct];
|
let [_, acct1, acct2] = acct.match(/([^@]+)(@.+)/i) || [, acct];
|
||||||
if (accountInstance) {
|
if (accountInstance) {
|
||||||
|
@ -188,6 +189,21 @@ function AccountBlock({
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{!bot &&
|
||||||
|
!group &&
|
||||||
|
!hasRelationship &&
|
||||||
|
!followersCount &&
|
||||||
|
!verifiedField &&
|
||||||
|
!!createdAt && (
|
||||||
|
<span class="created-at">
|
||||||
|
Joined{' '}
|
||||||
|
<time datetime={createdAt}>
|
||||||
|
{niceDateTime(createdAt, {
|
||||||
|
hideTime: true,
|
||||||
|
})}
|
||||||
|
</time>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -59,7 +59,11 @@ function AccountSheet({ account, instance: propInstance, onClose }) {
|
||||||
return result.accounts[0];
|
return result.accounts[0];
|
||||||
} else if (/https?:\/\/[^/]+\/@/.test(account)) {
|
} else if (/https?:\/\/[^/]+\/@/.test(account)) {
|
||||||
const accountURL = new URL(account);
|
const accountURL = new URL(account);
|
||||||
const acct = accountURL.pathname.replace(/^\//, '');
|
const { hostname, pathname } = accountURL;
|
||||||
|
const acct =
|
||||||
|
pathname.replace(/^\//, '').replace(/\/$/, '') +
|
||||||
|
'@' +
|
||||||
|
hostname;
|
||||||
const result = await masto.v2.search.fetch({
|
const result = await masto.v2.search.fetch({
|
||||||
q: acct,
|
q: acct,
|
||||||
type: 'accounts',
|
type: 'accounts',
|
||||||
|
|
|
@ -133,7 +133,14 @@ const SCAN_RE = new RegExp(
|
||||||
|
|
||||||
function highlightText(text, { maxCharacters = Infinity }) {
|
function highlightText(text, { maxCharacters = Infinity }) {
|
||||||
// Accept text string, return formatted HTML string
|
// Accept text string, return formatted HTML string
|
||||||
let html = text;
|
// Escape all HTML special characters
|
||||||
|
let html = text
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
|
||||||
// Exceeded characters limit
|
// Exceeded characters limit
|
||||||
const { composerCharacterCount } = states;
|
const { composerCharacterCount } = states;
|
||||||
let leftoverHTML = '';
|
let leftoverHTML = '';
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
iframe {
|
iframe {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
width: max(var(--width), 480px);
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: var(--aspect-ratio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import './embed-modal.css';
|
||||||
|
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
|
|
||||||
function EmbedModal({ html, url, onClose = () => {} }) {
|
function EmbedModal({ html, url, width, height, onClose = () => {} }) {
|
||||||
return (
|
return (
|
||||||
<div class="embed-modal-container">
|
<div class="embed-modal-container">
|
||||||
<div class="top-controls">
|
<div class="top-controls">
|
||||||
|
@ -20,7 +20,15 @@ function EmbedModal({ html, url, onClose = () => {} }) {
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="embed-content" dangerouslySetInnerHTML={{ __html: html }} />
|
<div
|
||||||
|
class="embed-content"
|
||||||
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
|
style={{
|
||||||
|
'--width': width + 'px',
|
||||||
|
'--height': height + 'px',
|
||||||
|
'--aspect-ratio': `${width}/${height}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { memo } from 'preact/compat';
|
||||||
|
|
||||||
function EmojiText({ text, emojis }) {
|
function EmojiText({ text, emojis }) {
|
||||||
if (!text) return '';
|
if (!text) return '';
|
||||||
if (!emojis?.length) return text;
|
if (!emojis?.length) return text;
|
||||||
|
@ -31,4 +33,9 @@ function EmojiText({ text, emojis }) {
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmojiText;
|
export default memo(
|
||||||
|
EmojiText,
|
||||||
|
(oldProps, newProps) =>
|
||||||
|
oldProps.text === newProps.text &&
|
||||||
|
oldProps.emojis?.length === newProps.emojis?.length,
|
||||||
|
);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import moize from 'moize';
|
import moize from 'moize';
|
||||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
import { ICONS } from './ICONS';
|
||||||
|
|
||||||
const SIZES = {
|
const SIZES = {
|
||||||
s: 12,
|
s: 12,
|
||||||
m: 16,
|
m: 16,
|
||||||
|
@ -9,115 +11,13 @@ const SIZES = {
|
||||||
xxl: 32,
|
xxl: 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ICONS = {
|
|
||||||
x: () => import('@iconify-icons/mingcute/close-line'),
|
|
||||||
heart: () => import('@iconify-icons/mingcute/heart-line'),
|
|
||||||
bookmark: () => import('@iconify-icons/mingcute/bookmark-line'),
|
|
||||||
'check-circle': () => import('@iconify-icons/mingcute/check-circle-line'),
|
|
||||||
'x-circle': () => import('@iconify-icons/mingcute/close-circle-line'),
|
|
||||||
transfer: () => import('@iconify-icons/mingcute/transfer-4-line'),
|
|
||||||
rocket: () => import('@iconify-icons/mingcute/rocket-line'),
|
|
||||||
'arrow-left': () => import('@iconify-icons/mingcute/arrow-left-line'),
|
|
||||||
'arrow-right': () => import('@iconify-icons/mingcute/arrow-right-line'),
|
|
||||||
'arrow-up': () => import('@iconify-icons/mingcute/arrow-up-line'),
|
|
||||||
'arrow-down': () => import('@iconify-icons/mingcute/arrow-down-line'),
|
|
||||||
earth: () => import('@iconify-icons/mingcute/earth-line'),
|
|
||||||
lock: () => import('@iconify-icons/mingcute/lock-line'),
|
|
||||||
unlock: () => import('@iconify-icons/mingcute/unlock-line'),
|
|
||||||
'eye-close': () => import('@iconify-icons/mingcute/eye-close-line'),
|
|
||||||
'eye-open': () => import('@iconify-icons/mingcute/eye-2-line'),
|
|
||||||
message: () => import('@iconify-icons/mingcute/mail-line'),
|
|
||||||
comment: () => import('@iconify-icons/mingcute/chat-3-line'),
|
|
||||||
comment2: () => import('@iconify-icons/mingcute/comment-2-line'),
|
|
||||||
home: () => import('@iconify-icons/mingcute/home-3-line'),
|
|
||||||
notification: () => import('@iconify-icons/mingcute/notification-line'),
|
|
||||||
follow: () => import('@iconify-icons/mingcute/user-follow-line'),
|
|
||||||
'follow-add': () => import('@iconify-icons/mingcute/user-add-line'),
|
|
||||||
poll: [() => import('@iconify-icons/mingcute/chart-bar-line'), '90deg'],
|
|
||||||
pencil: () => import('@iconify-icons/mingcute/pencil-line'),
|
|
||||||
quill: () => import('@iconify-icons/mingcute/quill-pen-line'),
|
|
||||||
at: () => import('@iconify-icons/mingcute/at-line'),
|
|
||||||
attachment: () => import('@iconify-icons/mingcute/attachment-line'),
|
|
||||||
upload: () => import('@iconify-icons/mingcute/upload-3-line'),
|
|
||||||
gear: () => import('@iconify-icons/mingcute/settings-3-line'),
|
|
||||||
more: () => import('@iconify-icons/mingcute/more-3-line'),
|
|
||||||
external: () => import('@iconify-icons/mingcute/external-link-line'),
|
|
||||||
popout: () => import('@iconify-icons/mingcute/external-link-line'),
|
|
||||||
popin: [() => import('@iconify-icons/mingcute/external-link-line'), '180deg'],
|
|
||||||
plus: () => import('@iconify-icons/mingcute/add-circle-line'),
|
|
||||||
'chevron-left': () => import('@iconify-icons/mingcute/left-line'),
|
|
||||||
'chevron-right': () => import('@iconify-icons/mingcute/right-line'),
|
|
||||||
'chevron-down': () => import('@iconify-icons/mingcute/down-line'),
|
|
||||||
reply: [
|
|
||||||
() => import('@iconify-icons/mingcute/share-forward-line'),
|
|
||||||
'180deg',
|
|
||||||
'horizontal',
|
|
||||||
],
|
|
||||||
thread: () => import('@iconify-icons/mingcute/route-line'),
|
|
||||||
group: () => import('@iconify-icons/mingcute/group-line'),
|
|
||||||
bot: () => import('@iconify-icons/mingcute/android-2-line'),
|
|
||||||
menu: () => import('@iconify-icons/mingcute/rows-4-line'),
|
|
||||||
list: () => import('@iconify-icons/mingcute/list-check-line'),
|
|
||||||
search: () => import('@iconify-icons/mingcute/search-2-line'),
|
|
||||||
hashtag: () => import('@iconify-icons/mingcute/hashtag-line'),
|
|
||||||
info: () => import('@iconify-icons/mingcute/information-line'),
|
|
||||||
shortcut: () => import('@iconify-icons/mingcute/lightning-line'),
|
|
||||||
user: () => import('@iconify-icons/mingcute/user-4-line'),
|
|
||||||
following: () => import('@iconify-icons/mingcute/walk-line'),
|
|
||||||
pin: () => import('@iconify-icons/mingcute/pin-line'),
|
|
||||||
bus: () => import('@iconify-icons/mingcute/bus-2-line'),
|
|
||||||
link: () => import('@iconify-icons/mingcute/link-2-line'),
|
|
||||||
history: () => import('@iconify-icons/mingcute/history-line'),
|
|
||||||
share: () => import('@iconify-icons/mingcute/share-2-line'),
|
|
||||||
sparkles: () => import('@iconify-icons/mingcute/sparkles-line'),
|
|
||||||
sparkles2: () => import('@iconify-icons/mingcute/sparkles-2-line'),
|
|
||||||
exit: () => import('@iconify-icons/mingcute/exit-line'),
|
|
||||||
translate: () => import('@iconify-icons/mingcute/translate-line'),
|
|
||||||
play: () => import('@iconify-icons/mingcute/play-fill'),
|
|
||||||
trash: () => import('@iconify-icons/mingcute/delete-2-line'),
|
|
||||||
mute: () => import('@iconify-icons/mingcute/volume-mute-line'),
|
|
||||||
unmute: () => import('@iconify-icons/mingcute/volume-line'),
|
|
||||||
block: () => import('@iconify-icons/mingcute/forbid-circle-line'),
|
|
||||||
unblock: [
|
|
||||||
() => import('@iconify-icons/mingcute/forbid-circle-line'),
|
|
||||||
'180deg',
|
|
||||||
],
|
|
||||||
flag: () => import('@iconify-icons/mingcute/flag-4-line'),
|
|
||||||
time: () => import('@iconify-icons/mingcute/time-line'),
|
|
||||||
refresh: () => import('@iconify-icons/mingcute/refresh-2-line'),
|
|
||||||
emoji2: () => import('@iconify-icons/mingcute/emoji-2-line'),
|
|
||||||
filter: () => import('@iconify-icons/mingcute/filter-2-line'),
|
|
||||||
chart: () => import('@iconify-icons/mingcute/chart-line-line'),
|
|
||||||
react: () => import('@iconify-icons/mingcute/react-line'),
|
|
||||||
layout4: () => import('@iconify-icons/mingcute/layout-4-line'),
|
|
||||||
layout5: () => import('@iconify-icons/mingcute/layout-5-line'),
|
|
||||||
announce: () => import('@iconify-icons/mingcute/announcement-line'),
|
|
||||||
alert: () => import('@iconify-icons/mingcute/alert-line'),
|
|
||||||
round: () => import('@iconify-icons/mingcute/round-fill'),
|
|
||||||
'arrow-up-circle': () =>
|
|
||||||
import('@iconify-icons/mingcute/arrow-up-circle-line'),
|
|
||||||
'arrow-down-circle': () =>
|
|
||||||
import('@iconify-icons/mingcute/arrow-down-circle-line'),
|
|
||||||
clipboard: () => import('@iconify-icons/mingcute/clipboard-line'),
|
|
||||||
'account-edit': () => import('@iconify-icons/mingcute/user-edit-line'),
|
|
||||||
'account-warning': () => import('@iconify-icons/mingcute/user-warning-line'),
|
|
||||||
keyboard: () => import('@iconify-icons/mingcute/keyboard-line'),
|
|
||||||
cloud: () => import('@iconify-icons/mingcute/cloud-line'),
|
|
||||||
month: () => import('@iconify-icons/mingcute/calendar-month-line'),
|
|
||||||
media: () => import('@iconify-icons/mingcute/photo-album-line'),
|
|
||||||
speak: () => import('@iconify-icons/mingcute/radar-line'),
|
|
||||||
building: () => import('@iconify-icons/mingcute/building-5-line'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const ICONDATA = {};
|
const ICONDATA = {};
|
||||||
|
|
||||||
// Memoize the dangerouslySetInnerHTML of the SVGs
|
// Memoize the dangerouslySetInnerHTML of the SVGs
|
||||||
const SVGICon = moize(
|
const SVGICon = moize(
|
||||||
function ({ size, width, height, body, rotate, flip }) {
|
function ({ width, height, body, rotate, flip }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={size}
|
|
||||||
height={size}
|
|
||||||
viewBox={`0 0 ${width} ${height}`}
|
viewBox={`0 0 ${width} ${height}`}
|
||||||
dangerouslySetInnerHTML={{ __html: body }}
|
dangerouslySetInnerHTML={{ __html: body }}
|
||||||
style={{
|
style={{
|
||||||
|
@ -131,6 +31,8 @@ const SVGICon = moize(
|
||||||
{
|
{
|
||||||
isShallowEqual: true,
|
isShallowEqual: true,
|
||||||
maxSize: Object.keys(ICONS).length,
|
maxSize: Object.keys(ICONS).length,
|
||||||
|
matchesArg: (cacheKeyArg, keyArg) =>
|
||||||
|
cacheKeyArg.icon === keyArg.icon && cacheKeyArg.body === keyArg.body,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -191,7 +93,7 @@ function Icon({
|
||||||
// }}
|
// }}
|
||||||
// />
|
// />
|
||||||
<SVGICon
|
<SVGICon
|
||||||
size={iconSize}
|
icon={icon}
|
||||||
width={iconData.width}
|
width={iconData.width}
|
||||||
height={iconData.height}
|
height={iconData.height}
|
||||||
body={iconData.body}
|
body={iconData.body}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default memo(function KeyboardShortcutsHelp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'?, shift+?',
|
'?, shift+?, shift+slash',
|
||||||
(e) => {
|
(e) => {
|
||||||
console.log('help');
|
console.log('help');
|
||||||
states.showKeyboardShortcutsHelp = true;
|
states.showKeyboardShortcutsHelp = true;
|
||||||
|
@ -71,6 +71,10 @@ export default memo(function KeyboardShortcutsHelp() {
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
action: 'Load new posts',
|
||||||
|
keys: <kbd>.</kbd>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
action: 'Open post details',
|
action: 'Open post details',
|
||||||
keys: (
|
keys: (
|
||||||
|
|
|
@ -273,7 +273,7 @@ function MediaModal({
|
||||||
<span>
|
<span>
|
||||||
<Menu2
|
<Menu2
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
align="end"
|
align="center"
|
||||||
position="anchor"
|
position="anchor"
|
||||||
gap={4}
|
gap={4}
|
||||||
menuClassName="glass-menu"
|
menuClassName="glass-menu"
|
||||||
|
|
|
@ -151,11 +151,18 @@ function Media({
|
||||||
[to],
|
[to],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const remoteMediaURLObj = remoteMediaURL ? new URL(remoteMediaURL) : null;
|
||||||
const isVideoMaybe =
|
const isVideoMaybe =
|
||||||
type === 'unknown' &&
|
type === 'unknown' &&
|
||||||
/\.(mp4|m4a|m4p|m4b|m4r|m4v|mov|webm)$/i.test(remoteMediaURL);
|
remoteMediaURLObj &&
|
||||||
|
/\.(mp4|m4r|m4v|mov|webm)$/i.test(remoteMediaURLObj.pathname);
|
||||||
|
const isAudioMaybe =
|
||||||
|
type === 'unknown' &&
|
||||||
|
remoteMediaURLObj &&
|
||||||
|
/\.(mp3|ogg|wav|m4a|m4p|m4b)$/i.test(remoteMediaURLObj.pathname);
|
||||||
const isImage =
|
const isImage =
|
||||||
type === 'image' || (type === 'unknown' && previewUrl && !isVideoMaybe);
|
type === 'image' ||
|
||||||
|
(type === 'unknown' && previewUrl && !isVideoMaybe && !isAudioMaybe);
|
||||||
|
|
||||||
const parentRef = useRef();
|
const parentRef = useRef();
|
||||||
const [imageSmallerThanParent, setImageSmallerThanParent] = useState(false);
|
const [imageSmallerThanParent, setImageSmallerThanParent] = useState(false);
|
||||||
|
@ -476,7 +483,7 @@ function Media({
|
||||||
</Parent>
|
</Parent>
|
||||||
</Figure>
|
</Figure>
|
||||||
);
|
);
|
||||||
} else if (type === 'audio') {
|
} else if (type === 'audio' || isAudioMaybe) {
|
||||||
const formattedDuration = formatDuration(original.duration);
|
const formattedDuration = formatDuration(original.duration);
|
||||||
return (
|
return (
|
||||||
<Figure>
|
<Figure>
|
||||||
|
@ -499,6 +506,12 @@ function Media({
|
||||||
height={height}
|
height={height}
|
||||||
data-orientation={orientation}
|
data-orientation={orientation}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
onError={(e) => {
|
||||||
|
try {
|
||||||
|
// Remove self if broken
|
||||||
|
e.target?.remove?.();
|
||||||
|
} catch (e) {}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{!showOriginal && (
|
{!showOriginal && (
|
||||||
|
|
|
@ -210,6 +210,8 @@ export default function Modals() {
|
||||||
<EmbedModal
|
<EmbedModal
|
||||||
html={snapStates.showEmbedModal.html}
|
html={snapStates.showEmbedModal.html}
|
||||||
url={snapStates.showEmbedModal.url}
|
url={snapStates.showEmbedModal.url}
|
||||||
|
width={snapStates.showEmbedModal.width}
|
||||||
|
height={snapStates.showEmbedModal.height}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
states.showEmbedModal = false;
|
states.showEmbedModal = false;
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -97,4 +97,9 @@ function NameText({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(NameText);
|
export default memo(NameText, (oldProps, newProps) => {
|
||||||
|
// Only care about account.id, the other props usually don't change
|
||||||
|
const { account } = oldProps;
|
||||||
|
const { account: newAccount } = newProps;
|
||||||
|
return account?.acct === newAccount?.acct;
|
||||||
|
});
|
||||||
|
|
|
@ -292,7 +292,12 @@ function Notification({
|
||||||
instance ? `/${instance}/s/${status.id}` : `/s/${status.id}`
|
instance ? `/${instance}/s/${status.id}` : `/s/${status.id}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Status status={status} size="s" />
|
<Status
|
||||||
|
status={status}
|
||||||
|
size="s"
|
||||||
|
previewMode
|
||||||
|
allowContextMenu
|
||||||
|
/>
|
||||||
</TruncatedLink>
|
</TruncatedLink>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -326,9 +331,19 @@ function Notification({
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{isStatic ? (
|
{isStatic ? (
|
||||||
<Status status={actualStatus} size="s" />
|
<Status
|
||||||
|
status={actualStatus}
|
||||||
|
size="s"
|
||||||
|
previewMode
|
||||||
|
allowContextMenu
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Status statusID={actualStatusID} size="s" />
|
<Status
|
||||||
|
statusID={actualStatusID}
|
||||||
|
size="s"
|
||||||
|
previewMode
|
||||||
|
allowContextMenu
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</TruncatedLink>
|
</TruncatedLink>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import dayjs from 'dayjs';
|
||||||
import dayjsTwitter from 'dayjs-twitter';
|
import dayjsTwitter from 'dayjs-twitter';
|
||||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
|
import { useMemo } from 'preact/hooks';
|
||||||
|
|
||||||
dayjs.extend(dayjsTwitter);
|
dayjs.extend(dayjsTwitter);
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
|
@ -17,23 +18,25 @@ const dtf = new Intl.DateTimeFormat();
|
||||||
|
|
||||||
export default function RelativeTime({ datetime, format }) {
|
export default function RelativeTime({ datetime, format }) {
|
||||||
if (!datetime) return null;
|
if (!datetime) return null;
|
||||||
const date = dayjs(datetime);
|
const date = useMemo(() => dayjs(datetime), [datetime]);
|
||||||
let dateStr;
|
const dateStr = useMemo(() => {
|
||||||
if (format === 'micro') {
|
if (format === 'micro') {
|
||||||
// If date <= 1 day ago or day is within this year
|
// If date <= 1 day ago or day is within this year
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
const dayDiff = now.diff(date, 'day');
|
const dayDiff = now.diff(date, 'day');
|
||||||
if (dayDiff <= 1 || now.year() === date.year()) {
|
if (dayDiff <= 1 || now.year() === date.year()) {
|
||||||
dateStr = date.twitter();
|
return date.twitter();
|
||||||
} else {
|
} else {
|
||||||
dateStr = dtf.format(date.toDate());
|
return dtf.format(date.toDate());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
return date.fromNow();
|
||||||
dateStr = date.fromNow();
|
}, [date, format]);
|
||||||
}
|
const dt = useMemo(() => date.toISOString(), [date]);
|
||||||
|
const title = useMemo(() => date.format('LLLL'), [date]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<time datetime={date.toISOString()} title={date.format('LLLL')}>
|
<time datetime={dt} title={title}>
|
||||||
{dateStr}
|
{dateStr}
|
||||||
</time>
|
</time>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default memo(function SearchCommand({ onClose = () => {} }) {
|
||||||
const searchFormRef = useRef(null);
|
const searchFormRef = useRef(null);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'/',
|
['Slash', '/'],
|
||||||
(e) => {
|
(e) => {
|
||||||
setShowSearch(true);
|
setShowSearch(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
@ -73,6 +73,7 @@ const SearchForm = forwardRef((props, ref) => {
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
autocapitalize="off"
|
autocapitalize="off"
|
||||||
|
spellcheck="false"
|
||||||
onSearch={(e) => {
|
onSearch={(e) => {
|
||||||
if (!e.target.value) {
|
if (!e.target.value) {
|
||||||
setSearchParams({});
|
setSearchParams({});
|
||||||
|
@ -84,6 +85,9 @@ const SearchForm = forwardRef((props, ref) => {
|
||||||
}}
|
}}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
setSearchMenuOpen(true);
|
setSearchMenuOpen(true);
|
||||||
|
formRef.current
|
||||||
|
?.querySelector('.search-popover-item')
|
||||||
|
?.classList.add('focus');
|
||||||
}}
|
}}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -178,8 +182,33 @@ const SearchForm = forwardRef((props, ref) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div class="search-popover" hidden={!searchMenuOpen || !query}>
|
<div class="search-popover" hidden={!searchMenuOpen || !query}>
|
||||||
|
{/* {!!query && (
|
||||||
|
<Link
|
||||||
|
to={`/search?q=${encodeURIComponent(query)}`}
|
||||||
|
class="search-popover-item focus"
|
||||||
|
onClick={(e) => {
|
||||||
|
props?.onSubmit?.(e);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="search" />
|
||||||
|
<span>{query}</span>
|
||||||
|
</Link>
|
||||||
|
)} */}
|
||||||
{!!query &&
|
{!!query &&
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
{query}{' '}
|
||||||
|
<small class="insignificant">
|
||||||
|
‒ accounts, hashtags & posts
|
||||||
|
</small>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
to: `/search?q=${encodeURIComponent(query)}`,
|
||||||
|
top: !type && !/\s/.test(query),
|
||||||
|
hidden: !!type,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: (
|
label: (
|
||||||
<>
|
<>
|
||||||
|
@ -188,6 +217,8 @@ const SearchForm = forwardRef((props, ref) => {
|
||||||
),
|
),
|
||||||
to: `/search?q=${encodeURIComponent(query)}&type=statuses`,
|
to: `/search?q=${encodeURIComponent(query)}&type=statuses`,
|
||||||
hidden: /^https?:/.test(query),
|
hidden: /^https?:/.test(query),
|
||||||
|
top: /\s/.test(query),
|
||||||
|
icon: 'document',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: (
|
label: (
|
||||||
|
@ -200,6 +231,7 @@ const SearchForm = forwardRef((props, ref) => {
|
||||||
/^@/.test(query) || /^https?:/.test(query) || /\s/.test(query),
|
/^@/.test(query) || /^https?:/.test(query) || /\s/.test(query),
|
||||||
top: /^#/.test(query),
|
top: /^#/.test(query),
|
||||||
type: 'link',
|
type: 'link',
|
||||||
|
icon: 'hashtag',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: (
|
label: (
|
||||||
|
@ -219,6 +251,7 @@ const SearchForm = forwardRef((props, ref) => {
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
to: `/search?q=${encodeURIComponent(query)}&type=accounts`,
|
to: `/search?q=${encodeURIComponent(query)}&type=accounts`,
|
||||||
|
icon: 'group',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
|
@ -226,17 +259,18 @@ const SearchForm = forwardRef((props, ref) => {
|
||||||
if (!a.top && b.top) return 1;
|
if (!a.top && b.top) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
})
|
})
|
||||||
.map(({ label, to, hidden, type }) => (
|
.filter(({ hidden }) => !hidden)
|
||||||
|
.map(({ label, to, icon, type }, i) => (
|
||||||
<Link
|
<Link
|
||||||
to={to}
|
to={to}
|
||||||
class="search-popover-item"
|
class={`search-popover-item ${i === 0 ? 'focus' : ''}`}
|
||||||
hidden={hidden}
|
// hidden={hidden}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
props?.onSubmit?.(e);
|
props?.onSubmit?.(e);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon={type === 'link' ? 'arrow-right' : 'search'}
|
icon={icon || (type === 'link' ? 'arrow-right' : 'search')}
|
||||||
class="more-insignificant"
|
class="more-insignificant"
|
||||||
/>
|
/>
|
||||||
<span>{label}</span>{' '}
|
<span>{label}</span>{' '}
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
#shortcuts-settings-container .shortcuts-view-mode {
|
#shortcuts-settings-container .shortcuts-view-mode {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
margin: 8px 0 0;
|
margin: 8px 0 0;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
#shortcuts-settings-container .shortcuts-view-mode label:first-child {
|
#shortcuts-settings-container .shortcuts-view-mode label:first-child {
|
||||||
border-top-left-radius: 16px;
|
border-top-left-radius: 16px;
|
||||||
|
|
|
@ -170,7 +170,7 @@ export const SHORTCUTS_META = {
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
id: 'search',
|
id: 'search',
|
||||||
title: ({ query }) => (query ? `"${query}"` : 'Search'),
|
title: ({ query }) => (query ? `“${query}”` : 'Search'),
|
||||||
path: ({ query }) =>
|
path: ({ query }) =>
|
||||||
query
|
query
|
||||||
? `/search?q=${encodeURIComponent(query)}&type=statuses`
|
? `/search?q=${encodeURIComponent(query)}&type=statuses`
|
||||||
|
@ -279,92 +279,93 @@ function ShortcutsSettings({ onClose }) {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{shortcuts.length > 0 ? (
|
{shortcuts.length > 0 ? (
|
||||||
<ol class="shortcuts-list" ref={shortcutsListParent}>
|
<>
|
||||||
{shortcuts.filter(Boolean).map((shortcut, i) => {
|
<ol class="shortcuts-list" ref={shortcutsListParent}>
|
||||||
// const key = i + Object.values(shortcut);
|
{shortcuts.filter(Boolean).map((shortcut, i) => {
|
||||||
const key = Object.values(shortcut).join('-');
|
// const key = i + Object.values(shortcut);
|
||||||
const { type } = shortcut;
|
const key = Object.values(shortcut).join('-');
|
||||||
if (!SHORTCUTS_META[type]) return null;
|
const { type } = shortcut;
|
||||||
let { icon, title, subtitle, excludeViewMode } =
|
if (!SHORTCUTS_META[type]) return null;
|
||||||
SHORTCUTS_META[type];
|
let { icon, title, subtitle, excludeViewMode } =
|
||||||
if (typeof title === 'function') {
|
SHORTCUTS_META[type];
|
||||||
title = title(shortcut, i);
|
if (typeof title === 'function') {
|
||||||
}
|
title = title(shortcut, i);
|
||||||
if (typeof subtitle === 'function') {
|
}
|
||||||
subtitle = subtitle(shortcut, i);
|
if (typeof subtitle === 'function') {
|
||||||
}
|
subtitle = subtitle(shortcut, i);
|
||||||
if (typeof icon === 'function') {
|
}
|
||||||
icon = icon(shortcut, i);
|
if (typeof icon === 'function') {
|
||||||
}
|
icon = icon(shortcut, i);
|
||||||
if (typeof excludeViewMode === 'function') {
|
}
|
||||||
excludeViewMode = excludeViewMode(shortcut, i);
|
if (typeof excludeViewMode === 'function') {
|
||||||
}
|
excludeViewMode = excludeViewMode(shortcut, i);
|
||||||
const excludedViewMode = excludeViewMode?.includes(
|
}
|
||||||
snapStates.settings.shortcutsViewMode,
|
const excludedViewMode = excludeViewMode?.includes(
|
||||||
);
|
snapStates.settings.shortcutsViewMode,
|
||||||
return (
|
);
|
||||||
<li key={key}>
|
return (
|
||||||
<Icon icon={icon} />
|
<li key={key}>
|
||||||
<span class="shortcut-text">
|
<Icon icon={icon} />
|
||||||
<AsyncText>{title}</AsyncText>
|
<span class="shortcut-text">
|
||||||
{subtitle && (
|
<AsyncText>{title}</AsyncText>
|
||||||
<>
|
{subtitle && (
|
||||||
{' '}
|
<>
|
||||||
<small class="ib insignificant">{subtitle}</small>
|
{' '}
|
||||||
</>
|
<small class="ib insignificant">{subtitle}</small>
|
||||||
)}
|
</>
|
||||||
{excludedViewMode && (
|
)}
|
||||||
<span class="tag">
|
{excludedViewMode && (
|
||||||
Not available in current view mode
|
<span class="tag">
|
||||||
</span>
|
Not available in current view mode
|
||||||
)}
|
</span>
|
||||||
</span>
|
)}
|
||||||
<span class="shortcut-actions">
|
</span>
|
||||||
<button
|
<span class="shortcut-actions">
|
||||||
type="button"
|
<button
|
||||||
class="plain small"
|
type="button"
|
||||||
disabled={i === 0}
|
class="plain small"
|
||||||
onClick={() => {
|
disabled={i === 0}
|
||||||
const shortcutsArr = Array.from(states.shortcuts);
|
onClick={() => {
|
||||||
if (i > 0) {
|
const shortcutsArr = Array.from(states.shortcuts);
|
||||||
const temp = states.shortcuts[i - 1];
|
if (i > 0) {
|
||||||
shortcutsArr[i - 1] = shortcut;
|
const temp = states.shortcuts[i - 1];
|
||||||
shortcutsArr[i] = temp;
|
shortcutsArr[i - 1] = shortcut;
|
||||||
states.shortcuts = shortcutsArr;
|
shortcutsArr[i] = temp;
|
||||||
}
|
states.shortcuts = shortcutsArr;
|
||||||
}}
|
}
|
||||||
>
|
}}
|
||||||
<Icon icon="arrow-up" alt="Move up" />
|
>
|
||||||
</button>
|
<Icon icon="arrow-up" alt="Move up" />
|
||||||
<button
|
</button>
|
||||||
type="button"
|
<button
|
||||||
class="plain small"
|
type="button"
|
||||||
disabled={i === shortcuts.length - 1}
|
class="plain small"
|
||||||
onClick={() => {
|
disabled={i === shortcuts.length - 1}
|
||||||
const shortcutsArr = Array.from(states.shortcuts);
|
onClick={() => {
|
||||||
if (i < states.shortcuts.length - 1) {
|
const shortcutsArr = Array.from(states.shortcuts);
|
||||||
const temp = states.shortcuts[i + 1];
|
if (i < states.shortcuts.length - 1) {
|
||||||
shortcutsArr[i + 1] = shortcut;
|
const temp = states.shortcuts[i + 1];
|
||||||
shortcutsArr[i] = temp;
|
shortcutsArr[i + 1] = shortcut;
|
||||||
states.shortcuts = shortcutsArr;
|
shortcutsArr[i] = temp;
|
||||||
}
|
states.shortcuts = shortcutsArr;
|
||||||
}}
|
}
|
||||||
>
|
}}
|
||||||
<Icon icon="arrow-down" alt="Move down" />
|
>
|
||||||
</button>
|
<Icon icon="arrow-down" alt="Move down" />
|
||||||
<button
|
</button>
|
||||||
type="button"
|
<button
|
||||||
class="plain small"
|
type="button"
|
||||||
onClick={() => {
|
class="plain small"
|
||||||
setShowForm({
|
onClick={() => {
|
||||||
shortcut,
|
setShowForm({
|
||||||
shortcutIndex: i,
|
shortcut,
|
||||||
});
|
shortcutIndex: i,
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
<Icon icon="pencil" alt="Edit" />
|
>
|
||||||
</button>
|
<Icon icon="pencil" alt="Edit" />
|
||||||
{/* <button
|
</button>
|
||||||
|
{/* <button
|
||||||
type="button"
|
type="button"
|
||||||
class="plain small"
|
class="plain small"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -373,11 +374,21 @@ function ShortcutsSettings({ onClose }) {
|
||||||
>
|
>
|
||||||
<Icon icon="x" alt="Remove" />
|
<Icon icon="x" alt="Remove" />
|
||||||
</button> */}
|
</button> */}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ol>
|
</ol>
|
||||||
|
{shortcuts.length === 1 &&
|
||||||
|
snapStates.settings.shortcutsViewMode !== 'float-button' && (
|
||||||
|
<div class="ui-state insignificant">
|
||||||
|
<Icon icon="info" />{' '}
|
||||||
|
<small>
|
||||||
|
Add more than one shortcut/column to make this work.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div class="ui-state insignificant">
|
<div class="ui-state insignificant">
|
||||||
<p>No shortcuts yet. Tap on the Add shortcut button.</p>
|
<p>No shortcuts yet. Tap on the Add shortcut button.</p>
|
||||||
|
@ -428,7 +439,12 @@ function ShortcutsSettings({ onClose }) {
|
||||||
disabled={shortcuts.length >= SHORTCUTS_LIMIT}
|
disabled={shortcuts.length >= SHORTCUTS_LIMIT}
|
||||||
onClick={() => setShowForm(true)}
|
onClick={() => setShowForm(true)}
|
||||||
>
|
>
|
||||||
<Icon icon="plus" /> <span>Add shortcut</span>
|
<Icon icon="plus" />{' '}
|
||||||
|
<span>
|
||||||
|
{snapStates.settings.shortcutsViewMode === 'multi-column'
|
||||||
|
? 'Add column…'
|
||||||
|
: 'Add shortcut…'}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -206,7 +206,7 @@
|
||||||
.status-card:not(.status-carousel .status)
|
.status-card:not(.status-carousel .status)
|
||||||
:is(.content, .poll, .media-container) {
|
:is(.content, .poll, .media-container) {
|
||||||
max-height: 160px !important;
|
max-height: 160px !important;
|
||||||
overflow: clip;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.status.small:not(.status-carousel .status, .status.large .status)
|
.status.small:not(.status-carousel .status, .status.large .status)
|
||||||
.status-card
|
.status-card
|
||||||
|
@ -290,7 +290,7 @@
|
||||||
transition: all 0.2s ease-out;
|
transition: all 0.2s ease-out;
|
||||||
}
|
}
|
||||||
.status.filtered:hover :is(.status-filtered-info-1, .status-filtered-info-2) {
|
.status.filtered:hover :is(.status-filtered-info-1, .status-filtered-info-2) {
|
||||||
transition-delay: 0.5s;
|
transition-delay: 1.5s;
|
||||||
}
|
}
|
||||||
.status.filtered .status-filtered-info-1 {
|
.status.filtered .status-filtered-info-1 {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
@ -330,6 +330,68 @@
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status.compact-reply {
|
||||||
|
--avatar-size: 20px;
|
||||||
|
--line-start: 40px;
|
||||||
|
--line-width: 3px;
|
||||||
|
--line-end: calc(var(--line-start) + var(--line-width));
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
--top-padding: 16px;
|
||||||
|
padding-top: var(--top-padding);
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: calc(-1 * var(--top-padding) / 2);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
160deg,
|
||||||
|
transparent 2.5%,
|
||||||
|
var(--reply-to-faded-color) 10%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% calc(100% - var(--top-padding) / 2);
|
||||||
|
|
||||||
|
> * {
|
||||||
|
opacity: 0.65;
|
||||||
|
transition: opacity 1s ease-out;
|
||||||
|
}
|
||||||
|
.status-link:hover & > * {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--top-padding) + var(--avatar-size));
|
||||||
|
left: var(--line-start);
|
||||||
|
width: var(--line-width);
|
||||||
|
height: calc(
|
||||||
|
100% - var(--top-padding) - var(--avatar-size) + (var(--top-padding) / 2)
|
||||||
|
);
|
||||||
|
background-color: var(--comment-line-color);
|
||||||
|
z-index: 0;
|
||||||
|
mask-image: linear-gradient(to bottom, #000 8px, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-left: calc((50px - var(--avatar-size)) / 2);
|
||||||
|
justify-self: center;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-compact {
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
font-size: 90%;
|
||||||
|
line-height: var(--avatar-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.status .container {
|
.status .container {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
@ -364,9 +426,8 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.status > .container > .meta :is(.time, .edited) {
|
.status > .container > .meta :is(.time, .edited) {
|
||||||
color: inherit;
|
color: var(--text-insignificant-color);
|
||||||
text-align: end;
|
text-align: end;
|
||||||
opacity: 0.5;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
@ -375,9 +436,21 @@
|
||||||
.status > .container > .meta a.time {
|
.status > .container > .meta a.time {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
font-size: 90%;
|
||||||
|
|
||||||
|
.more {
|
||||||
|
margin-left: 4px;
|
||||||
|
transition: transform 0.2s ease-out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.status > .container > .meta a.time:is(:hover, :focus) {
|
.status > .container > .meta a.time:is(:hover, :focus) {
|
||||||
text-decoration: underline;
|
.more {
|
||||||
|
transform: scale(1.2);
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.status > .container > .meta a.time:active,
|
.status > .container > .meta a.time:active,
|
||||||
.status > .container > .meta a.time.is-open {
|
.status > .container > .meta a.time.is-open {
|
||||||
|
@ -643,6 +716,10 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
.timeline-item-container & {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.compact-thread .spoiler-badge {
|
.status.compact-thread .spoiler-badge {
|
||||||
|
@ -1232,6 +1309,22 @@ body:has(#modal-container .carousel) .status .media img:hover {
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
/* Collapse possible hashtag stuffing */
|
||||||
|
/* If >= 9 hashtags, collapse */
|
||||||
|
/* TODO: lower the threshold one day */
|
||||||
|
.status:not(.large, .contextual .status)
|
||||||
|
p:not(.hashtag-stuffing):has(.hashtag:nth-of-type(1)):has(
|
||||||
|
.hashtag:nth-of-type(2)
|
||||||
|
):has(.hashtag:nth-of-type(3)):has(.hashtag:nth-of-type(4)):has(
|
||||||
|
.hashtag:nth-of-type(5)
|
||||||
|
):has(.hashtag:nth-of-type(6)):has(.hashtag:nth-of-type(7)):has(
|
||||||
|
.hashtag:nth-of-type(8)
|
||||||
|
):has(.hashtag:nth-of-type(9)) {
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
.media-figure-multiple {
|
.media-figure-multiple {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -1755,6 +1848,87 @@ a.card:is(:hover, :focus):visited {
|
||||||
color: var(--green-color);
|
color: var(--green-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ACTIONS */
|
||||||
|
|
||||||
|
.status-actions {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
z-index: 1;
|
||||||
|
border: 1px solid var(--outline-color);
|
||||||
|
box-shadow: 0 2px 6px -3px var(--drop-shadow-color);
|
||||||
|
overflow: clip;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateX(8px);
|
||||||
|
transform-origin: right center;
|
||||||
|
transition: all 0.15s ease-out 0.3s, border-color 0.3s ease-out;
|
||||||
|
|
||||||
|
button.plain {
|
||||||
|
color: var(--text-insignificant-color);
|
||||||
|
backdrop-filter: none;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
outline-offset: -5px;
|
||||||
|
outline: 1px solid transparent;
|
||||||
|
|
||||||
|
&:is(:hover, :focus) {
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: var(--bg-faded-color);
|
||||||
|
filter: none !important;
|
||||||
|
box-shadow: inset 0 0 0 2px var(--bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.reblog-button.checked {
|
||||||
|
color: var(--reblog-color);
|
||||||
|
outline-color: var(--reblog-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.favourite-button.checked {
|
||||||
|
color: var(--favourite-color);
|
||||||
|
outline-color: var(--favourite-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bookmark-button.checked {
|
||||||
|
color: var(--link-color);
|
||||||
|
outline-color: var(--link-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--outline-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
.status:hover &:not(:hover),
|
||||||
|
&.open {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
@media (pointer: coarse) {
|
||||||
|
.status:has(&):hover {
|
||||||
|
transition: background-color 0.1s ease-out 0.3s;
|
||||||
|
background-color: var(--bg-faded-blur-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
button.more-button {
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: var(--outline-color);
|
||||||
|
box-shadow: inset 0 0 0 2px var(--bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(.more-button) {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* BADGE */
|
/* BADGE */
|
||||||
|
|
||||||
.status-badge {
|
.status-badge {
|
||||||
|
@ -1853,6 +2027,12 @@ a.card:is(:hover, :focus):visited {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MENU OPEN */
|
||||||
|
|
||||||
|
.status-menu-open {
|
||||||
|
background-color: var(--link-bg-hover-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* FILTERED */
|
/* FILTERED */
|
||||||
|
|
||||||
#filtered-status-peek {
|
#filtered-status-peek {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -46,6 +46,7 @@ function Timeline({
|
||||||
view,
|
view,
|
||||||
filterContext,
|
filterContext,
|
||||||
showFollowedTags,
|
showFollowedTags,
|
||||||
|
showReplyParent,
|
||||||
}) {
|
}) {
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
const [items, setItems] = useState([]);
|
const [items, setItems] = useState([]);
|
||||||
|
@ -84,7 +85,7 @@ function Timeline({
|
||||||
if (boostsCarousel) {
|
if (boostsCarousel) {
|
||||||
value = groupBoosts(value);
|
value = groupBoosts(value);
|
||||||
}
|
}
|
||||||
value = groupContext(value);
|
value = groupContext(value, instance);
|
||||||
}
|
}
|
||||||
if (pinnedPosts.length) {
|
if (pinnedPosts.length) {
|
||||||
value = pinnedPosts.concat(value);
|
value = pinnedPosts.concat(value);
|
||||||
|
@ -204,6 +205,21 @@ function Timeline({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const showNewPostsIndicator =
|
||||||
|
items.length > 0 && uiState !== 'loading' && showNew;
|
||||||
|
const handleLoadNewPosts = useCallback(() => {
|
||||||
|
loadItems(true);
|
||||||
|
scrollableRef.current?.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}, [loadItems]);
|
||||||
|
const dotRef = useHotkeys('.', () => {
|
||||||
|
if (showNewPostsIndicator) {
|
||||||
|
handleLoadNewPosts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// const {
|
// const {
|
||||||
// scrollDirection,
|
// scrollDirection,
|
||||||
// nearReachStart,
|
// nearReachStart,
|
||||||
|
@ -387,24 +403,15 @@ function Timeline({
|
||||||
{!!headerEnd && headerEnd}
|
{!!headerEnd && headerEnd}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{items.length > 0 &&
|
{showNewPostsIndicator && (
|
||||||
uiState !== 'loading' &&
|
<button
|
||||||
// !hiddenUI &&
|
class="updates-button shiny-pill"
|
||||||
showNew && (
|
type="button"
|
||||||
<button
|
onClick={handleLoadNewPosts}
|
||||||
class="updates-button shiny-pill"
|
>
|
||||||
type="button"
|
<Icon icon="arrow-up" /> New posts
|
||||||
onClick={() => {
|
</button>
|
||||||
loadItems(true);
|
)}
|
||||||
scrollableRef.current?.scrollTo({
|
|
||||||
top: 0,
|
|
||||||
behavior: 'smooth',
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon icon="arrow-up" /> New posts
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</header>
|
</header>
|
||||||
{!!timelineStart && (
|
{!!timelineStart && (
|
||||||
<div
|
<div
|
||||||
|
@ -426,6 +433,7 @@ function Timeline({
|
||||||
key={status.id + status?._pinned + view}
|
key={status.id + status?._pinned + view}
|
||||||
view={view}
|
view={view}
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
|
showReplyParent={showReplyParent}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{showMore &&
|
{showMore &&
|
||||||
|
@ -516,6 +524,7 @@ function TimelineItem({
|
||||||
filterContext,
|
filterContext,
|
||||||
view,
|
view,
|
||||||
showFollowedTags,
|
showFollowedTags,
|
||||||
|
showReplyParent,
|
||||||
}) {
|
}) {
|
||||||
const { id: statusID, reblog, items, type, _pinned } = status;
|
const { id: statusID, reblog, items, type, _pinned } = status;
|
||||||
if (_pinned) useItemID = false;
|
if (_pinned) useItemID = false;
|
||||||
|
@ -674,6 +683,7 @@ function TimelineItem({
|
||||||
instance={instance}
|
instance={instance}
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
|
showReplyParent={showReplyParent}
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -682,6 +692,7 @@ function TimelineItem({
|
||||||
instance={instance}
|
instance={instance}
|
||||||
enableCommentHint
|
enableCommentHint
|
||||||
showFollowedTags={showFollowedTags}
|
showFollowedTags={showFollowedTags}
|
||||||
|
showReplyParent={showReplyParent}
|
||||||
// allowFilters={allowFilters}
|
// allowFilters={allowFilters}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -779,7 +790,7 @@ function StatusCarousel({ title, class: className, children }) {
|
||||||
|
|
||||||
function TimelineStatusCompact({ status, instance }) {
|
function TimelineStatusCompact({ status, instance }) {
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
const { id, visibility } = status;
|
const { id, visibility, language } = status;
|
||||||
const statusPeekText = statusPeek(status);
|
const statusPeekText = statusPeek(status);
|
||||||
const sKey = statusKey(id, instance);
|
const sKey = statusKey(id, instance);
|
||||||
return (
|
return (
|
||||||
|
@ -801,7 +812,12 @@ function TimelineStatusCompact({ status, instance }) {
|
||||||
<Icon icon="thread" size="s" />
|
<Icon icon="thread" size="s" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="content-compact" title={statusPeekText}>
|
<div
|
||||||
|
class="content-compact"
|
||||||
|
title={statusPeekText}
|
||||||
|
lang={language}
|
||||||
|
dir="auto"
|
||||||
|
>
|
||||||
{statusPeekText}
|
{statusPeekText}
|
||||||
{status.sensitive && status.spoilerText && (
|
{status.sensitive && status.spoilerText && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -959,11 +959,6 @@
|
||||||
"Kabyle",
|
"Kabyle",
|
||||||
"Taqbaylit"
|
"Taqbaylit"
|
||||||
],
|
],
|
||||||
[
|
|
||||||
"kmr",
|
|
||||||
"Kurmanji (Kurdish)",
|
|
||||||
"Kurmancî"
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
"ldn",
|
"ldn",
|
||||||
"Láadan",
|
"Láadan",
|
||||||
|
|
|
@ -45,19 +45,31 @@ function FollowedHashtags() {
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{followedHashtags.length > 0 ? (
|
{followedHashtags.length > 0 ? (
|
||||||
<ul class="link-list">
|
<>
|
||||||
{followedHashtags.map((tag) => (
|
<ul class="link-list">
|
||||||
<li>
|
{followedHashtags.map((tag) => (
|
||||||
<Link
|
<li>
|
||||||
to={
|
<Link
|
||||||
instance ? `/${instance}/t/${tag.name}` : `/t/${tag.name}`
|
to={
|
||||||
}
|
instance
|
||||||
>
|
? `/${instance}/t/${tag.name}`
|
||||||
<Icon icon="hashtag" /> <span>{tag.name}</span>
|
: `/t/${tag.name}`
|
||||||
</Link>
|
}
|
||||||
</li>
|
>
|
||||||
))}
|
<Icon icon="hashtag" /> <span>{tag.name}</span>
|
||||||
</ul>
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
{followedHashtags.length > 1 && (
|
||||||
|
<footer class="ui-state">
|
||||||
|
<small class="insignificant">
|
||||||
|
{followedHashtags.length} hashtag
|
||||||
|
{followedHashtags.length === 1 ? '' : 's'}
|
||||||
|
</small>
|
||||||
|
</footer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
) : uiState === 'loading' ? (
|
) : uiState === 'loading' ? (
|
||||||
<p class="ui-state">
|
<p class="ui-state">
|
||||||
<Loader abrupt />
|
<Loader abrupt />
|
||||||
|
|
|
@ -129,6 +129,7 @@ function Following({ title, path, id, ...props }) {
|
||||||
// allowFilters
|
// allowFilters
|
||||||
filterContext="home"
|
filterContext="home"
|
||||||
showFollowedTags
|
showFollowedTags
|
||||||
|
showReplyParent
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ function List(props) {
|
||||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||||
// allowFilters
|
// allowFilters
|
||||||
filterContext="home"
|
filterContext="home"
|
||||||
|
showReplyParent
|
||||||
// refresh={reloadCount}
|
// refresh={reloadCount}
|
||||||
headerStart={
|
headerStart={
|
||||||
<Link to="/l" class="button plain">
|
<Link to="/l" class="button plain">
|
||||||
|
|
|
@ -61,14 +61,15 @@ function Lists() {
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{lists.length > 0 ? (
|
{lists.length > 0 ? (
|
||||||
<ul class="link-list">
|
<>
|
||||||
{lists.map((list) => (
|
<ul class="link-list">
|
||||||
<li>
|
{lists.map((list) => (
|
||||||
<Link to={`/l/${list.id}`}>
|
<li>
|
||||||
<span>
|
<Link to={`/l/${list.id}`}>
|
||||||
<Icon icon="list" /> <span>{list.title}</span>
|
<span>
|
||||||
</span>
|
<Icon icon="list" /> <span>{list.title}</span>
|
||||||
{/* <button
|
</span>
|
||||||
|
{/* <button
|
||||||
type="button"
|
type="button"
|
||||||
class="plain"
|
class="plain"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
@ -81,10 +82,19 @@ function Lists() {
|
||||||
>
|
>
|
||||||
<Icon icon="pencil" />
|
<Icon icon="pencil" />
|
||||||
</button> */}
|
</button> */}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
{lists.length > 1 && (
|
||||||
|
<footer class="ui-state">
|
||||||
|
<small class="insignificant">
|
||||||
|
{lists.length} list
|
||||||
|
{lists.length === 1 ? '' : 's'}
|
||||||
|
</small>
|
||||||
|
</footer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
) : uiState === 'loading' ? (
|
) : uiState === 'loading' ? (
|
||||||
<p class="ui-state">
|
<p class="ui-state">
|
||||||
<Loader />
|
<Loader />
|
||||||
|
|
|
@ -44,6 +44,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#search-page h2 {
|
||||||
|
a {
|
||||||
|
.icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
&:hover .icon {
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#search-page ul.accounts-list {
|
#search-page ul.accounts-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
@ -174,7 +174,7 @@ function Search({ columnMode, ...props }) {
|
||||||
}, [q, type, instance]);
|
}, [q, type, instance]);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'/',
|
['/', 'Slash'],
|
||||||
(e) => {
|
(e) => {
|
||||||
searchFormRef.current?.focus?.();
|
searchFormRef.current?.focus?.();
|
||||||
},
|
},
|
||||||
|
@ -253,7 +253,14 @@ function Search({ columnMode, ...props }) {
|
||||||
{(!type || type === 'accounts') && (
|
{(!type || type === 'accounts') && (
|
||||||
<>
|
<>
|
||||||
{type !== 'accounts' && (
|
{type !== 'accounts' && (
|
||||||
<h2 class="timeline-header">Accounts</h2>
|
<h2 class="timeline-header">
|
||||||
|
Accounts{' '}
|
||||||
|
<Link
|
||||||
|
to={`/search?q=${encodeURIComponent(q)}&type=accounts`}
|
||||||
|
>
|
||||||
|
<Icon icon="arrow-right" size="l" />
|
||||||
|
</Link>
|
||||||
|
</h2>
|
||||||
)}
|
)}
|
||||||
{accountResults.length > 0 ? (
|
{accountResults.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
@ -273,7 +280,9 @@ function Search({ columnMode, ...props }) {
|
||||||
<div class="ui-state">
|
<div class="ui-state">
|
||||||
<Link
|
<Link
|
||||||
class="plain button"
|
class="plain button"
|
||||||
to={`/search?q=${q}&type=accounts`}
|
to={`/search?q=${encodeURIComponent(
|
||||||
|
q,
|
||||||
|
)}&type=accounts`}
|
||||||
>
|
>
|
||||||
See more accounts <Icon icon="arrow-right" />
|
See more accounts <Icon icon="arrow-right" />
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -295,7 +304,14 @@ function Search({ columnMode, ...props }) {
|
||||||
{(!type || type === 'hashtags') && (
|
{(!type || type === 'hashtags') && (
|
||||||
<>
|
<>
|
||||||
{type !== 'hashtags' && (
|
{type !== 'hashtags' && (
|
||||||
<h2 class="timeline-header">Hashtags</h2>
|
<h2 class="timeline-header">
|
||||||
|
Hashtags{' '}
|
||||||
|
<Link
|
||||||
|
to={`/search?q=${encodeURIComponent(q)}&type=hashtags`}
|
||||||
|
>
|
||||||
|
<Icon icon="arrow-right" size="l" />
|
||||||
|
</Link>
|
||||||
|
</h2>
|
||||||
)}
|
)}
|
||||||
{hashtagResults.length > 0 ? (
|
{hashtagResults.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
@ -331,7 +347,9 @@ function Search({ columnMode, ...props }) {
|
||||||
<div class="ui-state">
|
<div class="ui-state">
|
||||||
<Link
|
<Link
|
||||||
class="plain button"
|
class="plain button"
|
||||||
to={`/search?q=${q}&type=hashtags`}
|
to={`/search?q=${encodeURIComponent(
|
||||||
|
q,
|
||||||
|
)}&type=hashtags`}
|
||||||
>
|
>
|
||||||
See more hashtags <Icon icon="arrow-right" />
|
See more hashtags <Icon icon="arrow-right" />
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -353,7 +371,14 @@ function Search({ columnMode, ...props }) {
|
||||||
{(!type || type === 'statuses') && (
|
{(!type || type === 'statuses') && (
|
||||||
<>
|
<>
|
||||||
{type !== 'statuses' && (
|
{type !== 'statuses' && (
|
||||||
<h2 class="timeline-header">Posts</h2>
|
<h2 class="timeline-header">
|
||||||
|
Posts{' '}
|
||||||
|
<Link
|
||||||
|
to={`/search?q=${encodeURIComponent(q)}&type=statuses`}
|
||||||
|
>
|
||||||
|
<Icon icon="arrow-right" size="l" />
|
||||||
|
</Link>
|
||||||
|
</h2>
|
||||||
)}
|
)}
|
||||||
{statusResults.length > 0 ? (
|
{statusResults.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
@ -377,7 +402,9 @@ function Search({ columnMode, ...props }) {
|
||||||
<div class="ui-state">
|
<div class="ui-state">
|
||||||
<Link
|
<Link
|
||||||
class="plain button"
|
class="plain button"
|
||||||
to={`/search?q=${q}&type=statuses`}
|
to={`/search?q=${encodeURIComponent(
|
||||||
|
q,
|
||||||
|
)}&type=statuses`}
|
||||||
>
|
>
|
||||||
See more posts <Icon icon="arrow-right" />
|
See more posts <Icon icon="arrow-right" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -643,7 +643,7 @@ function PushNotificationsSection({ onClose }) {
|
||||||
const { instance } = api();
|
const { instance } = api();
|
||||||
const [uiState, setUIState] = useState('default');
|
const [uiState, setUIState] = useState('default');
|
||||||
const pushFormRef = useRef();
|
const pushFormRef = useRef();
|
||||||
const [allowNofitications, setAllowNotifications] = useState(false);
|
const [allowNotifications, setAllowNotifications] = useState(false);
|
||||||
const [needRelogin, setNeedRelogin] = useState(false);
|
const [needRelogin, setNeedRelogin] = useState(false);
|
||||||
const previousPolicyRef = useRef();
|
const previousPolicyRef = useRef();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -689,7 +689,7 @@ function PushNotificationsSection({ onClose }) {
|
||||||
ref={pushFormRef}
|
ref={pushFormRef}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
const values = Object.fromEntries(new FormData(pushFormRef.current));
|
const values = Object.fromEntries(new FormData(pushFormRef.current));
|
||||||
const allowNofitications = !!values['policy-allow'];
|
const allowNotifications = !!values['policy-allow'];
|
||||||
const params = {
|
const params = {
|
||||||
policy: values.policy,
|
policy: values.policy,
|
||||||
data: {
|
data: {
|
||||||
|
@ -718,9 +718,13 @@ function PushNotificationsSection({ onClose }) {
|
||||||
});
|
});
|
||||||
const policyChanged = previousPolicyRef.current !== params.policy;
|
const policyChanged = previousPolicyRef.current !== params.policy;
|
||||||
|
|
||||||
console.log('PN Form', { values, allowNofitications, params });
|
console.log('PN Form', {
|
||||||
|
values,
|
||||||
|
allowNotifications: allowNotifications,
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
|
||||||
if (allowNofitications && alertsCount > 0) {
|
if (allowNotifications && alertsCount > 0) {
|
||||||
if (policyChanged) {
|
if (policyChanged) {
|
||||||
console.debug('Policy changed.');
|
console.debug('Policy changed.');
|
||||||
removeSubscription()
|
removeSubscription()
|
||||||
|
@ -754,7 +758,7 @@ function PushNotificationsSection({ onClose }) {
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
disabled={isLoading || needRelogin}
|
disabled={isLoading || needRelogin}
|
||||||
name="policy-allow"
|
name="policy-allow"
|
||||||
checked={allowNofitications}
|
checked={allowNotifications}
|
||||||
onChange={async (e) => {
|
onChange={async (e) => {
|
||||||
const { checked } = e.target;
|
const { checked } = e.target;
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
@ -778,7 +782,7 @@ function PushNotificationsSection({ onClose }) {
|
||||||
Allow from{' '}
|
Allow from{' '}
|
||||||
<select
|
<select
|
||||||
name="policy"
|
name="policy"
|
||||||
disabled={isLoading || needRelogin || !allowNofitications}
|
disabled={isLoading || needRelogin || !allowNotifications}
|
||||||
>
|
>
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
|
@ -803,7 +807,7 @@ function PushNotificationsSection({ onClose }) {
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
hidden={!allowNofitications}
|
hidden={!allowNotifications}
|
||||||
>
|
>
|
||||||
<div class="shazam-container-inner">
|
<div class="shazam-container-inner">
|
||||||
<div class="sub-section">
|
<div class="sub-section">
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
.status-deck header {
|
.status-deck {
|
||||||
white-space: nowrap;
|
header {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
header h1 {
|
||||||
|
min-width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
header h1 .deck-back {
|
||||||
|
margin-left: -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-refresh .icon {
|
||||||
|
animation: spin 1s linear;
|
||||||
|
}
|
||||||
|
.button-refresh:is(:hover, :focus) .icon {
|
||||||
|
transition: transform 1s linear;
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.status-deck header h1 {
|
|
||||||
min-width: 0;
|
@keyframes spin {
|
||||||
flex-grow: 1;
|
to {
|
||||||
text-overflow: ellipsis;
|
transform: rotate(360deg);
|
||||||
overflow: hidden;
|
}
|
||||||
white-space: nowrap;
|
|
||||||
align-self: stretch;
|
|
||||||
}
|
|
||||||
.status-deck header h1 .deck-back {
|
|
||||||
margin-left: -16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-heading {
|
.hero-heading {
|
||||||
|
|
|
@ -245,6 +245,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
|
||||||
}, [id, uiState !== 'loading']);
|
}, [id, uiState !== 'loading']);
|
||||||
|
|
||||||
const scrollOffsets = useRef();
|
const scrollOffsets = useRef();
|
||||||
|
const lastInitContextTS = useRef();
|
||||||
const initContext = ({ reloadHero } = {}) => {
|
const initContext = ({ reloadHero } = {}) => {
|
||||||
console.debug('initContext', id);
|
console.debug('initContext', id);
|
||||||
setUIState('loading');
|
setUIState('loading');
|
||||||
|
@ -432,12 +433,31 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
lastInitContextTS.current = Date.now();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearTimeout(heroTimer);
|
clearTimeout(heroTimer);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(initContext, [id, masto]);
|
useEffect(initContext, [id, masto]);
|
||||||
|
|
||||||
|
const [showRefresh, setShowRefresh] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
let interval = setInterval(() => {
|
||||||
|
const now = Date.now();
|
||||||
|
if (
|
||||||
|
lastInitContextTS.current &&
|
||||||
|
now - lastInitContextTS.current >= 60_000
|
||||||
|
) {
|
||||||
|
setShowRefresh(true);
|
||||||
|
}
|
||||||
|
}, 60_000); // 1 minute
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!statuses.length) return;
|
if (!statuses.length) return;
|
||||||
console.debug('STATUSES', statuses);
|
console.debug('STATUSES', statuses);
|
||||||
|
@ -845,6 +865,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
|
||||||
enableTranslate
|
enableTranslate
|
||||||
onMediaClick={handleMediaClick}
|
onMediaClick={handleMediaClick}
|
||||||
onStatusLinkClick={handleStatusLinkClick}
|
onStatusLinkClick={handleStatusLinkClick}
|
||||||
|
showActionsBar={!!descendant}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{ancestor && repliesCount > 1 && (
|
{ancestor && repliesCount > 1 && (
|
||||||
|
@ -1094,6 +1115,18 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
|
||||||
>
|
>
|
||||||
<Icon icon="layout4" size="l" />
|
<Icon icon="layout4" size="l" />
|
||||||
</button>
|
</button>
|
||||||
|
{showRefresh && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="plain button-refresh"
|
||||||
|
onClick={() => {
|
||||||
|
states.reloadStatusPage++;
|
||||||
|
setShowRefresh(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="refresh" size="l" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<Menu2
|
<Menu2
|
||||||
align="end"
|
align="end"
|
||||||
portal={{
|
portal={{
|
||||||
|
@ -1400,6 +1433,7 @@ function SubComments({
|
||||||
size="s"
|
size="s"
|
||||||
enableTranslate
|
enableTranslate
|
||||||
onMediaClick={handleMediaClick}
|
onMediaClick={handleMediaClick}
|
||||||
|
showActionsBar
|
||||||
/>
|
/>
|
||||||
{!r.replies?.length && r.repliesCount > 0 && (
|
{!r.replies?.length && r.repliesCount > 0 && (
|
||||||
<div class="replies-link">
|
<div class="replies-link">
|
||||||
|
|
|
@ -37,6 +37,7 @@ function _enhanceContent(content, opts = {}) {
|
||||||
links.forEach((link) => {
|
links.forEach((link) => {
|
||||||
if (/^https?:\/\//i.test(link.textContent.trim())) {
|
if (/^https?:\/\//i.test(link.textContent.trim())) {
|
||||||
link.classList.add('has-url-text');
|
link.classList.add('has-url-text');
|
||||||
|
shortenLink(link);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -287,6 +288,30 @@ const defaultRejectFilter = [
|
||||||
const defaultRejectFilterMap = Object.fromEntries(
|
const defaultRejectFilterMap = Object.fromEntries(
|
||||||
defaultRejectFilter.map((nodeName) => [nodeName, true]),
|
defaultRejectFilter.map((nodeName) => [nodeName, true]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const URL_PREFIX_REGEX = /^(https?:\/\/(www\.)?|xmpp:)/;
|
||||||
|
const URL_DISPLAY_LENGTH = 30;
|
||||||
|
// Similar to https://github.com/mastodon/mastodon/blob/1666b1955992e16f4605b414c6563ca25b3a3f18/app/lib/text_formatter.rb#L54-L69
|
||||||
|
function shortenLink(link) {
|
||||||
|
if (!link || link.querySelector?.('*')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const url = link.innerText.trim();
|
||||||
|
const prefix = (url.match(URL_PREFIX_REGEX) || [])[0] || '';
|
||||||
|
if (!prefix) return;
|
||||||
|
const displayURL = url.slice(
|
||||||
|
prefix.length,
|
||||||
|
prefix.length + URL_DISPLAY_LENGTH,
|
||||||
|
);
|
||||||
|
const suffix = url.slice(prefix.length + URL_DISPLAY_LENGTH);
|
||||||
|
const cutoff = url.slice(prefix.length).length > URL_DISPLAY_LENGTH;
|
||||||
|
link.innerHTML = `<span class="invisible">${prefix}</span><span class=${
|
||||||
|
cutoff ? 'ellipsis' : ''
|
||||||
|
}>${displayURL}</span><span class="invisible">${suffix}</span>`;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
function extractTextNodes(dom, opts = {}) {
|
function extractTextNodes(dom, opts = {}) {
|
||||||
const textNodes = [];
|
const textNodes = [];
|
||||||
const rejectFilterMap = Object.assign(
|
const rejectFilterMap = Object.assign(
|
||||||
|
|
|
@ -9,6 +9,17 @@ function getHTMLText(html) {
|
||||||
div.querySelectorAll('br').forEach((br) => {
|
div.querySelectorAll('br').forEach((br) => {
|
||||||
br.replaceWith('\n');
|
br.replaceWith('\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// MASTODON-SPECIFIC classes
|
||||||
|
// Remove .invisible
|
||||||
|
div.querySelectorAll('.invisible').forEach((el) => {
|
||||||
|
el.remove();
|
||||||
|
});
|
||||||
|
// Add … at end of .ellipsis
|
||||||
|
div.querySelectorAll('.ellipsis').forEach((el) => {
|
||||||
|
el.append('...');
|
||||||
|
});
|
||||||
|
|
||||||
return div.innerText.replace(/[\r\n]{3,}/g, '\n\n').trim();
|
return div.innerText.replace(/[\r\n]{3,}/g, '\n\n').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ const states = proxy({
|
||||||
unfurledLinks: {},
|
unfurledLinks: {},
|
||||||
statusQuotes: {},
|
statusQuotes: {},
|
||||||
statusFollowedTags: {},
|
statusFollowedTags: {},
|
||||||
|
statusReply: {},
|
||||||
accounts: {},
|
accounts: {},
|
||||||
routeNotification: null,
|
routeNotification: null,
|
||||||
// Modals
|
// Modals
|
||||||
|
@ -187,9 +188,19 @@ export function saveStatus(status, instance, opts) {
|
||||||
if (oldStatus?._pinned) status._pinned = oldStatus._pinned;
|
if (oldStatus?._pinned) status._pinned = oldStatus._pinned;
|
||||||
// if (oldStatus?._filtered) status._filtered = oldStatus._filtered;
|
// if (oldStatus?._filtered) status._filtered = oldStatus._filtered;
|
||||||
states.statuses[key] = status;
|
states.statuses[key] = status;
|
||||||
if (status.reblog) {
|
if (status.reblog?.id) {
|
||||||
const key = statusKey(status.reblog.id, instance);
|
const srKey = statusKey(status.reblog.id, instance);
|
||||||
states.statuses[key] = status.reblog;
|
states.statuses[srKey] = status.reblog;
|
||||||
|
}
|
||||||
|
if (status.quote?.id) {
|
||||||
|
const sKey = statusKey(status.quote.id, instance);
|
||||||
|
states.statuses[sKey] = status.quote;
|
||||||
|
states.statusQuotes[key] = [
|
||||||
|
{
|
||||||
|
id: status.quote.id,
|
||||||
|
instance,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import { api } from './api';
|
||||||
import { extractTagsFromStatus, getFollowedTags } from './followed-tags';
|
import { extractTagsFromStatus, getFollowedTags } from './followed-tags';
|
||||||
|
import pmem from './pmem';
|
||||||
import { fetchRelationships } from './relationships';
|
import { fetchRelationships } from './relationships';
|
||||||
import states, { statusKey } from './states';
|
import states, { saveStatus, statusKey } from './states';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
|
|
||||||
export function groupBoosts(values) {
|
export function groupBoosts(values) {
|
||||||
|
@ -81,7 +83,7 @@ export function dedupeBoosts(items, instance) {
|
||||||
return filteredItems;
|
return filteredItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function groupContext(items) {
|
export function groupContext(items, instance) {
|
||||||
const contexts = [];
|
const contexts = [];
|
||||||
let contextIndex = 0;
|
let contextIndex = 0;
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
|
@ -173,12 +175,44 @@ export function groupContext(items) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.inReplyToId && item.inReplyToAccountId !== item.account.id) {
|
||||||
|
const sKey = statusKey(item.id, instance);
|
||||||
|
if (!states.statusReply[sKey]) {
|
||||||
|
// If it's a reply and not a thread
|
||||||
|
queueMicrotask(async () => {
|
||||||
|
try {
|
||||||
|
const { masto } = api({ instance });
|
||||||
|
// const replyToStatus = await masto.v1.statuses
|
||||||
|
// .$select(item.inReplyToId)
|
||||||
|
// .fetch();
|
||||||
|
const replyToStatus = await fetchStatus(item.inReplyToId, masto);
|
||||||
|
saveStatus(replyToStatus, instance, {
|
||||||
|
skipThreading: true,
|
||||||
|
skipUnfurling: true,
|
||||||
|
});
|
||||||
|
states.statusReply[sKey] = {
|
||||||
|
id: replyToStatus.id,
|
||||||
|
instance,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
// Silently fail
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newItems.push(item);
|
newItems.push(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
return newItems;
|
return newItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchStatus = pmem((statusID, masto) => {
|
||||||
|
return masto.v1.statuses.$select(statusID).fetch();
|
||||||
|
});
|
||||||
|
|
||||||
export async function assignFollowedTags(items, instance) {
|
export async function assignFollowedTags(items, instance) {
|
||||||
const followedTags = await getFollowedTags(); // [{name: 'tag'}, {...}]
|
const followedTags = await getFollowedTags(); // [{name: 'tag'}, {...}]
|
||||||
if (!followedTags.length) return;
|
if (!followedTags.length) return;
|
||||||
|
@ -219,7 +253,7 @@ export async function assignFollowedTags(items, instance) {
|
||||||
statusWithFollowedTags.forEach((s) => {
|
statusWithFollowedTags.forEach((s) => {
|
||||||
const { item, sKey, followedTags } = s;
|
const { item, sKey, followedTags } = s;
|
||||||
const r = relationships[item.account.id];
|
const r = relationships[item.account.id];
|
||||||
if (!r.following) {
|
if (r && !r.following) {
|
||||||
statusFollowedTags[sKey] = followedTags;
|
statusFollowedTags[sKey] = followedTags;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue