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