Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69b6055353 | ||
|
|
1bdd0449e0 | ||
|
|
a6fdf9010b | ||
|
|
941dae0625 | ||
|
|
4a715bfd17 | ||
|
|
0b70c7e490 | ||
|
|
0539836714 | ||
|
|
c08b0e654b | ||
|
|
b3cb48319a | ||
|
|
44553cc375 | ||
|
|
fbe287a702 | ||
|
|
5863dcdf67 | ||
|
|
f77bee25ef | ||
|
|
c11328a064 | ||
|
|
d04de2fba0 |
@@ -1,5 +1,10 @@
|
|||||||
# Cinny
|
# Cinny
|
||||||
|
|
||||||
|
[](https://github.com/ajbura/cinny/tree/dev)
|
||||||
|
[](https://matrix.to/#/#cinny:matrix.org)
|
||||||
|
[](https://twitter.com/@cinnyapp)
|
||||||
|
[](https://opencollective.com/cinny)
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [About](#about)
|
- [About](#about)
|
||||||
|
|||||||
@@ -7,5 +7,6 @@
|
|||||||
"kde.org",
|
"kde.org",
|
||||||
"matrix.org",
|
"matrix.org",
|
||||||
"chat.mozilla.org"
|
"chat.mozilla.org"
|
||||||
]
|
],
|
||||||
|
"allowCustomHomeservers": true
|
||||||
}
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "2.0.0",
|
"version": "2.0.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "2.0.0",
|
"version": "2.0.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/inter": "^4.5.10",
|
"@fontsource/inter": "^4.5.10",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cinny",
|
"name": "cinny",
|
||||||
"version": "2.0.0",
|
"version": "2.0.3",
|
||||||
"description": "Yet another matrix client",
|
"description": "Yet another matrix client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -123,17 +123,26 @@ const MessageReplyWrapper = React.memo(({ roomTimeline, eventId }) => {
|
|||||||
const eTimeline = await mx.getEventTimeline(timelineSet, eventId);
|
const eTimeline = await mx.getEventTimeline(timelineSet, eventId);
|
||||||
await roomTimeline.decryptAllEventsOfTimeline(eTimeline);
|
await roomTimeline.decryptAllEventsOfTimeline(eTimeline);
|
||||||
|
|
||||||
const mEvent = eTimeline.getTimelineSet().findEventById(eventId);
|
let mEvent = eTimeline.getTimelineSet().findEventById(eventId);
|
||||||
|
const editedList = roomTimeline.editedTimeline.get(mEvent.getId());
|
||||||
|
if (editedList) {
|
||||||
|
mEvent = editedList[editedList.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
const rawBody = mEvent.getContent().body;
|
const rawBody = mEvent.getContent().body;
|
||||||
const username = getUsernameOfRoomMember(mEvent.sender);
|
const username = getUsernameOfRoomMember(mEvent.sender);
|
||||||
|
|
||||||
if (isMountedRef.current === false) return;
|
if (isMountedRef.current === false) return;
|
||||||
const fallbackBody = mEvent.isRedacted() ? '*** This message has been deleted ***' : '*** Unable to load reply ***';
|
const fallbackBody = mEvent.isRedacted() ? '*** This message has been deleted ***' : '*** Unable to load reply ***';
|
||||||
|
let parsedBody = parseReply(rawBody)?.body ?? rawBody ?? fallbackBody;
|
||||||
|
if (editedList && parsedBody.startsWith(' * ')) {
|
||||||
|
parsedBody = parsedBody.slice(3);
|
||||||
|
}
|
||||||
|
|
||||||
setReply({
|
setReply({
|
||||||
to: username,
|
to: username,
|
||||||
color: colorMXID(mEvent.getSender()),
|
color: colorMXID(mEvent.getSender()),
|
||||||
body: parseReply(rawBody)?.body ?? rawBody ?? fallbackBody,
|
body: parsedBody,
|
||||||
event: mEvent,
|
event: mEvent,
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ function RoomVisibility({ roomId }) {
|
|||||||
|
|
||||||
const noSpaceParent = currentState.getStateEvents('m.space.parent').length === 0;
|
const noSpaceParent = currentState.getStateEvents('m.space.parent').length === 0;
|
||||||
const mCreate = currentState.getStateEvents('m.room.create')[0]?.getContent();
|
const mCreate = currentState.getStateEvents('m.room.create')[0]?.getContent();
|
||||||
const roomVersion = Number(mCreate.room_version);
|
const roomVersion = Number(mCreate?.room_version ?? 0);
|
||||||
|
|
||||||
const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel || 0;
|
const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel || 0;
|
||||||
const canChange = room.currentState.hasSufficientPowerLevelFor('state_default', myPowerlevel);
|
const canChange = room.currentState.hasSufficientPowerLevelFor('state_default', myPowerlevel);
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ function JoinAliasContent({ term, requestClose }) {
|
|||||||
if (alias.startsWith('#')) {
|
if (alias.startsWith('#')) {
|
||||||
try {
|
try {
|
||||||
const aliasData = await mx.resolveRoomAlias(alias);
|
const aliasData = await mx.resolveRoomAlias(alias);
|
||||||
via = aliasData?.servers || [];
|
via = aliasData?.servers.slice(0, 3) || [];
|
||||||
if (mountStore.getItem()) {
|
if (mountStore.getItem()) {
|
||||||
setProcess(`Joining ${alias}...`);
|
setProcess(`Joining ${alias}...`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,12 +93,13 @@ function Homeserver({ onChange }) {
|
|||||||
const result = await (await fetch(configFileUrl, { method: 'GET' })).json();
|
const result = await (await fetch(configFileUrl, { method: 'GET' })).json();
|
||||||
const selectedHs = result?.defaultHomeserver;
|
const selectedHs = result?.defaultHomeserver;
|
||||||
const hsList = result?.homeserverList;
|
const hsList = result?.homeserverList;
|
||||||
|
const allowCustom = result?.allowCustomHomeservers ?? true;
|
||||||
if (!hsList?.length > 0 || selectedHs < 0 || selectedHs >= hsList?.length) {
|
if (!hsList?.length > 0 || selectedHs < 0 || selectedHs >= hsList?.length) {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
setHs({ selected: hsList[selectedHs], list: hsList });
|
setHs({ selected: hsList[selectedHs], list: hsList, allowCustom: allowCustom });
|
||||||
} catch {
|
} catch {
|
||||||
setHs({ selected: 'matrix.org', list: ['matrix.org'] });
|
setHs({ selected: 'matrix.org', list: ['matrix.org'], allowCustom: true });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -106,14 +107,15 @@ function Homeserver({ onChange }) {
|
|||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
setProcess({ isLoading: false });
|
setProcess({ isLoading: false });
|
||||||
debounce._(async () => {
|
debounce._(async () => {
|
||||||
setHs({ selected: value.trim(), list: hs.list });
|
setHs({ ...hs, selected: value.trim() });
|
||||||
}, 700)();
|
}, 700)();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="homeserver-form">
|
<div className="homeserver-form">
|
||||||
<Input name="homeserver" onChange={handleHsInput} value={hs?.selected} forwardRef={hsRef} label="Homeserver" />
|
<Input name="homeserver" onChange={handleHsInput} value={hs?.selected} forwardRef={hsRef} label="Homeserver"
|
||||||
|
disabled={hs === null || !hs.allowCustom} />
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
placement="right"
|
placement="right"
|
||||||
content={(hideMenu) => (
|
content={(hideMenu) => (
|
||||||
@@ -126,7 +128,7 @@ function Homeserver({ onChange }) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
hideMenu();
|
hideMenu();
|
||||||
hsRef.current.value = hsName;
|
hsRef.current.value = hsName;
|
||||||
setHs({ selected: hsName, list: hs.list });
|
setHs({ ...hs, selected: hsName });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{hsName}
|
{hsName}
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ async function leave(roomId) {
|
|||||||
const isDM = initMatrix.roomList.directs.has(roomId);
|
const isDM = initMatrix.roomList.directs.has(roomId);
|
||||||
try {
|
try {
|
||||||
await mx.leave(roomId);
|
await mx.leave(roomId);
|
||||||
await mx.forget(roomId);
|
|
||||||
appDispatcher.dispatch({
|
appDispatcher.dispatch({
|
||||||
type: cons.actions.room.LEAVE,
|
type: cons.actions.room.LEAVE,
|
||||||
roomId,
|
roomId,
|
||||||
|
|||||||
@@ -2,25 +2,60 @@ import { openSearch, toggleRoomSettings } from '../action/navigation';
|
|||||||
import navigation from '../state/navigation';
|
import navigation from '../state/navigation';
|
||||||
import { markAsRead } from '../action/notifications';
|
import { markAsRead } from '../action/notifications';
|
||||||
|
|
||||||
|
function shouldFocusMessageField(code) {
|
||||||
|
// do not focus on F keys
|
||||||
|
if (/^F\d+$/.test(code)) return false;
|
||||||
|
|
||||||
|
// do not focus on numlock/scroll lock
|
||||||
|
if (
|
||||||
|
code.metaKey
|
||||||
|
|| code.startsWith('OS')
|
||||||
|
|| code.startsWith('Meta')
|
||||||
|
|| code.startsWith('Shift')
|
||||||
|
|| code.startsWith('Alt')
|
||||||
|
|| code.startsWith('Control')
|
||||||
|
|| code.startsWith('Arrow')
|
||||||
|
|| code === 'Tab'
|
||||||
|
|| code === 'Space'
|
||||||
|
|| code === 'Enter'
|
||||||
|
|| code === 'NumLock'
|
||||||
|
|| code === 'ScrollLock'
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function listenKeyboard(event) {
|
function listenKeyboard(event) {
|
||||||
// Ctrl/Cmd +
|
// Ctrl/Cmd +
|
||||||
if (event.ctrlKey || event.metaKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
// k - for search Modal
|
// open search modal
|
||||||
if (event.keyCode === 75) {
|
if (event.code === 'KeyK') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (navigation.isRawModalVisible) return;
|
if (navigation.isRawModalVisible) return;
|
||||||
openSearch();
|
openSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// focus message field on paste
|
||||||
|
if (event.code === 'KeyV') {
|
||||||
|
if (navigation.isRawModalVisible) return;
|
||||||
|
const msgTextarea = document.getElementById('message-textarea');
|
||||||
|
const { activeElement } = document;
|
||||||
|
if (activeElement !== msgTextarea
|
||||||
|
&& ['input', 'textarea'].includes(activeElement.tagName.toLowerCase())
|
||||||
|
) return;
|
||||||
|
msgTextarea?.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event.ctrlKey && !event.altKey) {
|
if (!event.ctrlKey && !event.altKey && !event.metaKey) {
|
||||||
if (navigation.isRawModalVisible) return;
|
if (navigation.isRawModalVisible) return;
|
||||||
if (['text', 'textarea'].includes(document.activeElement.type)) {
|
if (['input', 'textarea'].includes(document.activeElement.tagName.toLowerCase())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// esc
|
if (event.code === 'Escape') {
|
||||||
if (event.keyCode === 27) {
|
|
||||||
if (navigation.isRoomSettings) {
|
if (navigation.isRoomSettings) {
|
||||||
toggleRoomSettings();
|
toggleRoomSettings();
|
||||||
return;
|
return;
|
||||||
@@ -31,16 +66,12 @@ function listenKeyboard(event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow these keys to type/focus message field
|
// focus the text field on most keypresses
|
||||||
if ((event.keyCode !== 8 && event.keyCode < 48)
|
if (shouldFocusMessageField(event.code)) {
|
||||||
|| (event.keyCode >= 91 && event.keyCode <= 93)
|
// press any key to focus and type in message field
|
||||||
|| (event.keyCode >= 112 && event.keyCode <= 183)) {
|
const msgTextarea = document.getElementById('message-textarea');
|
||||||
return;
|
msgTextarea?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// press any key to focus and type in message field
|
|
||||||
const msgTextarea = document.getElementById('message-textarea');
|
|
||||||
msgTextarea?.focus();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const cons = {
|
const cons = {
|
||||||
version: '2.0.0',
|
version: '2.0.3',
|
||||||
secretKey: {
|
secretKey: {
|
||||||
ACCESS_TOKEN: 'cinny_access_token',
|
ACCESS_TOKEN: 'cinny_access_token',
|
||||||
DEVICE_ID: 'cinny_device_id',
|
DEVICE_ID: 'cinny_device_id',
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Navigation extends EventEmitter {
|
|||||||
this.isRoomSettings = false;
|
this.isRoomSettings = false;
|
||||||
this.recentRooms = [];
|
this.recentRooms = [];
|
||||||
|
|
||||||
this.isRawModalVisible = false;
|
this.rawModelStack = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
_setSpacePath(roomId) {
|
_setSpacePath(roomId) {
|
||||||
@@ -47,8 +47,13 @@ class Navigation extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isRawModalVisible() {
|
||||||
|
return this.rawModelStack.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
setIsRawModalVisible(visible) {
|
setIsRawModalVisible(visible) {
|
||||||
this.isRawModalVisible = visible;
|
if (visible) this.rawModelStack.push(true);
|
||||||
|
else this.rawModelStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(action) {
|
navigate(action) {
|
||||||
|
|||||||
Reference in New Issue
Block a user