Compare commits

...

131 commits

Author SHA1 Message Date
github-actions[bot] 4ace792156
Merge 5c9a47c31e into 51457302f8 2024-05-01 16:14:58 +00:00
Lim Chee Aun 5c9a47c31e Might as well re-use it for instances search 2024-05-02 00:14:48 +08:00
Lim Chee Aun 65a4c3441c Add search for custom emojis 2024-05-02 00:14:25 +08:00
Lim Chee Aun 77bc06545c Handle inline images 2024-05-01 15:05:29 +08:00
Chee Aun 51457302f8
Merge pull request #506 from cheeaun/main
Update from main
2024-04-19 09:18:55 +08:00
Chee Aun b6cf53c221
Merge pull request #489 from cheeaun/main
Update from main
2024-04-17 20:21:00 +08:00
Chee Aun 65d51b077f
Merge pull request #475 from cheeaun/main
Update from main
2024-04-04 19:41:09 +08:00
Chee Aun ecd308ce39
Merge pull request #472 from cheeaun/main
Update from main
2024-03-28 20:55:24 +08:00
Chee Aun 8fc5e10be9
Merge pull request #465 from cheeaun/main
Update from main
2024-03-26 22:19:53 +08:00
Chee Aun c7c764a4f0
Merge pull request #459 from cheeaun/main
Update from main
2024-03-17 21:12:58 +08:00
Chee Aun e73ae563e6
Merge pull request #456 from cheeaun/main
Update from main
2024-03-12 14:57:02 +08:00
Chee Aun d34cce6d18
Merge pull request #450 from cheeaun/main
Update from main
2024-03-11 17:14:49 +08:00
Chee Aun aed84226a1
Merge pull request #446 from cheeaun/main
Update from main
2024-03-07 22:28:08 +08:00
Chee Aun 6827a3811e
Merge pull request #445 from cheeaun/main
Update from main
2024-03-06 19:24:58 +08:00
Chee Aun 83f9498b79
Merge pull request #429 from cheeaun/main
Update from main
2024-03-06 17:52:16 +08:00
Chee Aun 77818d3ac8
Merge pull request #412 from cheeaun/main
Update from main
2024-02-25 20:32:39 +08:00
Chee Aun 35a31e1613
Merge pull request #406 from cheeaun/main
Update from main
2024-02-04 00:40:45 +08:00
Chee Aun 247ed4a7e1
Merge pull request #404 from cheeaun/main
Update from main
2024-01-21 23:05:33 +08:00
Chee Aun 98e6a6e42e
Merge pull request #401 from cheeaun/main
Update from main
2024-01-18 10:49:55 +08:00
Chee Aun fe91c13703
Merge pull request #397 from cheeaun/main
Update from main
2024-01-16 18:36:41 +08:00
Chee Aun e791ea8015
Merge pull request #393 from cheeaun/main
Update from main
2024-01-11 21:49:26 +08:00
Chee Aun dbef125ee3
Merge pull request #387 from cheeaun/main
Update from main
2024-01-09 00:13:19 +08:00
Chee Aun 32c53b8c6e
Merge pull request #388 from cheeaun/hotfix/infinite-loop-intersection-observer
[Hotfix] Infinite loop bug in intersection observer
2024-01-06 03:24:21 +08:00
Lim Chee Aun bd112f19b1 Potential fix to infinite loop of intersection observer 2024-01-06 03:18:08 +08:00
Chee Aun adf0b351c1
Merge pull request #383 from cheeaun/main
Update from main
2024-01-05 10:16:09 +08:00
Chee Aun 8aa05422b1
Merge pull request #375 from cheeaun/main
Update from main
2023-12-31 18:21:25 +08:00
Chee Aun 1e21f519f3
Merge pull request #360 from cheeaun/main
Update from main
2023-12-22 23:34:15 +08:00
Chee Aun 19da64a787
Merge pull request #352 from cheeaun/main
Update from main
2023-12-13 23:18:23 +08:00
Chee Aun efca016d81
Merge pull request #337 from cheeaun/main
Update from main
2023-12-06 15:23:20 +08:00
Chee Aun 1d9d22a214
Merge pull request #334 from cheeaun/main
Update from main
2023-11-25 18:04:32 +08:00
Chee Aun e4dd76cb11
Merge pull request #333 from cheeaun/main
Update from main
2023-11-22 11:16:11 +08:00
Chee Aun 0f537667d8
Merge pull request #327 from cheeaun/main
Update from main
2023-11-20 13:29:33 +08:00
Chee Aun 3f575142f1
Merge pull request #321 from cheeaun/main
Update from main
2023-11-16 20:01:02 +08:00
Chee Aun 11407d0f3c
Merge pull request #311 from cheeaun/main
Update from main
2023-11-13 22:13:48 +08:00
Chee Aun 84b6368253
Merge pull request #310 from cheeaun/main
Update from main
2023-11-07 14:59:48 +08:00
Chee Aun a75b214999
Merge pull request #308 from cheeaun/main
Update from main
2023-11-07 00:52:17 +08:00
Chee Aun 33c3b63b6e
Merge pull request #307 from cheeaun/main
Update from main
2023-11-06 23:05:24 +08:00
Chee Aun 74991c326d
Merge pull request #286 from cheeaun/main
Update from main
2023-11-06 21:18:15 +08:00
Chee Aun f558a8cd32
Merge pull request #283 from cheeaun/main
Update from main
2023-10-27 01:04:55 +08:00
Chee Aun 87f1d17ce3
Merge pull request #272 from cheeaun/main
Update from main
2023-10-26 20:26:20 +08:00
Chee Aun 0cf7d683ee
Merge pull request #268 from cheeaun/main
Update from main
2023-10-20 00:44:53 +08:00
Chee Aun cb80057f21
Merge pull request #267 from cheeaun/main
Update from main
2023-10-19 07:17:54 +08:00
Chee Aun bf6ee572fb
Merge pull request #266 from cheeaun/main
Update from main
2023-10-18 16:36:11 +08:00
Chee Aun 7b049385f7
Merge pull request #264 from cheeaun/main
Update from main
2023-10-16 22:10:32 +08:00
Chee Aun 6feb2e7b41
Merge pull request #263 from cheeaun/main
Update from main
2023-10-16 20:06:11 +08:00
Chee Aun 4ea8e2c145
Merge pull request #261 from cheeaun/main
Update from main
2023-10-16 19:53:00 +08:00
Chee Aun cd68aee186
Merge pull request #260 from cheeaun/main
Update from main
2023-10-12 01:34:30 +08:00
Chee Aun 5ba2af0970
Merge pull request #259 from cheeaun/main
Update from main
2023-10-09 23:10:34 +08:00
Chee Aun 9b1800dc56
Merge pull request #254 from cheeaun/main
Update from main
2023-10-07 22:23:18 +08:00
Chee Aun 2e97f19133
Merge pull request #252 from cheeaun/main
Update from main
2023-10-06 21:19:29 +08:00
Chee Aun 3f23d42966
Merge pull request #251 from cheeaun/main
Update from main
2023-10-04 12:35:04 +08:00
Chee Aun e9c5025d31
Merge pull request #250 from cheeaun/main
Update from main
2023-10-04 00:00:27 +08:00
Chee Aun eb013645e7
Merge pull request #248 from cheeaun/main
Update from main
2023-10-03 19:21:44 +08:00
Chee Aun d5a3b48f0f
Merge pull request #228 from cheeaun/main
Update from main
2023-10-02 23:05:44 +08:00
Chee Aun b8d92bceb2
Merge pull request #223 from cheeaun/main
Update from main
2023-09-01 13:00:48 +08:00
Chee Aun def1e8d099
Merge pull request #206 from cheeaun/main
Update from main
2023-08-24 17:50:49 +08:00
Chee Aun 8e2099daa7
Merge pull request #200 from cheeaun/main
Update from main
2023-08-06 12:03:53 +08:00
Chee Aun ba81352844
Merge pull request #195 from cheeaun/main
Update from main
2023-07-30 15:23:09 +08:00
Chee Aun 650b71e9cc
Merge pull request #194 from cheeaun/main
Update from main
2023-07-23 23:29:16 +08:00
Chee Aun a6e369b1a8
Merge pull request #193 from cheeaun/main 2023-07-23 01:07:14 +08:00
Chee Aun f479feba65
Merge pull request #192 from cheeaun/main
Update from main
2023-07-22 21:06:22 +08:00
Chee Aun 38680aa6e7
Merge pull request #190 from cheeaun/main
Update from main
2023-07-22 10:04:50 +08:00
Chee Aun d1d606fa10
Merge pull request #182 from cheeaun/main
Update from main
2023-07-21 10:32:30 +08:00
Chee Aun 375da8d173
Merge pull request #173 from cheeaun/main
Update from main
2023-06-30 23:25:52 +08:00
Chee Aun 2c31e8e04c
Merge pull request #172 from cheeaun/main
Update from main
2023-06-24 09:15:26 +08:00
Chee Aun 534c4c97cd
Merge pull request #170 from cheeaun/main
Update from main
2023-06-20 21:38:11 +08:00
Chee Aun 482a64cfac
Merge pull request #166 from cheeaun/main
Update from main
2023-06-16 18:21:45 +08:00
Chee Aun 2dc1343f54
Merge pull request #156 from cheeaun/main
Update from main
2023-06-15 09:05:28 +08:00
Chee Aun 5e52fa87e0
Merge pull request #153 from cheeaun/main
Update from main
2023-05-30 09:46:02 +08:00
Chee Aun 6b03ae1fee
Merge pull request #152 from cheeaun/main
Update from main
2023-05-27 21:57:38 +08:00
Chee Aun c763d8b954
Merge pull request #151 from cheeaun/main
Update from main
2023-05-23 13:11:19 +08:00
Chee Aun 0a5d7267d5
Merge pull request #143 from cheeaun/main
Update from main
2023-05-22 23:40:44 +08:00
Chee Aun 5ee926481a
Merge pull request #142 from cheeaun/main
Update from main
2023-05-18 00:13:24 +08:00
Chee Aun 0cd9a2db6e
Merge pull request #141 from cheeaun/main
Update from main
2023-05-16 19:38:41 +08:00
Chee Aun 69f9b750c2
Merge pull request #139 from cheeaun/main
Update from main
2023-05-14 22:02:10 +08:00
Chee Aun f5955ef258
Merge pull request #133 from cheeaun/main
Update from main
2023-05-14 11:55:03 +08:00
Chee Aun 27a999f733
Merge pull request #122 from cheeaun/main
Update from main
2023-05-11 21:59:39 +08:00
Chee Aun 54271101c1
Merge pull request #119 from cheeaun/main
Update from main
2023-04-28 23:23:10 +08:00
Chee Aun d0cbb0812d
Merge pull request #117 from cheeaun/main
Update from main
2023-04-26 15:36:11 +08:00
Chee Aun ad45bf9d19
Merge pull request #97 from cheeaun/main
Update from main
2023-04-25 22:54:01 +08:00
Chee Aun 982f7b3ec4
Merge pull request #94 from cheeaun/main
Update from main
2023-04-07 21:57:29 +08:00
Chee Aun 4e50f227d8
Merge pull request #93 from cheeaun/main
Update from main
2023-04-03 09:28:40 +08:00
Chee Aun 546e77d3e1
Merge pull request #92 from cheeaun/main
Update from main
2023-04-03 01:25:32 +08:00
Chee Aun e29f14bbcf
Merge pull request #86 from cheeaun/main
Update from main
2023-03-31 23:21:27 +08:00
Chee Aun 05e87e084a
Merge pull request #84 from cheeaun/main
Update from main
2023-03-16 23:31:30 +08:00
Chee Aun 01f10d3daa
Merge pull request #83 from cheeaun/main
Update from main
2023-03-15 23:25:58 +08:00
Chee Aun fc615e0c0d
Merge pull request #82 from cheeaun/main
Update from main
2023-03-15 22:12:17 +08:00
Chee Aun 25e9771754
Merge pull request #80 from cheeaun/main
Update from main
2023-03-15 21:21:04 +08:00
Chee Aun 5e916559b3
Merge pull request #79 from cheeaun/main
Update from main
2023-03-15 20:49:59 +08:00
Chee Aun 883fe39b6c
Merge pull request #78 from cheeaun/main
Update from main
2023-03-03 13:08:28 +08:00
Chee Aun 9933d83846
Merge pull request #77 from cheeaun/main
Update from main
2023-03-02 22:56:43 +08:00
Chee Aun 7d806301f2
Merge pull request #74 from cheeaun/main
Update from main
2023-03-02 22:29:28 +08:00
Chee Aun faf9cbf23d
Merge pull request #73 from cheeaun/main
Update from main
2023-02-24 12:35:44 +08:00
Chee Aun a0f79e7eea
Merge pull request #71 from cheeaun/main
Update from main
2023-02-24 12:04:00 +08:00
Chee Aun 0b1974e94b
Merge pull request #70 from cheeaun/main
Update from main
2023-02-23 23:31:09 +08:00
Chee Aun b4a4615b9a
Merge pull request #68 from cheeaun/main
Update from main
2023-02-22 09:51:37 +08:00
Chee Aun dda14587c0
Merge pull request #67 from cheeaun/main
Update from main
2023-02-22 00:47:07 +08:00
Chee Aun ed9289d8c6
Merge pull request #66 from cheeaun/main
Update from main
2023-02-21 09:22:54 +08:00
Chee Aun 6274f2f24f
Merge pull request #63 from cheeaun/main
Update from main
2023-02-20 00:49:21 +08:00
Chee Aun b4e8ba820c
Merge pull request #62 from cheeaun/main
Update from main
2023-02-20 00:23:56 +08:00
Chee Aun 29896dfe0e
Merge pull request #54 from cheeaun/main
Update from main
2023-02-19 22:33:56 +08:00
Chee Aun 69c3f1a082
Merge pull request #53 from cheeaun/main
Update from main
2023-02-01 02:10:43 +08:00
Chee Aun 451dc57a69
Merge pull request #49 from cheeaun/main
Update from main
2023-02-01 01:27:15 +08:00
Chee Aun 4fbee9168d
Merge pull request #48 from cheeaun/main
Update from main
2023-01-17 21:34:52 +08:00
Chee Aun 6ecc015199
Merge pull request #47 from cheeaun/main
Update from main
2023-01-17 18:05:25 +08:00
Chee Aun a7a3d5605b
Merge pull request #42 from cheeaun/main
Update from main
2023-01-06 23:24:13 +08:00
Chee Aun ad4ed66cd6
Merge pull request #41 from cheeaun/main
Update from main
2023-01-01 19:26:30 +08:00
Chee Aun 4277992773
Merge pull request #39 from cheeaun/main
Update from main
2023-01-01 19:06:33 +08:00
Chee Aun 6bcf6b143c
Merge pull request #38 from cheeaun/main
Update from main
2022-12-28 20:57:56 +08:00
Chee Aun 9e9f7a6ea1
Merge pull request #37 from cheeaun/main
Update from main
2022-12-27 22:07:51 +08:00
Chee Aun f0014cb26a
Merge pull request #36 from cheeaun/main
Update from main
2022-12-27 19:59:16 +08:00
Chee Aun b0e118fcab
Merge pull request #34 from cheeaun/main
Update from main
2022-12-27 09:57:15 +08:00
Chee Aun f51201a787
Merge pull request #33 from cheeaun/main
Update from main
2022-12-27 01:18:41 +08:00
Chee Aun 5a035089ab
Merge pull request #31 from cheeaun/main
Update from main
2022-12-27 00:10:32 +08:00
Chee Aun 206f00af40
Merge pull request #27 from cheeaun/main
Update from main
2022-12-24 23:20:13 +08:00
Chee Aun 13de3d9263
Merge pull request #20 from cheeaun/main
Update from main
2022-12-24 23:06:13 +08:00
Chee Aun eb41ddf2de
Merge pull request #18 from cheeaun/main
Update from main
2022-12-22 09:04:05 +08:00
Chee Aun 940e8f5376
Merge pull request #16 from cheeaun/main
Update from main
2022-12-22 08:44:56 +08:00
Chee Aun 77ba42dba9
Merge pull request #13 from cheeaun/main
Update from main
2022-12-21 21:32:38 +08:00
Chee Aun 95e204c439
Merge pull request #12 from cheeaun/main
Update from main
2022-12-19 20:12:56 +08:00
Chee Aun 82770e8035
Merge pull request #11 from cheeaun/main 2022-12-19 18:44:01 +08:00
Chee Aun 818c8e61cd
Merge pull request #10 from cheeaun/main
Update from main
2022-12-18 13:07:45 +08:00
Chee Aun 3b8592e946
Merge pull request #9 from cheeaun/main
Update from main
2022-12-17 18:37:19 +08:00
Chee Aun c0c7d65034
Merge pull request #8 from cheeaun/main
Update from main
2022-12-16 13:59:24 +08:00
Chee Aun 5631126e8d
Merge pull request #7 from cheeaun/main
Update from main
2022-12-16 13:33:54 +08:00
Chee Aun bd2ed53f32
Merge pull request #6 from cheeaun/main
Update from main
2022-12-16 12:28:52 +08:00
Chee Aun 694fa22942
Merge pull request #5 from cheeaun/main
Update from main
2022-12-15 21:48:39 +08:00
Chee Aun 15c3979815
Merge pull request #4 from cheeaun/main
Update from main
2022-12-15 17:45:04 +08:00
Chee Aun ab5f53273f
Merge pull request #3 from cheeaun/main
Update from main
2022-12-15 14:48:02 +08:00
Chee Aun 19c2f9b048
Merge pull request #2 from cheeaun/main
Update from main
2022-12-15 13:17:46 +08:00
Chee Aun a45250ac96
Merge pull request #1 from cheeaun/main
Update from main
2022-12-15 12:02:26 +08:00
7 changed files with 435 additions and 186 deletions

