Compare commits

...

10 commits

Author SHA1 Message Date
a9ac779fbb
message logger
Some checks failed
main.yml / message logger (push) Failing after 0s
2026-05-17 02:44:50 +09:00
4158688761
actions 2026-05-17 02:44:39 +09:00
kfiven
e89b8f7d12 chore(release): 4.12.1 [skip ci] 2026-05-15 07:20:54 +00:00
Krishan
9bc1e7e9ff
fix: null edit for another release (#2942) 2026-05-15 17:19:08 +10:00
kfiven
c05a6be6f2 chore(release): 4.12.0 [skip ci] 2026-05-15 07:02:05 +00:00
Krishan
f7f4a41d61
Revert "chore: Update GITHUB_TOKEN to CLA_PAT in prod workflow" (#2941)
Revert "chore: Update GITHUB_TOKEN to CLA_PAT in prod workflow (#2940)"

This reverts commit 81327678b1.
2026-05-15 16:59:52 +10:00
Krishan
81327678b1
chore: Update GITHUB_TOKEN to CLA_PAT in prod workflow (#2940)
Changed GITHUB_TOKEN secret to CLA_PAT for semantic release due to branch protection.
2026-05-15 16:57:38 +10:00
renovate[bot]
bad1fb609a
fix(deps): update dependency sanitize-html to v2.17.4 (#2937)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-15 16:33:13 +10:00
Ajay Bura
bef267257a
fix: support for stable mutual rooms endpoint (#2939)
* add support for stable mutual rooms endpoint

* add stable mutual room feature check
2026-05-15 16:31:47 +10:00
Ajay Bura
909aa430b8
fix: notification cause crash on android (#2938)
fix notification cause crash on android
2026-05-15 16:30:23 +10:00
25 changed files with 3106 additions and 45 deletions

View file

@ -0,0 +1,63 @@
name: Upstream Sync and PR
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout Local Repository
uses: actions/checkout@v4
- name: Configure Git
run: |
git config --global user.name "mizuki"
git config --global user.email "akiyama@mizuki.guru"
- name: Add Upstream and Fetch
run: |
git remote add upstream https://github.com/cinnyapp/cinny.git
git fetch upstream main
- name: Check for Changes and Push Branch
id: check_changes
run: |
if ! git diff --quiet HEAD upstream/main; then
echo "New changes detected in upstream."
BRANCH_NAME="upstream-sync-$(date +'%Y%m%d%H%M')"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
echo "has_changes=true" >> $GITHUB_OUTPUT
git checkout -b $BRANCH_NAME
git merge upstream/main --no-edit
git push origin $BRANCH_NAME
else
echo "No changes detected. Everything is up to date."
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request via Forgejo API
if: steps: .check_changes.outputs.has_changes == 'true'
run: |
PR_TITLE="chore(deps): sync upstream changes ($(date +'%Y-%m-%d'))"
PR_BODY="Upstream (cinnyapp/cinny)에 새로운 변경사항이 감지되어 자동으로 생성된 PR입니다."
HEAD_BRANCH="${{ steps.check_changes.outputs.branch_name }}"
BASE_BRANCH="main"
curl -X 'POST' \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/pulls" \
-H "accept: application/json" \
-H "Authorization: token ${{ secrets.TOKEN_FORGEJO }}" \
-H "Content-Type: application/json" \
-d "{
\"base\": \"$BASE_BRANCH\",
\"head\": \"$HEAD_BRANCH\",
\"title\": \"$PR_TITLE\",
\"body\": \"$PR_BODY\"
}"

2904
bun.lock Normal file

File diff suppressed because it is too large Load diff

86
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "cinny",
"version": "4.11.1",
"version": "4.12.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cinny",
"version": "4.11.1",
"version": "4.12.1",
"license": "AGPL-3.0-only",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
@ -58,7 +58,7 @@
"react-i18next": "15.0.0",
"react-range": "1.8.14",
"react-router-dom": "6.30.3",
"sanitize-html": "2.12.1",
"sanitize-html": "2.17.4",
"slate": "0.123.0",
"slate-dom": "0.123.0",
"slate-history": "0.113.1",
@ -80,7 +80,7 @@
"@types/react": "18.2.39",
"@types/react-dom": "18.2.17",
"@types/react-google-recaptcha": "2.1.8",
"@types/sanitize-html": "2.9.0",
"@types/sanitize-html": "2.16.1",
"@types/ua-parser-js": "0.7.36",
"@typescript-eslint/eslint-plugin": "5.46.1",
"@typescript-eslint/parser": "5.46.1",
@ -5691,12 +5691,13 @@
"license": "MIT"
},
"node_modules/@types/sanitize-html": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.9.0.tgz",
"integrity": "sha512-4fP/kEcKNj2u39IzrxWYuf/FnCCwwQCpif6wwY6ROUS1EPRIfWJjGkY3HIowY1EX/VbX5e86yq8AAE7UPMgATg==",
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.16.1.tgz",
"integrity": "sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"htmlparser2": "^8.0.0"
"htmlparser2": "^10.1"
}
},
"node_modules/@types/scheduler": {
@ -6041,7 +6042,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"devOptional": true
"optional": true
},
"node_modules/acorn": {
"version": "8.14.0",
@ -6885,7 +6886,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"devOptional": true,
"optional": true,
"engines": {
"node": ">=10"
}
@ -9498,7 +9499,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"devOptional": true,
"optional": true,
"dependencies": {
"minipass": "^3.0.0"
},
@ -9510,7 +9511,7 @@
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"devOptional": true,
"optional": true,
"dependencies": {
"yallist": "^4.0.0"
},
@ -9522,7 +9523,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"devOptional": true
"optional": true
},
"node_modules/fs.realpath": {
"version": "1.0.0",
@ -10169,9 +10170,9 @@
}
},
"node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
"integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@ -10179,11 +10180,24 @@
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
"domutils": "^3.2.2",
"entities": "^7.0.1"
}
},
"node_modules/htmlparser2/node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/http-proxy-agent": {
@ -10448,6 +10462,7 @@
"integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
"dev": true,
"license": "ISC",
"optional": true,
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
@ -11303,6 +11318,15 @@
"node": ">=0.10"
}
},
"node_modules/launder": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/launder/-/launder-1.7.1.tgz",
"integrity": "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==",
"license": "MIT",
"dependencies": {
"dayjs": "^1.11.7"
}
},
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@ -12284,7 +12308,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"devOptional": true,
"optional": true,
"engines": {
"node": ">=8"
}
@ -12293,7 +12317,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"devOptional": true,
"optional": true,
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
@ -12306,7 +12330,7 @@
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"devOptional": true,
"optional": true,
"dependencies": {
"yallist": "^4.0.0"
},
@ -12318,13 +12342,13 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"devOptional": true
"optional": true
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"devOptional": true,
"optional": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
@ -12465,7 +12489,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"devOptional": true,
"optional": true,
"dependencies": {
"abbrev": "1"
},
@ -16198,14 +16222,16 @@
"license": "MIT"
},
"node_modules/sanitize-html": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz",
"integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==",
"version": "2.17.4",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.4.tgz",
"integrity": "sha512-2HW7v2ol/uAM7sX4hbD8Z59OGWmAPrvjL8E71UWlBcj6m+kcF6ilQBLny+cIgY214QJeJT5tQuxKKqX0SQqjGQ==",
"license": "MIT",
"dependencies": {
"deepmerge": "^4.2.2",
"escape-string-regexp": "^4.0.0",
"htmlparser2": "^8.0.0",
"htmlparser2": "^10.1.0",
"is-plain-object": "^5.0.0",
"launder": "^1.7.1",
"parse-srcset": "^1.0.2",
"postcss": "^8.3.11"
}
@ -17414,7 +17440,7 @@
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"devOptional": true,
"optional": true,
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@ -17431,7 +17457,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"devOptional": true
"optional": true
},
"node_modules/temp-dir": {
"version": "2.0.0",

View file

@ -1,6 +1,6 @@
{
"name": "cinny",
"version": "4.11.1",
"version": "4.12.1",
"description": "Yet another matrix client",
"main": "index.js",
"type": "module",
@ -111,7 +111,7 @@
"react-i18next": "15.0.0",
"react-range": "1.8.14",
"react-router-dom": "6.30.3",
"sanitize-html": "2.12.1",
"sanitize-html": "2.17.4",
"slate": "0.123.0",
"slate-dom": "0.123.0",
"slate-history": "0.113.1",
@ -133,7 +133,7 @@
"@types/react": "18.2.39",
"@types/react-dom": "18.2.17",
"@types/react-google-recaptcha": "2.1.8",
"@types/sanitize-html": "2.9.0",
"@types/sanitize-html": "2.16.1",
"@types/ua-parser-js": "0.7.36",
"@typescript-eslint/eslint-plugin": "5.46.1",
"@typescript-eslint/parser": "5.46.1",

View file

@ -28,7 +28,11 @@ import { copyToClipboard } from '../../utils/dom';
import { getExploreServerPath } from '../../pages/pathUtils';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { factoryRoomIdByAtoZ } from '../../utils/sort';
import { useMutualRooms, useMutualRoomsSupport } from '../../hooks/useMutualRooms';
import {
useMutualRooms,
useMutualRoomsSupport,
useUnstableMutualRoomsSupport,
} from '../../hooks/useMutualRooms';
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import { useDirectRooms } from '../../pages/client/direct/useDirectRooms';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
@ -233,7 +237,9 @@ type MutualRoomsData = {
export function MutualRoomsChip({ userId }: { userId: string }) {
const mx = useMatrixClient();
const mutualRoomSupported = useMutualRoomsSupport();
const mutualRoomUnstable = useUnstableMutualRoomsSupport();
const mutualRoomsState = useMutualRooms(userId);
console.log(mutualRoomSupported, mutualRoomsState);
const { navigateRoom, navigateSpace } = useRoomNavigate();
const closeUserRoomProfile = useCloseUserRoomProfile();
const directs = useDirectRooms();
@ -279,7 +285,7 @@ export function MutualRoomsChip({ userId }: { userId: string }) {
if (
userId === mx.getSafeUserId() ||
!mutualRoomSupported ||
(!mutualRoomSupported && !mutualRoomUnstable) ||
mutualRoomsState.status === AsyncStatus.Error
) {
return null;

View file

@ -719,6 +719,16 @@ export const Message = as<'div', MessageProps>(
ref
) => {
const mx = useMatrixClient();
React.useEffect(() => {
console.log(`[Message Rendered] ${mEvent.getSender()} : ${mEvent.getClearContent()?.body}`, {
roomId: mEvent.getRoomId(),
sender: mEvent.getSender(),
content: mEvent.getClearContent(),
event: mEvent,
});
}, [mEvent]);
const useAuthentication = useMediaAuthentication();
const senderId = mEvent.getSender() ?? '';

View file

@ -46,7 +46,7 @@ export function About({ requestClose }: AboutProps) {
<Box direction="Column" gap="100">
<Box gap="100" alignItems="End">
<Text size="H3">Cinny</Text>
<Text size="T200">v4.11.1</Text>
<Text size="T200">v4.12.1</Text>
</Box>
<Text>Yet another matrix client.</Text>
</Box>

View file

@ -1,9 +1,10 @@
import { useCallback } from 'react';
import { MatrixClient, Method } from 'matrix-js-sdk';
import { useMatrixClient } from './useMatrixClient';
import { AsyncState, useAsyncCallbackValue } from './useAsyncCallback';
import { useSpecVersions } from './useSpecVersions';
export const useMutualRoomsSupport = (): boolean => {
export const useUnstableMutualRoomsSupport = (): boolean => {
const { unstable_features: unstableFeatures } = useSpecVersions();
const supported =
@ -14,16 +15,59 @@ export const useMutualRoomsSupport = (): boolean => {
return !!supported;
};
export const useMutualRoomsSupport = (): boolean => {
const { unstable_features: unstableFeatures, versions } = useSpecVersions();
const supported =
versions.includes('v1.19') ||
unstableFeatures?.['uk.half-shot.msc2666.query_mutual_rooms.stable'];
return !!supported;
};
type MutualRoomsOK = {
joined: string[];
next_batch?: string;
count: number;
};
const fetchAllMutualRooms = async (mx: MatrixClient, userId: string): Promise<string[]> => {
const mutualRooms: Set<string> = new Set();
let nextBatch: string | undefined;
do {
// eslint-disable-next-line no-await-in-loop
const result = await mx.http.authedRequest<MutualRoomsOK>(
Method.Get,
'/mutual_rooms',
{
user_id: userId,
from: nextBatch,
},
undefined,
{
prefix: '/_matrix/client/v1',
}
);
result.joined.forEach((r) => mutualRooms.add(r));
nextBatch = result.next_batch;
} while (typeof nextBatch === 'string');
return Array.from(mutualRooms);
};
export const useMutualRooms = (userId: string): AsyncState<string[], unknown> => {
const mx = useMatrixClient();
const supported = useMutualRoomsSupport();
const unstableSupport = useUnstableMutualRoomsSupport();
const support = useMutualRoomsSupport();
const [mutualRoomsState] = useAsyncCallbackValue(
useCallback(
() => (supported ? mx._unstable_getSharedRooms(userId) : Promise.resolve([])),
[mx, userId, supported]
)
useCallback(() => {
if (support) return fetchAllMutualRooms(mx, userId);
if (unstableSupport) return mx._unstable_getSharedRooms(userId);
return Promise.resolve([]);
}, [mx, userId, unstableSupport, support])
);
return mutualRoomsState;

View file

@ -15,7 +15,7 @@ export function AuthFooter() {
target="_blank"
rel="noreferrer"
>
v4.11.1
v4.12.1
</Text>
<Text as="a" size="T300" href="https://twitter.com/cinnyapp" target="_blank" rel="noreferrer">
Twitter

View file

@ -24,7 +24,7 @@ export function WelcomePage() {
target="_blank"
rel="noreferrer noopener"
>
v4.11.1
v4.12.1
</a>
</span>
}

View file

@ -233,7 +233,15 @@ export const notificationPermission = (permission: NotificationPermission) => {
if ('Notification' in window) {
return window.Notification.permission === permission;
}
try {
// https://stackoverflow.com/questions/29774836/failed-to-construct-notification-illegal-constructor
// https://issues.chromium.org/issues/40415865
// eslint-disable-next-line no-new
new Notification('');
} catch {
return false;
}
return true;
};
export const getMouseEventCords = (event: MouseEvent) => ({