kopia lustrzana https://github.com/cheeaun/phanpy
It's time for keyboard-layout-dependant shortcuts
Let's hope this works!pull/1124/head
rodzic
4719e2c6a4
commit
e87f7777c6
|
@ -144,13 +144,22 @@ export default memo(function BackgroundService({ isLoggedIn }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global keyboard shortcuts "service"
|
// Global keyboard shortcuts "service"
|
||||||
useHotkeys('shift+alt+k', () => {
|
useHotkeys(
|
||||||
const currentCloakMode = states.settings.cloakMode;
|
'shift+alt+k',
|
||||||
states.settings.cloakMode = !currentCloakMode;
|
(e) => {
|
||||||
showToast({
|
// Need modifers check due to useKey: true
|
||||||
text: currentCloakMode ? t`Cloak mode disabled` : t`Cloak mode enabled`,
|
if (!e.shiftKey || !e.altKey) return;
|
||||||
});
|
|
||||||
});
|
const currentCloakMode = states.settings.cloakMode;
|
||||||
|
states.settings.cloakMode = !currentCloakMode;
|
||||||
|
showToast({
|
||||||
|
text: currentCloakMode ? t`Cloak mode disabled` : t`Cloak mode enabled`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
|
@ -68,31 +68,38 @@ function Columns() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useHotkeys(['[', ']'], (e, handler) => {
|
useHotkeys(
|
||||||
const key = handler.keys[0];
|
['[', ']'],
|
||||||
const currentFocusedColumn = document.activeElement.closest('#columns > *');
|
(e, handler) => {
|
||||||
|
const key = handler.keys[0];
|
||||||
|
const currentFocusedColumn =
|
||||||
|
document.activeElement.closest('#columns > *');
|
||||||
|
|
||||||
const rtl = isRTL();
|
const rtl = isRTL();
|
||||||
const prevColKey = rtl ? ']' : '[';
|
const prevColKey = rtl ? ']' : '[';
|
||||||
const nextColKey = rtl ? '[' : ']';
|
const nextColKey = rtl ? '[' : ']';
|
||||||
let $column;
|
let $column;
|
||||||
|
|
||||||
if (key === prevColKey) {
|
if (key === prevColKey) {
|
||||||
// If [, focus on left of focused column, else first column
|
// If [, focus on left of focused column, else first column
|
||||||
$column = currentFocusedColumn
|
$column = currentFocusedColumn
|
||||||
? currentFocusedColumn.previousElementSibling
|
? currentFocusedColumn.previousElementSibling
|
||||||
: document.querySelectorAll('#columns > *')[0];
|
: document.querySelectorAll('#columns > *')[0];
|
||||||
} else if (key === nextColKey) {
|
} else if (key === nextColKey) {
|
||||||
// If ], focus on right of focused column, else 2nd column
|
// If ], focus on right of focused column, else 2nd column
|
||||||
$column = currentFocusedColumn
|
$column = currentFocusedColumn
|
||||||
? currentFocusedColumn.nextElementSibling
|
? currentFocusedColumn.nextElementSibling
|
||||||
: document.querySelectorAll('#columns > *')[1];
|
: document.querySelectorAll('#columns > *')[1];
|
||||||
}
|
}
|
||||||
if ($column) {
|
if ($column) {
|
||||||
$column.focus();
|
$column.focus();
|
||||||
$column.scrollIntoView(scrollIntoViewOptions);
|
$column.scrollIntoView(scrollIntoViewOptions);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -70,12 +70,19 @@ export default function ComposeButton() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useHotkeys('c, shift+c', handleButton, {
|
useHotkeys(
|
||||||
ignoreEventWhen: (e) => {
|
'c, shift+c',
|
||||||
const hasModal = !!document.querySelector('#modal-container > *');
|
handleButton,
|
||||||
return hasModal;
|
{
|
||||||
|
ignoreEventWhen: (e) => {
|
||||||
|
const hasModal = !!document.querySelector('#modal-container > *');
|
||||||
|
return hasModal;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Setup longpress handler to open context menu
|
// Setup longpress handler to open context menu
|
||||||
const bindLongPress = useLongPress(
|
const bindLongPress = useLongPress(
|
||||||
|
|
|
@ -19,12 +19,13 @@ export default memo(function KeyboardShortcutsHelp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'?, shift+?, shift+slash',
|
'?',
|
||||||
(e) => {
|
() => {
|
||||||
console.log('help');
|
console.log('help');
|
||||||
states.showKeyboardShortcutsHelp = true;
|
states.showKeyboardShortcutsHelp = true;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
useKey: true,
|
||||||
ignoreEventWhen: (e) => {
|
ignoreEventWhen: (e) => {
|
||||||
const isCatchUpPage = /\/catchup/i.test(location.hash);
|
const isCatchUpPage = /\/catchup/i.test(location.hash);
|
||||||
return isCatchUpPage;
|
return isCatchUpPage;
|
||||||
|
|
|
@ -20,6 +20,7 @@ export default memo(function SearchCommand({ onClose = () => {} }) {
|
||||||
}, 0);
|
}, 0);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
useKey: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
ignoreEventWhen: (e) => {
|
ignoreEventWhen: (e) => {
|
||||||
const isSearchPage = /\/search/.test(location.hash);
|
const isSearchPage = /\/search/.test(location.hash);
|
||||||
|
|
|
@ -1482,16 +1482,22 @@ function Status({
|
||||||
const hotkeysEnabled = !readOnly && !previewMode && !quoted;
|
const hotkeysEnabled = !readOnly && !previewMode && !quoted;
|
||||||
const rRef = useHotkeys('r, shift+r', replyStatus, {
|
const rRef = useHotkeys('r, shift+r', replyStatus, {
|
||||||
enabled: hotkeysEnabled,
|
enabled: hotkeysEnabled,
|
||||||
|
useKey: true,
|
||||||
});
|
});
|
||||||
const fRef = useHotkeys('f, l', favouriteStatusNotify, {
|
const fRef = useHotkeys('f, l', favouriteStatusNotify, {
|
||||||
enabled: hotkeysEnabled,
|
enabled: hotkeysEnabled,
|
||||||
|
useKey: true,
|
||||||
});
|
});
|
||||||
const dRef = useHotkeys('d', bookmarkStatusNotify, {
|
const dRef = useHotkeys('d', bookmarkStatusNotify, {
|
||||||
enabled: hotkeysEnabled,
|
enabled: hotkeysEnabled,
|
||||||
|
useKey: true,
|
||||||
});
|
});
|
||||||
const bRef = useHotkeys(
|
const bRef = useHotkeys(
|
||||||
'shift+b',
|
'shift+b',
|
||||||
() => {
|
(e) => {
|
||||||
|
// Need shiftKey check due to useKey: true
|
||||||
|
if (!e.shiftKey) return;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const done = await confirmBoostStatus();
|
const done = await confirmBoostStatus();
|
||||||
|
@ -1507,30 +1513,37 @@ function Status({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: hotkeysEnabled && canBoost,
|
enabled: hotkeysEnabled && canBoost,
|
||||||
|
useKey: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const xRef = useHotkeys('x', (e) => {
|
const xRef = useHotkeys(
|
||||||
const activeStatus = document.activeElement.closest(
|
'x',
|
||||||
'.status-link, .status-focus',
|
(e) => {
|
||||||
);
|
const activeStatus = document.activeElement.closest(
|
||||||
if (activeStatus) {
|
'.status-link, .status-focus',
|
||||||
const spoilerButton = activeStatus.querySelector(
|
|
||||||
'.spoiler-button:not(.spoiling)',
|
|
||||||
);
|
);
|
||||||
if (spoilerButton) {
|
if (activeStatus) {
|
||||||
e.stopPropagation();
|
const spoilerButton = activeStatus.querySelector(
|
||||||
spoilerButton.click();
|
'.spoiler-button:not(.spoiling)',
|
||||||
} else {
|
|
||||||
const spoilerMediaButton = activeStatus.querySelector(
|
|
||||||
'.spoiler-media-button:not(.spoiling)',
|
|
||||||
);
|
);
|
||||||
if (spoilerMediaButton) {
|
if (spoilerButton) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
spoilerMediaButton.click();
|
spoilerButton.click();
|
||||||
|
} else {
|
||||||
|
const spoilerMediaButton = activeStatus.querySelector(
|
||||||
|
'.spoiler-media-button:not(.spoiling)',
|
||||||
|
);
|
||||||
|
if (spoilerMediaButton) {
|
||||||
|
e.stopPropagation();
|
||||||
|
spoilerMediaButton.click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const displayedMediaAttachments = mediaAttachments.slice(
|
const displayedMediaAttachments = mediaAttachments.slice(
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -143,91 +143,115 @@ function Timeline({
|
||||||
|
|
||||||
const itemsSelector = '.timeline-item, .timeline-item-alt';
|
const itemsSelector = '.timeline-item, .timeline-item-alt';
|
||||||
|
|
||||||
const jRef = useHotkeys('j, shift+j', (_, handler) => {
|
const jRef = useHotkeys(
|
||||||
// focus on next status after active item
|
'j, shift+j',
|
||||||
const activeItem = document.activeElement.closest(itemsSelector);
|
(e, handler) => {
|
||||||
const activeItemRect = activeItem?.getBoundingClientRect();
|
// Fix bug: shift+j is fired even when j is pressed due to useKey: true
|
||||||
const allItems = Array.from(
|
if (e.shiftKey !== handler.shift) return;
|
||||||
scrollableRef.current.querySelectorAll(itemsSelector),
|
|
||||||
).filter((item) => !!item.offsetHeight);
|
|
||||||
if (
|
|
||||||
activeItem &&
|
|
||||||
activeItemRect.top < scrollableRef.current.clientHeight &&
|
|
||||||
activeItemRect.bottom > 0
|
|
||||||
) {
|
|
||||||
const activeItemIndex = allItems.indexOf(activeItem);
|
|
||||||
let nextItem = allItems[activeItemIndex + 1];
|
|
||||||
if (handler.shift) {
|
|
||||||
// get next status that's not .timeline-item-alt
|
|
||||||
nextItem = allItems.find(
|
|
||||||
(item, index) =>
|
|
||||||
index > activeItemIndex &&
|
|
||||||
!item.classList.contains('timeline-item-alt'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (nextItem) {
|
|
||||||
nextItem.focus();
|
|
||||||
nextItem.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If active status is not in viewport, get the topmost status-link in viewport
|
|
||||||
const topmostItem = allItems.find((item) => {
|
|
||||||
const itemRect = item.getBoundingClientRect();
|
|
||||||
return itemRect.top >= 44 && itemRect.left >= 0; // 44 is the magic number for header height, not real
|
|
||||||
});
|
|
||||||
if (topmostItem) {
|
|
||||||
topmostItem.focus();
|
|
||||||
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const kRef = useHotkeys('k, shift+k', (_, handler) => {
|
// focus on next status after active item
|
||||||
// focus on previous status after active item
|
const activeItem = document.activeElement.closest(itemsSelector);
|
||||||
const activeItem = document.activeElement.closest(itemsSelector);
|
const activeItemRect = activeItem?.getBoundingClientRect();
|
||||||
const activeItemRect = activeItem?.getBoundingClientRect();
|
const allItems = Array.from(
|
||||||
const allItems = Array.from(
|
scrollableRef.current.querySelectorAll(itemsSelector),
|
||||||
scrollableRef.current.querySelectorAll(itemsSelector),
|
).filter((item) => !!item.offsetHeight);
|
||||||
).filter((item) => !!item.offsetHeight);
|
if (
|
||||||
if (
|
activeItem &&
|
||||||
activeItem &&
|
activeItemRect.top < scrollableRef.current.clientHeight &&
|
||||||
activeItemRect.top < scrollableRef.current.clientHeight &&
|
activeItemRect.bottom > 0
|
||||||
activeItemRect.bottom > 0
|
) {
|
||||||
) {
|
const activeItemIndex = allItems.indexOf(activeItem);
|
||||||
const activeItemIndex = allItems.indexOf(activeItem);
|
let nextItem = allItems[activeItemIndex + 1];
|
||||||
let prevItem = allItems[activeItemIndex - 1];
|
if (handler.shift) {
|
||||||
if (handler.shift) {
|
// get next status that's not .timeline-item-alt
|
||||||
// get prev status that's not .timeline-item-alt
|
nextItem = allItems.find(
|
||||||
prevItem = allItems.findLast(
|
(item, index) =>
|
||||||
(item, index) =>
|
index > activeItemIndex &&
|
||||||
index < activeItemIndex &&
|
!item.classList.contains('timeline-item-alt'),
|
||||||
!item.classList.contains('timeline-item-alt'),
|
);
|
||||||
);
|
}
|
||||||
|
if (nextItem) {
|
||||||
|
nextItem.focus();
|
||||||
|
nextItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If active status is not in viewport, get the topmost status-link in viewport
|
||||||
|
const topmostItem = allItems.find((item) => {
|
||||||
|
const itemRect = item.getBoundingClientRect();
|
||||||
|
return itemRect.top >= 44 && itemRect.left >= 0; // 44 is the magic number for header height, not real
|
||||||
|
});
|
||||||
|
if (topmostItem) {
|
||||||
|
topmostItem.focus();
|
||||||
|
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (prevItem) {
|
},
|
||||||
prevItem.focus();
|
{
|
||||||
prevItem.scrollIntoView(scrollIntoViewOptions);
|
useKey: true,
|
||||||
}
|
},
|
||||||
} else {
|
);
|
||||||
// If active status is not in viewport, get the topmost status-link in viewport
|
|
||||||
const topmostItem = allItems.find((item) => {
|
|
||||||
const itemRect = item.getBoundingClientRect();
|
|
||||||
return itemRect.top >= 44 && itemRect.left >= 0; // 44 is the magic number for header height, not real
|
|
||||||
});
|
|
||||||
if (topmostItem) {
|
|
||||||
topmostItem.focus();
|
|
||||||
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const oRef = useHotkeys(['enter', 'o'], () => {
|
const kRef = useHotkeys(
|
||||||
// open active status
|
'k, shift+k',
|
||||||
const activeItem = document.activeElement;
|
(e, handler) => {
|
||||||
if (activeItem?.matches(itemsSelector)) {
|
// Fix bug: shift+k is fired even when k is pressed due to useKey: true
|
||||||
activeItem.click();
|
if (e.shiftKey !== handler.shift) return;
|
||||||
}
|
|
||||||
});
|
// focus on previous status after active item
|
||||||
|
const activeItem = document.activeElement.closest(itemsSelector);
|
||||||
|
const activeItemRect = activeItem?.getBoundingClientRect();
|
||||||
|
const allItems = Array.from(
|
||||||
|
scrollableRef.current.querySelectorAll(itemsSelector),
|
||||||
|
).filter((item) => !!item.offsetHeight);
|
||||||
|
if (
|
||||||
|
activeItem &&
|
||||||
|
activeItemRect.top < scrollableRef.current.clientHeight &&
|
||||||
|
activeItemRect.bottom > 0
|
||||||
|
) {
|
||||||
|
const activeItemIndex = allItems.indexOf(activeItem);
|
||||||
|
let prevItem = allItems[activeItemIndex - 1];
|
||||||
|
if (handler.shift) {
|
||||||
|
// get prev status that's not .timeline-item-alt
|
||||||
|
prevItem = allItems.findLast(
|
||||||
|
(item, index) =>
|
||||||
|
index < activeItemIndex &&
|
||||||
|
!item.classList.contains('timeline-item-alt'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (prevItem) {
|
||||||
|
prevItem.focus();
|
||||||
|
prevItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If active status is not in viewport, get the topmost status-link in viewport
|
||||||
|
const topmostItem = allItems.find((item) => {
|
||||||
|
const itemRect = item.getBoundingClientRect();
|
||||||
|
return itemRect.top >= 44 && itemRect.left >= 0; // 44 is the magic number for header height, not real
|
||||||
|
});
|
||||||
|
if (topmostItem) {
|
||||||
|
topmostItem.focus();
|
||||||
|
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const oRef = useHotkeys(
|
||||||
|
['enter', 'o'],
|
||||||
|
() => {
|
||||||
|
// open active status
|
||||||
|
const activeItem = document.activeElement;
|
||||||
|
if (activeItem?.matches(itemsSelector)) {
|
||||||
|
activeItem.click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const showNewPostsIndicator =
|
const showNewPostsIndicator =
|
||||||
items.length > 0 && uiState !== 'loading' && showNew;
|
items.length > 0 && uiState !== 'loading' && showNew;
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -716,6 +716,7 @@ function Catchup() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
useKey: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
ignoreModifiers: true,
|
ignoreModifiers: true,
|
||||||
},
|
},
|
||||||
|
@ -760,6 +761,7 @@ function Catchup() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
useKey: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
ignoreModifiers: true,
|
ignoreModifiers: true,
|
||||||
},
|
},
|
||||||
|
@ -789,6 +791,7 @@ function Catchup() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
useKey: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
ignoreModifiers: true,
|
ignoreModifiers: true,
|
||||||
enableOnFormTags: ['input'],
|
enableOnFormTags: ['input'],
|
||||||
|
@ -817,6 +820,7 @@ function Catchup() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
useKey: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
ignoreModifiers: true,
|
ignoreModifiers: true,
|
||||||
enableOnFormTags: ['input'],
|
enableOnFormTags: ['input'],
|
||||||
|
|
|
@ -451,72 +451,90 @@ function Notifications({ columnMode }) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const itemsSelector = '.notification';
|
const itemsSelector = '.notification';
|
||||||
const jRef = useHotkeys('j', () => {
|
const jRef = useHotkeys(
|
||||||
const activeItem = document.activeElement.closest(itemsSelector);
|
'j',
|
||||||
const activeItemRect = activeItem?.getBoundingClientRect();
|
() => {
|
||||||
const allItems = Array.from(
|
const activeItem = document.activeElement.closest(itemsSelector);
|
||||||
scrollableRef.current.querySelectorAll(itemsSelector),
|
const activeItemRect = activeItem?.getBoundingClientRect();
|
||||||
);
|
const allItems = Array.from(
|
||||||
if (
|
scrollableRef.current.querySelectorAll(itemsSelector),
|
||||||
activeItem &&
|
);
|
||||||
activeItemRect.top < scrollableRef.current.clientHeight &&
|
if (
|
||||||
activeItemRect.bottom > 0
|
activeItem &&
|
||||||
) {
|
activeItemRect.top < scrollableRef.current.clientHeight &&
|
||||||
const activeItemIndex = allItems.indexOf(activeItem);
|
activeItemRect.bottom > 0
|
||||||
let nextItem = allItems[activeItemIndex + 1];
|
) {
|
||||||
if (nextItem) {
|
const activeItemIndex = allItems.indexOf(activeItem);
|
||||||
nextItem.focus();
|
let nextItem = allItems[activeItemIndex + 1];
|
||||||
nextItem.scrollIntoView(scrollIntoViewOptions);
|
if (nextItem) {
|
||||||
|
nextItem.focus();
|
||||||
|
nextItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const topmostItem = allItems.find((item) => {
|
||||||
|
const itemRect = item.getBoundingClientRect();
|
||||||
|
return itemRect.top >= 44 && itemRect.left >= 0;
|
||||||
|
});
|
||||||
|
if (topmostItem) {
|
||||||
|
topmostItem.focus();
|
||||||
|
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
const topmostItem = allItems.find((item) => {
|
{
|
||||||
const itemRect = item.getBoundingClientRect();
|
useKey: true,
|
||||||
return itemRect.top >= 44 && itemRect.left >= 0;
|
},
|
||||||
});
|
);
|
||||||
if (topmostItem) {
|
|
||||||
topmostItem.focus();
|
|
||||||
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const kRef = useHotkeys('k', () => {
|
const kRef = useHotkeys(
|
||||||
// focus on previous status after active item
|
'k',
|
||||||
const activeItem = document.activeElement.closest(itemsSelector);
|
() => {
|
||||||
const activeItemRect = activeItem?.getBoundingClientRect();
|
// focus on previous status after active item
|
||||||
const allItems = Array.from(
|
const activeItem = document.activeElement.closest(itemsSelector);
|
||||||
scrollableRef.current.querySelectorAll(itemsSelector),
|
const activeItemRect = activeItem?.getBoundingClientRect();
|
||||||
);
|
const allItems = Array.from(
|
||||||
if (
|
scrollableRef.current.querySelectorAll(itemsSelector),
|
||||||
activeItem &&
|
);
|
||||||
activeItemRect.top < scrollableRef.current.clientHeight &&
|
if (
|
||||||
activeItemRect.bottom > 0
|
activeItem &&
|
||||||
) {
|
activeItemRect.top < scrollableRef.current.clientHeight &&
|
||||||
const activeItemIndex = allItems.indexOf(activeItem);
|
activeItemRect.bottom > 0
|
||||||
let prevItem = allItems[activeItemIndex - 1];
|
) {
|
||||||
if (prevItem) {
|
const activeItemIndex = allItems.indexOf(activeItem);
|
||||||
prevItem.focus();
|
let prevItem = allItems[activeItemIndex - 1];
|
||||||
prevItem.scrollIntoView(scrollIntoViewOptions);
|
if (prevItem) {
|
||||||
|
prevItem.focus();
|
||||||
|
prevItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const topmostItem = allItems.find((item) => {
|
||||||
|
const itemRect = item.getBoundingClientRect();
|
||||||
|
return itemRect.top >= 44 && itemRect.left >= 0;
|
||||||
|
});
|
||||||
|
if (topmostItem) {
|
||||||
|
topmostItem.focus();
|
||||||
|
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
const topmostItem = allItems.find((item) => {
|
{
|
||||||
const itemRect = item.getBoundingClientRect();
|
useKey: true,
|
||||||
return itemRect.top >= 44 && itemRect.left >= 0;
|
},
|
||||||
});
|
);
|
||||||
if (topmostItem) {
|
|
||||||
topmostItem.focus();
|
|
||||||
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const oRef = useHotkeys(['enter', 'o'], () => {
|
const oRef = useHotkeys(
|
||||||
const activeItem = document.activeElement.closest(itemsSelector);
|
['enter', 'o'],
|
||||||
const statusLink = activeItem?.querySelector('.status-link');
|
() => {
|
||||||
if (statusLink) {
|
const activeItem = document.activeElement.closest(itemsSelector);
|
||||||
statusLink.click();
|
const statusLink = activeItem?.querySelector('.status-link');
|
||||||
}
|
if (statusLink) {
|
||||||
});
|
statusLink.click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const todaySubHeading = useMemo(() => {
|
const todaySubHeading = useMemo(() => {
|
||||||
|
|
|
@ -198,75 +198,88 @@ function Search({ columnMode, ...props }) {
|
||||||
}, [q, type, instance]);
|
}, [q, type, instance]);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
['/', 'Slash'],
|
['Slash', '/'],
|
||||||
(e) => {
|
(e) => {
|
||||||
searchFormRef.current?.focus?.();
|
searchFormRef.current?.focus?.();
|
||||||
searchFormRef.current?.select?.();
|
searchFormRef.current?.select?.();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
useKey: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const itemsSelector = '.timeline > li > a, .hashtag-list > li > a';
|
const itemsSelector = '.timeline > li > a, .hashtag-list > li > a';
|
||||||
const jRef = useHotkeys('j', () => {
|
const jRef = useHotkeys(
|
||||||
const activeItem = document.activeElement.closest(itemsSelector);
|
'j',
|
||||||
const activeItemRect = activeItem?.getBoundingClientRect();
|
() => {
|
||||||
const allItems = Array.from(
|
const activeItem = document.activeElement.closest(itemsSelector);
|
||||||
scrollableRef.current.querySelectorAll(itemsSelector),
|
const activeItemRect = activeItem?.getBoundingClientRect();
|
||||||
);
|
const allItems = Array.from(
|
||||||
if (
|
scrollableRef.current.querySelectorAll(itemsSelector),
|
||||||
activeItem &&
|
);
|
||||||
activeItemRect.top < scrollableRef.current.clientHeight &&
|
if (
|
||||||
activeItemRect.bottom > 0
|
activeItem &&
|
||||||
) {
|
activeItemRect.top < scrollableRef.current.clientHeight &&
|
||||||
const activeItemIndex = allItems.indexOf(activeItem);
|
activeItemRect.bottom > 0
|
||||||
let nextItem = allItems[activeItemIndex + 1];
|
) {
|
||||||
if (nextItem) {
|
const activeItemIndex = allItems.indexOf(activeItem);
|
||||||
nextItem.focus();
|
let nextItem = allItems[activeItemIndex + 1];
|
||||||
nextItem.scrollIntoView(scrollIntoViewOptions);
|
if (nextItem) {
|
||||||
|
nextItem.focus();
|
||||||
|
nextItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const topmostItem = allItems.find((item) => {
|
||||||
|
const itemRect = item.getBoundingClientRect();
|
||||||
|
return itemRect.top >= 44 && itemRect.left >= 0;
|
||||||
|
});
|
||||||
|
if (topmostItem) {
|
||||||
|
topmostItem.focus();
|
||||||
|
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
const topmostItem = allItems.find((item) => {
|
{
|
||||||
const itemRect = item.getBoundingClientRect();
|
useKey: true,
|
||||||
return itemRect.top >= 44 && itemRect.left >= 0;
|
},
|
||||||
});
|
);
|
||||||
if (topmostItem) {
|
|
||||||
topmostItem.focus();
|
|
||||||
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const kRef = useHotkeys('k', () => {
|
const kRef = useHotkeys(
|
||||||
// focus on previous status after active item
|
'k',
|
||||||
const activeItem = document.activeElement.closest(itemsSelector);
|
() => {
|
||||||
const activeItemRect = activeItem?.getBoundingClientRect();
|
// focus on previous status after active item
|
||||||
const allItems = Array.from(
|
const activeItem = document.activeElement.closest(itemsSelector);
|
||||||
scrollableRef.current.querySelectorAll(itemsSelector),
|
const activeItemRect = activeItem?.getBoundingClientRect();
|
||||||
);
|
const allItems = Array.from(
|
||||||
if (
|
scrollableRef.current.querySelectorAll(itemsSelector),
|
||||||
activeItem &&
|
);
|
||||||
activeItemRect.top < scrollableRef.current.clientHeight &&
|
if (
|
||||||
activeItemRect.bottom > 0
|
activeItem &&
|
||||||
) {
|
activeItemRect.top < scrollableRef.current.clientHeight &&
|
||||||
const activeItemIndex = allItems.indexOf(activeItem);
|
activeItemRect.bottom > 0
|
||||||
let prevItem = allItems[activeItemIndex - 1];
|
) {
|
||||||
if (prevItem) {
|
const activeItemIndex = allItems.indexOf(activeItem);
|
||||||
prevItem.focus();
|
let prevItem = allItems[activeItemIndex - 1];
|
||||||
prevItem.scrollIntoView(scrollIntoViewOptions);
|
if (prevItem) {
|
||||||
|
prevItem.focus();
|
||||||
|
prevItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const topmostItem = allItems.find((item) => {
|
||||||
|
const itemRect = item.getBoundingClientRect();
|
||||||
|
return itemRect.top >= 44 && itemRect.left >= 0;
|
||||||
|
});
|
||||||
|
if (topmostItem) {
|
||||||
|
topmostItem.focus();
|
||||||
|
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
const topmostItem = allItems.find((item) => {
|
{
|
||||||
const itemRect = item.getBoundingClientRect();
|
useKey: true,
|
||||||
return itemRect.top >= 44 && itemRect.left >= 0;
|
},
|
||||||
});
|
);
|
||||||
if (topmostItem) {
|
|
||||||
topmostItem.focus();
|
|
||||||
topmostItem.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const [filterBarParent] = useAutoAnimate();
|
const [filterBarParent] = useAutoAnimate();
|
||||||
|
|
||||||
|
|
|
@ -651,84 +651,102 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
|
||||||
location.hash = closeLink;
|
location.hash = closeLink;
|
||||||
});
|
});
|
||||||
|
|
||||||
useHotkeys('j', () => {
|
useHotkeys(
|
||||||
const activeStatus = document.activeElement.closest(
|
'j',
|
||||||
'.status-link, .status-focus',
|
() => {
|
||||||
);
|
const activeStatus = document.activeElement.closest(
|
||||||
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
'.status-link, .status-focus',
|
||||||
const allStatusLinks = Array.from(
|
);
|
||||||
scrollableRef.current.querySelectorAll(STATUSES_SELECTOR),
|
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
||||||
);
|
const allStatusLinks = Array.from(
|
||||||
console.log({ allStatusLinks });
|
scrollableRef.current.querySelectorAll(STATUSES_SELECTOR),
|
||||||
if (
|
);
|
||||||
activeStatus &&
|
console.log({ allStatusLinks });
|
||||||
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
if (
|
||||||
activeStatusRect.bottom > 0
|
activeStatus &&
|
||||||
) {
|
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
||||||
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
activeStatusRect.bottom > 0
|
||||||
let nextStatus = allStatusLinks[activeStatusIndex + 1];
|
) {
|
||||||
if (nextStatus) {
|
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
||||||
nextStatus.focus();
|
let nextStatus = allStatusLinks[activeStatusIndex + 1];
|
||||||
nextStatus.scrollIntoView(scrollIntoViewOptions);
|
if (nextStatus) {
|
||||||
|
nextStatus.focus();
|
||||||
|
nextStatus.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If active status is not in viewport, get the topmost status-link in viewport
|
||||||
|
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
||||||
|
const statusLinkRect = statusLink.getBoundingClientRect();
|
||||||
|
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
||||||
|
});
|
||||||
|
if (topmostStatusLink) {
|
||||||
|
topmostStatusLink.focus();
|
||||||
|
topmostStatusLink.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
// If active status is not in viewport, get the topmost status-link in viewport
|
{
|
||||||
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
useKey: true,
|
||||||
const statusLinkRect = statusLink.getBoundingClientRect();
|
},
|
||||||
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
);
|
||||||
});
|
|
||||||
if (topmostStatusLink) {
|
|
||||||
topmostStatusLink.focus();
|
|
||||||
topmostStatusLink.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
useHotkeys('k', () => {
|
useHotkeys(
|
||||||
const activeStatus = document.activeElement.closest(
|
'k',
|
||||||
'.status-link, .status-focus',
|
() => {
|
||||||
);
|
const activeStatus = document.activeElement.closest(
|
||||||
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
'.status-link, .status-focus',
|
||||||
const allStatusLinks = Array.from(
|
);
|
||||||
scrollableRef.current.querySelectorAll(STATUSES_SELECTOR),
|
const activeStatusRect = activeStatus?.getBoundingClientRect();
|
||||||
);
|
const allStatusLinks = Array.from(
|
||||||
if (
|
scrollableRef.current.querySelectorAll(STATUSES_SELECTOR),
|
||||||
activeStatus &&
|
);
|
||||||
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
if (
|
||||||
activeStatusRect.bottom > 0
|
activeStatus &&
|
||||||
) {
|
activeStatusRect.top < scrollableRef.current.clientHeight &&
|
||||||
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
activeStatusRect.bottom > 0
|
||||||
let prevStatus = allStatusLinks[activeStatusIndex - 1];
|
) {
|
||||||
if (prevStatus) {
|
const activeStatusIndex = allStatusLinks.indexOf(activeStatus);
|
||||||
prevStatus.focus();
|
let prevStatus = allStatusLinks[activeStatusIndex - 1];
|
||||||
prevStatus.scrollIntoView(scrollIntoViewOptions);
|
if (prevStatus) {
|
||||||
|
prevStatus.focus();
|
||||||
|
prevStatus.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If active status is not in viewport, get the topmost status-link in viewport
|
||||||
|
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
||||||
|
const statusLinkRect = statusLink.getBoundingClientRect();
|
||||||
|
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
||||||
|
});
|
||||||
|
if (topmostStatusLink) {
|
||||||
|
topmostStatusLink.focus();
|
||||||
|
topmostStatusLink.scrollIntoView(scrollIntoViewOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
// If active status is not in viewport, get the topmost status-link in viewport
|
{
|
||||||
const topmostStatusLink = allStatusLinks.find((statusLink) => {
|
useKey: true,
|
||||||
const statusLinkRect = statusLink.getBoundingClientRect();
|
},
|
||||||
return statusLinkRect.top >= 44 && statusLinkRect.left >= 0; // 44 is the magic number for header height, not real
|
);
|
||||||
});
|
|
||||||
if (topmostStatusLink) {
|
|
||||||
topmostStatusLink.focus();
|
|
||||||
topmostStatusLink.scrollIntoView(scrollIntoViewOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// NOTE: I'm not sure if 'x' is the best shortcut for this, might change it later
|
// NOTE: I'm not sure if 'x' is the best shortcut for this, might change it later
|
||||||
// IDEA: x is for expand
|
// IDEA: x is for expand
|
||||||
useHotkeys('x', () => {
|
useHotkeys(
|
||||||
const activeStatus = document.activeElement.closest(
|
'x',
|
||||||
'.status-link, .status-focus',
|
() => {
|
||||||
);
|
const activeStatus = document.activeElement.closest(
|
||||||
if (activeStatus) {
|
'.status-link, .status-focus',
|
||||||
const details = activeStatus.nextElementSibling;
|
);
|
||||||
if (details && details.tagName.toLowerCase() === 'details') {
|
if (activeStatus) {
|
||||||
details.open = !details.open;
|
const details = activeStatus.nextElementSibling;
|
||||||
|
if (details && details.tagName.toLowerCase() === 'details') {
|
||||||
|
details.open = !details.open;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
{
|
||||||
|
useKey: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const [reachTopPost, setReachTopPost] = useState(false);
|
const [reachTopPost, setReachTopPost] = useState(false);
|
||||||
// const { nearReachStart } = useScroll({
|
// const { nearReachStart } = useScroll({
|
||||||
|
|
Ładowanie…
Reference in New Issue