9
package-lock.json generated
View file

@ -21,6 +21,7 @@
"dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2",
"fast-equals": "~5.0.1",
"fuse.js": "~7.0.0",
"html-prettify": "^1.0.7",
"idb-keyval": "~6.2.1",
"just-debounce-it": "~3.2.0",
@ -5130,6 +5131,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/fuse.js": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
"integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==",
"engines": {
"node": ">=10"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",

View file

@ -23,6 +23,7 @@
"dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2",
"fast-equals": "~5.0.1",
"fuse.js": "~7.0.0",
"html-prettify": "^1.0.7",
"idb-keyval": "~6.2.1",
"just-debounce-it": "~3.2.0",

View file

@ -597,41 +597,123 @@
#custom-emojis-sheet {
max-height: 50vh;
max-height: 50dvh;
}
#custom-emojis-sheet main {
mask-image: none;
}
#custom-emojis-sheet .custom-emojis-list .section-header {
font-size: 80%;
text-transform: uppercase;
color: var(--text-insignificant-color);
padding: 8px 0 4px;
position: sticky;
top: 0;
background-color: var(--bg-blur-color);
backdrop-filter: blur(1px);
}
#custom-emojis-sheet .custom-emojis-list section {
display: flex;
flex-wrap: wrap;
}
#custom-emojis-sheet .custom-emojis-list button {
border-radius: 8px;
background-image: radial-gradient(
closest-side,
var(--img-bg-color),
transparent
);
}
#custom-emojis-sheet .custom-emojis-list button:is(:hover, :focus) {
filter: none;
background-color: var(--bg-faded-color);
}
#custom-emojis-sheet .custom-emojis-list button img {
transition: transform 0.1s ease-out;
}
#custom-emojis-sheet .custom-emojis-list button:is(:hover, :focus) img {
transform: scale(1.5);
header {
.loader-container {
margin: 0;
}
form {
margin: 8px 0 0;
input {
width: 100%;
min-width: 0;
font-size: 0.8em;
}
}
}
main {
mask-image: none;
min-height: 40vh;
padding-bottom: 88px;
}
.custom-emojis-matches {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-wrap: wrap;
}
.custom-emojis-list {
.section-header {
font-size: 80%;
text-transform: uppercase;
color: var(--text-insignificant-color);
padding: 8px 0 4px;
position: sticky;
top: 0;
background-color: var(--bg-color);
z-index: 1;
}
section {
display: flex;
flex-wrap: wrap;
}
button {
color: var(--text-color);
border-radius: 8px;
background-image: radial-gradient(
closest-side,
var(--img-bg-color),
transparent
);
text-shadow: 0 1px 0 var(--bg-color);
position: relative;
min-width: 44px;
min-height: 44px;
font-variant-numeric: slashed-zero;
font-feature-settings: 'ss01';
&[data-title]:after {
max-width: 50vw;
pointer-events: none;
position: absolute;
content: attr(data-title);
left: 50%;
top: 0;
background-color: var(--bg-color);
padding: 2px 4px;
border-radius: 4px;
font-size: 12px;
border: 1px solid var(--text-color);
transform: translate(-50%, -110%);
opacity: 0;
transition: opacity 0.1s ease-out 0.1s;
font-family: var(--monospace-font);
line-height: 1;
}
&.edge-left[data-title]:after {
left: 0;
transform: translate(0, -110%);
}
&.edge-right[data-title]:after {
left: 100%;
transform: translate(-100%, -110%);
}
&:is(:hover, :focus) {
z-index: 1;
filter: none;
background-color: var(--bg-faded-color);
&[data-title]:after {
opacity: 1;
}
}
img {
transition: transform 0.1s ease-out;
}
&:is(:hover, :focus) img {
transform: scale(2);
}
&.edge-left img {
transform-origin: left center;
}
&.edge-right img {
transform-origin: right center;
}
code {
font-size: 0.8em;
}
}
}
}
.compose-field-container {

View file

@ -3,8 +3,16 @@ import './compose.css';
import '@github/text-expander-element';
import { MenuItem } from '@szhsin/react-menu';
import { deepEqual } from 'fast-equals';
import Fuse from 'fuse.js';
import { memo } from 'preact/compat';
import { forwardRef } from 'preact/compat';
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'preact/hooks';
import { useHotkeys } from 'react-hotkeys-hook';
import stringLength from 'string-length';
import { uid } from 'uid/single';
@ -21,6 +29,7 @@ import db from '../utils/db';
import emojifyText from '../utils/emojify-text';
import localeMatch from '../utils/locale-match';
import openCompose from '../utils/open-compose';
import pmem from '../utils/pmem';
import shortenNumber from '../utils/shorten-number';
import showToast from '../utils/show-toast';
import states, { saveStatus } from '../utils/states';
@ -181,6 +190,8 @@ function highlightText(text, { maxCharacters = Infinity }) {
const rtf = new Intl.RelativeTimeFormat();
const CUSTOM_EMOJIS_COUNT = 100;
function Compose({
onClose,
replyToStatus,
@ -1423,25 +1434,40 @@ function autoResizeTextarea(textarea) {
}
}
async function _getCustomEmojis(instance, masto) {
const emojis = await masto.v1.customEmojis.list();
const visibleEmojis = emojis.filter((e) => e.visibleInPicker);
const searcher = new Fuse(visibleEmojis, {
keys: ['shortcode'],
findAllMatches: true,
});
return [visibleEmojis, searcher];
}
const getCustomEmojis = pmem(_getCustomEmojis, {
// Limit by time to reduce memory usage
// Cached by instance
matchesArg: (cacheKeyArg, keyArg) => cacheKeyArg.instance === keyArg.instance,
maxAge: 30 * 60 * 1000, // 30 minutes
});
const Textarea = forwardRef((props, ref) => {
const { masto } = api();
const { masto, instance } = api();
const [text, setText] = useState(ref.current?.value || '');
const { maxCharacters, performSearch = () => {}, ...textareaProps } = props;
// const snapStates = useSnapshot(states);
// const charCount = snapStates.composerCharacterCount;
const customEmojis = useRef();
// const customEmojis = useRef();
const searcherRef = useRef();
useEffect(() => {
(async () => {
try {
const emojis = await masto.v1.customEmojis.list();
console.log({ emojis });
customEmojis.current = emojis;
} catch (e) {
// silent fail
getCustomEmojis(instance, masto)
.then((r) => {
const [emojis, searcher] = r;
searcherRef.current = searcher;
})
.catch((e) => {
console.error(e);
}
})();
});
}, []);
const textExpanderRef = useRef();
@ -1467,23 +1493,26 @@ const Textarea = forwardRef((props, ref) => {
// const emojis = customEmojis.current.filter((emoji) =>
// emoji.shortcode.startsWith(text),
// );
const emojis = filterShortcodes(customEmojis.current, text);
// const emojis = filterShortcodes(customEmojis.current, text);
const results = searcherRef.current?.search(text, {
limit: 5,
});
let html = '';
emojis.forEach((emoji) => {
results.forEach(({ item: emoji }) => {
const { shortcode, url } = emoji;
html += `
<li role="option" data-value="${encodeHTML(shortcode)}">
<img src="${encodeHTML(
url,
)}" width="16" height="16" alt="" loading="lazy" />
:${encodeHTML(shortcode)}:
${encodeHTML(shortcode)}
</li>`;
});
// console.log({ emojis, html });
menu.innerHTML = html;
provide(
Promise.resolve({
matched: emojis.length > 0,
matched: results.length > 0,
fragment: menu,
}),
);
@ -2185,38 +2214,19 @@ function CustomEmojisModal({
}) {
const [uiState, setUIState] = useState('default');
const customEmojisList = useRef([]);
const [customEmojis, setCustomEmojis] = useState({});
const [customEmojis, setCustomEmojis] = useState([]);
const recentlyUsedCustomEmojis = useMemo(
() => store.account.get('recentlyUsedCustomEmojis') || [],
);
const searcherRef = useRef();
useEffect(() => {
setUIState('loading');
(async () => {
try {
const emojis = await masto.v1.customEmojis.list();
// Group emojis by category
const emojisCat = {
'--recent--': recentlyUsedCustomEmojis.filter((emoji) =>
emojis.find((e) => e.shortcode === emoji.shortcode),
),
};
const othersCat = [];
emojis.forEach((emoji) => {
if (!emoji.visibleInPicker) return;
customEmojisList.current?.push?.(emoji);
if (!emoji.category) {
othersCat.push(emoji);
return;
}
if (!emojisCat[emoji.category]) {
emojisCat[emoji.category] = [];
}
emojisCat[emoji.category].push(emoji);
});
if (othersCat.length) {
emojisCat['--others--'] = othersCat;
}
setCustomEmojis(emojisCat);
const [emojis, searcher] = await getCustomEmojis(instance, masto);
console.log('emojis', emojis);
searcherRef.current = searcher;
setCustomEmojis(emojis);
setUIState('default');
} catch (e) {
setUIState('error');
@ -2225,6 +2235,83 @@ function CustomEmojisModal({
})();
}, []);
const customEmojisCatList = useMemo(() => {
// Group emojis by category
const emojisCat = {
'--recent--': recentlyUsedCustomEmojis.filter((emoji) =>
customEmojis.find((e) => e.shortcode === emoji.shortcode),
),
};
const othersCat = [];
customEmojis.forEach((emoji) => {
customEmojisList.current?.push?.(emoji);
if (!emoji.category) {
othersCat.push(emoji);
return;
}
if (!emojisCat[emoji.category]) {
emojisCat[emoji.category] = [];
}
emojisCat[emoji.category].push(emoji);
});
if (othersCat.length) {
emojisCat['--others--'] = othersCat;
}
return emojisCat;
}, [customEmojis]);
const scrollableRef = useRef();
const [matches, setMatches] = useState(null);
const onFind = useCallback(
(e) => {
const { value } = e.target;
if (value) {
const results = searcherRef.current?.search(value, {
limit: CUSTOM_EMOJIS_COUNT,
});
setMatches(results.map((r) => r.item));
scrollableRef.current?.scrollTo?.(0, 0);
} else {
setMatches(null);
}
},
[customEmojis],
);
const onSelectEmoji = useCallback(
(emoji) => {
onSelect?.(emoji);
onClose?.();
queueMicrotask(() => {
let recentlyUsedCustomEmojis =
store.account.get('recentlyUsedCustomEmojis') || [];
const recentlyUsedEmojiIndex = recentlyUsedCustomEmojis.findIndex(
(e) => e.shortcode === emoji.shortcode,
);
if (recentlyUsedEmojiIndex !== -1) {
// Move emoji to index 0
recentlyUsedCustomEmojis.splice(recentlyUsedEmojiIndex, 1);
recentlyUsedCustomEmojis.unshift(emoji);
} else {
recentlyUsedCustomEmojis.unshift(emoji);
// Remove unavailable ones
recentlyUsedCustomEmojis = recentlyUsedCustomEmojis.filter((e) =>
customEmojisList.current?.find?.(
(emoji) => emoji.shortcode === e.shortcode,
),
);
// Limit to 10
recentlyUsedCustomEmojis = recentlyUsedCustomEmojis.slice(0, 10);
}
// Store back
store.account.set('recentlyUsedCustomEmojis', recentlyUsedCustomEmojis);
});
},
[onSelect],
);
return (
<div id="custom-emojis-sheet" class="sheet">
{!!onClose && (
@ -2233,107 +2320,167 @@ function CustomEmojisModal({
</button>
)}
<header>
<b>Custom emojis</b>{' '}
{uiState === 'loading' ? (
<Loader />
) : (
<small class="insignificant"> {instance}</small>
)}
</header>
<main>
<div class="custom-emojis-list">
{uiState === 'error' && (
<div class="ui-state">
<p>Error loading custom emojis</p>
</div>
<div>
<b>Custom emojis</b>{' '}
{uiState === 'loading' ? (
<Loader />
) : (
<small class="insignificant"> {instance}</small>
)}
{uiState === 'default' &&
Object.entries(customEmojis).map(
([category, emojis]) =>
!!emojis?.length && (
<>
<div class="section-header">
{{
'--recent--': 'Recently used',
'--others--': 'Others',
}[category] || category}
</div>
<section>
{emojis.map((emoji) => (
<button
key={emoji}
type="button"
class="plain4"
onClick={() => {
onClose();
requestAnimationFrame(() => {
onSelect(`:${emoji.shortcode}:`);
});
let recentlyUsedCustomEmojis =
store.account.get('recentlyUsedCustomEmojis') ||
[];
const recentlyUsedEmojiIndex =
recentlyUsedCustomEmojis.findIndex(
(e) => e.shortcode === emoji.shortcode,
);
if (recentlyUsedEmojiIndex !== -1) {
// Move emoji to index 0
recentlyUsedCustomEmojis.splice(
recentlyUsedEmojiIndex,
1,
);
recentlyUsedCustomEmojis.unshift(emoji);
} else {
recentlyUsedCustomEmojis.unshift(emoji);
// Remove unavailable ones
recentlyUsedCustomEmojis =
recentlyUsedCustomEmojis.filter((e) =>
customEmojisList.current?.find?.(
(emoji) => emoji.shortcode === e.shortcode,
),
);
// Limit to 10
recentlyUsedCustomEmojis =
recentlyUsedCustomEmojis.slice(0, 10);
}
// Store back
store.account.set(
'recentlyUsedCustomEmojis',
recentlyUsedCustomEmojis,
);
}}
title={`:${emoji.shortcode}:`}
>
<picture>
{!!emoji.staticUrl && (
<source
srcset={emoji.staticUrl}
media="(prefers-reduced-motion: reduce)"
/>
)}
<img
class="shortcode-emoji"
src={emoji.url || emoji.staticUrl}
alt={emoji.shortcode}
width="16"
height="16"
loading="lazy"
decoding="async"
/>
</picture>
</button>
))}
</section>
</>
),
)}
</div>
<form
onSubmit={(e) => {
e.preventDefault();
const emoji = matches[0];
if (emoji) {
onSelectEmoji(`:${emoji.shortcode}:`);
}
}}
>
<input
type="search"
placeholder="Search emoji"
onInput={onFind}
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellCheck="false"
dir="auto"
/>
</form>
</header>
<main ref={scrollableRef}>
{matches !== null ? (
<ul class="custom-emojis-matches custom-emojis-list">
{matches.map((emoji) => (
<li key={emoji.shortcode} class="custom-emojis-match">
<CustomEmojiButton
emoji={emoji}
onClick={() => {
onSelectEmoji(`:${emoji.shortcode}:`);
}}
showCode
/>
</li>
))}
</ul>
) : (
<div class="custom-emojis-list">
{uiState === 'error' && (
<div class="ui-state">
<p>Error loading custom emojis</p>
</div>
)}
{uiState === 'default' &&
Object.entries(customEmojisCatList).map(
([category, emojis]) =>
!!emojis?.length && (
<>
<div class="section-header">
{{
'--recent--': 'Recently used',
'--others--': 'Others',
}[category] || category}
</div>
<CustomEmojisList
emojis={emojis}
onSelect={onSelectEmoji}
/>
</>
),
)}
</div>
)}
</main>
</div>
);
}
const CustomEmojisList = memo(({ emojis, onSelect }) => {
const [max, setMax] = useState(CUSTOM_EMOJIS_COUNT);
const showMore = emojis.length > max;
return (
<section>
{emojis.slice(0, max).map((emoji) => (
<CustomEmojiButton
key={emoji.shortcode}
emoji={emoji}
onClick={() => {
onSelect(`:${emoji.shortcode}:`);
}}
/>
))}
{showMore && (
<button
type="button"
class="plain small"
onClick={() => setMax(max + CUSTOM_EMOJIS_COUNT)}
>
{(emojis.length - max).toLocaleString()} more
</button>
)}
</section>
);
});
const CustomEmojiButton = memo(({ emoji, onClick, showCode }) => {
const addEdges = (e) => {
// Add edge-left or edge-right class based on self position relative to scrollable parent
// If near left edge, add edge-left, if near right edge, add edge-right
const buffer = 88;
const parent = e.currentTarget.closest('main');
if (parent) {
const rect = parent.getBoundingClientRect();
const selfRect = e.currentTarget.getBoundingClientRect();
const targetClassList = e.currentTarget.classList;
if (selfRect.left < rect.left + buffer) {
targetClassList.add('edge-left');
targetClassList.remove('edge-right');
} else if (selfRect.right > rect.right - buffer) {
targetClassList.add('edge-right');
targetClassList.remove('edge-left');
} else {
targetClassList.remove('edge-left', 'edge-right');
}
}
};
return (
<button
type="button"
className="plain4"
onClick={onClick}
data-title={showCode ? undefined : emoji.shortcode}
onPointerEnter={addEdges}
onFocus={addEdges}
>
<picture>
{!!emoji.staticUrl && (
<source
srcSet={emoji.staticUrl}
media="(prefers-reduced-motion: reduce)"
/>
)}
<img
className="shortcode-emoji"
src={emoji.url || emoji.staticUrl}
alt={emoji.shortcode}
width="24"
height="24"
loading="lazy"
decoding="async"
/>
</picture>
{showCode && (
<>
{' '}
<code>{emoji.shortcode}</code>
</>
)}
</button>
);
});
const GIFS_PER_PAGE = 20;
function GIFPickerModal({ onClose = () => {}, onSelect = () => {} }) {
const [uiState, setUIState] = useState('default');

View file

@ -825,6 +825,12 @@
.timeline-deck .status .content.truncated ~ .card {
display: none;
}
.status .content .inner-content {
> img[height] {
height: auto;
aspect-ratio: var(--original-aspect-ratio);
}
}
.status .content .inner-content a:not(.mention, .has-url-text) {
color: var(--link-text-color);
}
@ -2380,8 +2386,8 @@ a.card:is(:hover, :focus):visited {
max-width: 100%;
height: 1.2em;
vertical-align: text-bottom;
object-fit: cover;
object-position: left;
object-fit: contain;
/* object-position: left; */
}
/* EDIT HISTORY */

View file

@ -1,5 +1,6 @@
import './login.css';
import Fuse from 'fuse.js';
import { useEffect, useRef, useState } from 'preact/hooks';
import { useSearchParams } from 'react-router-dom';
@ -27,12 +28,14 @@ function Login() {
);
const [instancesList, setInstancesList] = useState([]);
const searcher = useRef();
useEffect(() => {
(async () => {
try {
const res = await fetch(instancesListURL);
const data = await res.json();
setInstancesList(data);
searcher.current = new Fuse(data);
} catch (e) {
// Silently fail
console.error(e);
@ -90,21 +93,11 @@ function Login() {
!/[\s\/\\@]/.test(cleanInstanceText);
const instancesSuggestions = cleanInstanceText
? instancesList
.filter((instance) => instance.includes(instanceText))
.sort((a, b) => {
// Move text that starts with instanceText to the start
const aStartsWith = a
.toLowerCase()
.startsWith(instanceText.toLowerCase());
const bStartsWith = b
.toLowerCase()
.startsWith(instanceText.toLowerCase());
if (aStartsWith && !bStartsWith) return -1;
if (!aStartsWith && bStartsWith) return 1;
return 0;
? searcher.current
?.search(cleanInstanceText, {
limit: 10,
})
.slice(0, 10)
?.map((match) => match.item)
: [];
const selectedInstanceText = instanceTextLooksLikeDomain

View file

@ -242,6 +242,17 @@ function _enhanceContent(content, opts = {}) {
}
}
// ADD ASPECT RATIO TO ALL IMAGES
if (enhancedContent.includes('<img')) {
dom.querySelectorAll('img').forEach((img) => {
const width = img.getAttribute('width') || img.naturalWidth;
const height = img.getAttribute('height') || img.naturalHeight;
if (width && height) {
img.style.setProperty('--original-aspect-ratio', `${width}/${height}`);
}
});
}
if (postEnhanceDOM) {
queueMicrotask(() => postEnhanceDOM(dom));
// postEnhanceDOM(dom); // mutate dom