2.2.6.5114514

This commit is contained in:
WhkSoft committed 2023-04-02 19:55:27 +08:00
1 parent a41440857d
commit 085627cffd
23 files changed
+4496 -48

No files matched your search

+1
View File
@@ -4,3 +4,4 @@ node_modules
devAssets
.DS_Store
android
+1
View File
@@ -6,6 +6,7 @@ WORKDIR /src
COPY .npmrc package.json package-lock.json /src/
RUN npm ci
COPY . /src/
ENV NODE_OPTIONS=--max_old_space_size=4096
RUN npm run build
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

+10
View File
@@ -0,0 +1,10 @@
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'today.icarus.cinny',
appName: 'Cinny',
webDir: 'dist',
bundledWebRuntime: false,
};
export default config;
+4151 -29
View File
File diff suppressed because it is too large. Load diff
+6 -2
View File
@@ -1,6 +1,6 @@
{
"name": "cinny",
"version": "2.2.4",
"version": "2.2.6",
"description": "Yet another matrix client",
"main": "index.js",
"engines": {
@@ -19,6 +19,8 @@
"author": "Ajay Bura",
"license": "MIT",
"dependencies": {
"@capacitor/android": "4.7.1",
"@capacitor/core": "4.7.1",
"@fontsource/inter": "4.5.14",
"@fontsource/roboto": "4.5.8",
"@khanacademy/simple-markdown": "0.8.6",
@@ -40,7 +42,7 @@
"katex": "0.16.4",
"linkify-html": "4.0.2",
"linkifyjs": "4.0.2",
"matrix-js-sdk": "22.0.0",
"matrix-js-sdk": "24.0.0",
"prop-types": "15.8.1",
"react": "17.0.2",
"react-autosize-textarea": "7.1.0",
@@ -56,6 +58,8 @@
"twemoji": "14.0.2"
},
"devDependencies": {
"@capacitor/assets": "2.0.4",
"@capacitor/cli": "4.7.1",
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
"@rollup/plugin-inject": "5.0.3",
"@rollup/plugin-wasm": "6.1.1",
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 800 800" style="enable-background:new 0 0 800 800;" xml:space="preserve">
<style type="text/css">
.st0{stroke:#000000;stroke-width:9;stroke-miterlimit:10;}
</style>
<g>
<path class="st0" d="M405,442.5H244.8v160.2h-67.1V442.5H17.5v-67.1h160.2V215.2h67.1v160.2H405V442.5z"/>
<path class="st0" d="M757,697.8h-75.1V237.2c-30.4,20.2-77.4,30.4-141,30.4v-59.1c91.9,0,144.6-35.4,158.2-106.3H757V697.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 688 B

+12
View File
@@ -50,6 +50,7 @@ import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
import CmdIC from '../../../../public/res/ic/outlined/cmd.svg';
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
import TransIC from '../../../../public/res/ic/outlined/translate.svg';
import repeatIC from '../../../../public/res/ic/outlined/repeat.svg';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import { getBlobSafeMimeType } from '../../../util/mimetypes';
@@ -552,6 +553,14 @@ function handleTextTranslate(mEvent) {
openTextTranslate(mEvent);
}
async function handleRepeat(mEvent){
console.log(mEvent);
const opt = {};
opt.msgType = 'm.text';
initMatrix.roomsInput.setMessage(mEvent.sender.roomId, mEvent.clearEvent.content.body);
await initMatrix.roomsInput.sendInput(mEvent.sender.roomId, opt);
}
const MessageOptions = React.memo(({ roomTimeline, mEvent, edit, reply }) => {
const { roomId, room } = roomTimeline;
const mx = initMatrix.matrixClient;
@@ -591,6 +600,9 @@ const MessageOptions = React.memo(({ roomTimeline, mEvent, edit, reply }) => {
<MenuItem iconSrc={TransIC} onClick={() => handleTextTranslate(mEvent)}>
Translate
</MenuItem>
<MenuItem iconSrc={repeatIC} onClick={() => handleRepeat(mEvent)}>
Repeat
</MenuItem>
{(canIRedact || senderId === mx.getUserId()) && (
<>
<MenuBorder />
+2 -1
View File
@@ -188,6 +188,7 @@ function RoomViewInput({ roomId, roomTimeline, viewEvent }) {
}, [roomId]);
const sendBody = async (body, options) => {
console.log(options)
const opt = options ?? {};
if (!opt.msgType) opt.msgType = 'm.text';
if (typeof opt.autoMarkdown !== 'boolean') opt.autoMarkdown = true;
@@ -218,7 +219,7 @@ function RoomViewInput({ roomId, roomTimeline, viewEvent }) {
confirmDialog('Invalid Command', `"${cmdName}" is not a valid command.`, 'Alright');
return;
}
if (['me', 'shrug', 'plain'].includes(cmdName)) {
if (['me', 'shrug', 'plain', 'rainbow'].includes(cmdName)) {
commands[cmdName].exe(roomId, cmdData, sendBody);
return;
}
+59
View File
@@ -31,6 +31,57 @@ export function processMxidAndReason(data) {
};
}
function rainbowText(str) {
// 定义渐变颜色数组
const startColor = [255, 0, 0]; // 红色
const middleColor = [0, 255, 0]; // 绿色
const endColor = [0, 0, 255]; // 蓝色
const numSteps = str.length - 1;
const stepsToGreen = Math.round(numSteps / 2);
const stepsToBlue = numSteps - stepsToGreen;
const stepToGreen = [
(middleColor[0] - startColor[0]) / stepsToGreen,
(middleColor[1] - startColor[1]) / stepsToGreen,
(middleColor[2] - startColor[2]) / stepsToGreen
];
const stepToBlue = [
(endColor[0] - middleColor[0]) / stepsToBlue,
(endColor[1] - middleColor[1]) / stepsToBlue,
(endColor[2] - middleColor[2]) / stepsToBlue
];
// 生成 HTML 字符串
let html = '';
const currentColor = startColor.slice();
// eslint-disable-next-line no-plusplus
for (let i = 0; i < str.length; i++) {
// 转换颜色为 16 进制字符串
const hexColor = `#${ currentColor.map(c => {
const hex = Math.round(c).toString(16);
return hex.length === 1 ? `0${ hex}` : hex;
}).join('')}`;
// 将字符包装在 <font> 标签中,并设置颜色
const fontTag = `<font color="${hexColor}">${str[i]}</font>`;
html += fontTag;
// 计算下一个颜色
if (i < stepsToGreen) {
currentColor[0] += stepToGreen[0];
currentColor[1] += stepToGreen[1];
currentColor[2] += stepToGreen[2];
} else {
currentColor[0] += stepToBlue[0];
currentColor[1] += stepToBlue[1];
currentColor[2] += stepToBlue[2];
}
}
return html;
}
const commands = {
me: {
name: 'me',
@@ -198,6 +249,14 @@ const commands = {
roomActions.convertToRoom(roomId);
},
},
rainbow: {
name: 'rainbow',
description: 'Rainbow text',
exe: (roomId, data, onSuccess) => onSuccess(
data,
{ msgtype:'m.text', format: 'org.matrix.custom.html', formatted_body: rainbowText(data) },
)
},
};
function openHelpDialog() {
+57 -2
View File
@@ -42,11 +42,15 @@ import BellIC from '../../../../public/res/ic/outlined/bell.svg';
import InfoIC from '../../../../public/res/ic/outlined/info.svg';
import PowerIC from '../../../../public/res/ic/outlined/power.svg';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
import CinnySVG from '../../../../public/res/svg/cinny.svg';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import '../../i18n.jsx'
import PeopleSelector from '../../molecules/people-selector/PeopleSelector';
import colorMXID from '../../../util/colorMXID';
import { openReusableDialog } from '../../../client/action/navigation';
let capabilities = {
privateReadReceipts: false,
@@ -101,6 +105,9 @@ function AppearanceSection() {
{ text: 'Silver' },
{ text: 'Dark' },
{ text: 'Butter' },
{ text: 'NGA'},
{ text: 'NGA Green'},
{ text: '?'}
]}
onSelect={(index) => {
if (settings.useSystemTheme) toggleSystemTheme();
@@ -355,10 +362,10 @@ function AboutSection() {
<div>
<Text variant="h2" weight="medium">
Cinny Mod
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}.52`}</span>
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}.5114514`}</span>
</Text>
<Text>Yet another matrix client</Text>
<Text>Modded By WHK with magic & love</Text>
<Text>Modded By WHK(a.k.a CroiX) with magic & love</Text>
<div className="settings-about__btns">
<Button onClick={() => window.open('https://github.com/ajbura/cinny')}>Source code</Button>
@@ -468,6 +475,51 @@ function Settings() {
}
};
const handleSwitchUsers = async () => {
const loggedInUsers = JSON.parse(window.localStorage.getItem("loggedInUsers"));
const renderUserSwitcher = () => (
<div>
{
loggedInUsers.map((userId) => {
const mx = initMatrix.matrixClient;
const user = mx.getUser(userId);
const avatarUrl = user.avatarUrl ? mx.mxcUrlToHttp(user.avatarUrl, 80, 80, 'crop') : null;
return (
<PeopleSelector
key={userId}
onClick={() => {
if (userId !== window.localStorage.getItem("currentUser")) {
window.localStorage.setItem("currentUser", userId);
window.location.reload();
}
}}
name={user.displayName}
avatarSrc={avatarUrl}
color={colorMXID(userId)}
/>
);
})
}
<Button
variant="caution"
iconSrc={AddUserIC}
onClick={() => {
window.localStorage.removeItem("currentUser");
window.location.reload();
}}
>
Add user
</Button>
</div>
);
await openReusableDialog(
<Text variant="s1" weight="medium">Switch users</Text>,
renderUserSwitcher,
)
}
return (
<PopupWindow
isOpen={isOpen}
@@ -475,6 +527,9 @@ function Settings() {
title={<Text variant="s1" weight="medium" primary>{t("Settings")}</Text>}
contentOptions={(
<>
<Button variant="caution" iconSrc={AddUserIC} onClick={handleSwitchUsers}>
Switch users
</Button>
<Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}>
Logout
</Button>
+36 -2
View File
@@ -25,6 +25,8 @@ import EyeIC from '../../../../public/res/ic/outlined/eye.svg';
import EyeBlindIC from '../../../../public/res/ic/outlined/eye-blind.svg';
import CinnySvg from '../../../../public/res/svg/cinny.svg';
import SSOButtons from '../../molecules/sso-buttons/SSOButtons';
import colorMXID from '../../../util/colorMXID';
import PeopleSelector from '../../molecules/people-selector/PeopleSelector';
const LOCALPART_SIGNUP_REGEX = /^[a-z0-9_\-.=/]+$/;
const BAD_LOCALPART_ERROR = 'Username can only contain characters a-z, 0-9, or \'=_-./\'';
@@ -537,13 +539,14 @@ function AuthCard() {
function Auth() {
const [loginToken, setLoginToken] = useState(getUrlPrams('loginToken'));
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(async () => {
if (!loginToken) return;
if (localStorage.getItem(cons.secretKey.BASE_URL) === undefined) {
if (window.userLocalStorage.getItem(cons.secretKey.BASE_URL) === undefined) {
setLoginToken(null);
return;
}
const baseUrl = localStorage.getItem(cons.secretKey.BASE_URL);
const baseUrl = window.userLocalStorage.getItem(cons.secretKey.BASE_URL);
try {
await auth.loginWithToken(baseUrl, loginToken);
@@ -567,6 +570,37 @@ function Auth() {
<Text variant="h2" weight="medium">Cinny</Text>
</TitleWrapper>
</Header>
{(() => {
let loggedInUsers = JSON.parse(window.localStorage.getItem("loggedInUsers"));
if (!loggedInUsers) {
// 检查currentUser是否存在
const currentUser = window.localStorage.getItem('cinny_user_id');
if(currentUser){
// 开始迁移
window.localStorage.setItem('loggedInUsers', JSON.stringify([currentUser]));
window.localStorage.setItem('currentUser', currentUser);
window.localStorage.setItem(`${currentUser}.cinny_access_token`, window.localStorage.getItem('cinny_access_token'));
window.localStorage.setItem(`${currentUser}.cinny_device_id`, window.localStorage.getItem('cinny_device_id'));
window.localStorage.setItem(`${currentUser}.cinny_hs_base_url`, window.localStorage.getItem('cinny_hs_base_url'));
window.localStorage.setItem(`${currentUser}.cinny_user_id`, window.localStorage.getItem('cinny_user_id'));
} else{
loggedInUsers = []
}
}
return (
loggedInUsers.map((userId) => (
<PeopleSelector
key={userId}
onClick={() => {
window.localStorage.setItem("currentUser", userId);
window.location.reload();
}}
name={userId}
color={colorMXID(userId)}
/>
))
);
})()}
<div className="auth-card__content">
<AuthCard />
</div>
+16
View File
@@ -89,7 +89,9 @@ function Client() {
if (isLoading) {
return (
<div className="loading-display">
{/* <div className="watermark">Cinny Mod</div> */}
<div className="loading__menu">
<ContextMenu
placement="bottom"
@@ -170,6 +172,18 @@ function Client() {
initMatrix.roomsInput.emit(cons.events.roomsInput.ATTACHMENT_SET, file);
}
// 盲水印实验性特性
// const watermark = document.querySelector('.watermark');
// for (let i = 0; i < window.innerHeight / watermark.offsetHeight + 1; i += 0.25) {
// for (let j = 0; j < window.innerWidth / watermark.offsetWidth + 1; j += 0.25) {
// const clone = watermark.cloneNode(true);
// clone.style.top = i * watermark.offsetHeight + 'px';
// clone.style.left = j * watermark.offsetWidth + 'px';
// document.body.appendChild(clone);
// }
// }
return (
<div
className="client-container"
@@ -178,6 +192,7 @@ function Client() {
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
{/* <div className="watermark">Cinny Mod</div> */}
<div className={`navigation__wrapper ${settings.hideNavigation && roomSelected ? toggleHidden : ''}`} ref={navWrapperRef}>
<Navigation />
</div>
@@ -191,6 +206,7 @@ function Client() {
<DragDrop isOpen={dragCounter !== 0} />
</div>
);
}
export default Client;
+13 -5
View File
@@ -1,11 +1,19 @@
import * as sdk from 'matrix-js-sdk';
import cons from '../state/cons';
import NamespacedStorage from '../state/NamespacedStorage';
function updateLocalStore(accessToken, deviceId, userId, baseUrl) {
localStorage.setItem(cons.secretKey.ACCESS_TOKEN, accessToken);
localStorage.setItem(cons.secretKey.DEVICE_ID, deviceId);
localStorage.setItem(cons.secretKey.USER_ID, userId);
localStorage.setItem(cons.secretKey.BASE_URL, baseUrl);
window.localStorage.setItem("currentUser", userId);
const loggedInUsers = new Set(JSON.parse(window.localStorage.getItem("loggedInUsers")));
loggedInUsers.add(userId);
window.localStorage.setItem("loggedInUsers", JSON.stringify(Array.from(loggedInUsers)));
window.userLocalStorage = new NamespacedStorage(userId, window.localStorage);
window.userLocalStorage.setItem(cons.secretKey.ACCESS_TOKEN, accessToken);
window.userLocalStorage.setItem(cons.secretKey.DEVICE_ID, deviceId);
window.userLocalStorage.setItem(cons.secretKey.USER_ID, userId);
window.userLocalStorage.setItem(cons.secretKey.BASE_URL, baseUrl);
}
function createTemporaryClient(baseUrl) {
@@ -14,7 +22,7 @@ function createTemporaryClient(baseUrl) {
async function startSsoLogin(baseUrl, type, idpId) {
const client = createTemporaryClient(baseUrl);
localStorage.setItem(cons.secretKey.BASE_URL, client.baseUrl);
window.userLocalStorage.setItem(cons.secretKey.BASE_URL, client.baseUrl);
window.location.href = client.getSsoLoginUrl(window.location.href, type, idpId);
}
+6 -1
View File
@@ -115,7 +115,12 @@ class InitMatrix extends EventEmitter {
// ignore if failed to logout
}
await this.matrixClient.clearStores();
window.localStorage.clear();
window.userLocalStorage.clear();
const currentUser = window.localStorage.getItem("currentUser");
const loggedInUsers = new Set(JSON.parse(window.localStorage.getItem("loggedInUsers")));
loggedInUsers.delete(currentUser);
window.localStorage.setItem("loggedInUsers", JSON.stringify(loggedInUsers));
window.localStorage.removeItem("currentUser");
window.location.reload();
}
+46
View File
@@ -0,0 +1,46 @@
class NamespacedStorage {
#namespace;
#storage;
#cache;
constructor(namespace, storage) {
this.#namespace = namespace;
this.#storage = storage;
this.#cache = new Set();
for (let i = 0; i < this.#storage.length; i++) {
let key = this.#storage.key(i);
if (key.startsWith(`${this.#namespace}.`)) {
this.#cache.add(key.replace(`${this.#namespace}.`, ""));
}
}
}
key(n) {
return this.#cache[n];
}
getItem(key) {
return this.#storage.getItem(`${this.#namespace}.${key}`);
}
setItem(key, value) {
this.#storage.setItem(`${this.#namespace}.${key}`, value);
this.#cache.add(key);
}
removeItem(key) {
this.#storage.removeItem(`${this.#namespace}.${key}`);
this.#cache.delete(key);
}
clear() {
for (let key of this.#cache) {
this.removeItem(key);
}
}
}
export default NamespacedStorage;
+2
View File
@@ -298,7 +298,9 @@ class Notifications extends EventEmitter {
_deletePopupRoomNotis(roomId) {
this.roomIdToPopupNotis.get(roomId)?.forEach((n) => {
this.eventIdToPopupNoti.delete(n.tag);
if(n.close){
n.close();
}
});
this.roomIdToPopupNotis.delete(roomId);
}
+5
View File
@@ -211,6 +211,11 @@ class RoomsInput extends EventEmitter {
content.formatted_body = body.html;
}
if(options.format === 'org.matrix.custom.html'){
content.format = 'org.matrix.custom.html';
content.formatted_body = options.formatted_body;
}
if (edit) {
content['m.new_content'] = { ...content };
content['m.relates_to'] = {
+4 -1
View File
@@ -1,7 +1,10 @@
import NamespacedStorage from './NamespacedStorage';
import cons from './cons';
function getSecret(key) {
return localStorage.getItem(key);
const user = global.localStorage.getItem("currentUser");
const storage = new NamespacedStorage(user, global.localStorage);
return storage.getItem(key);
}
const isAuthenticated = () => getSecret(cons.secretKey.ACCESS_TOKEN) !== null;
+1 -1
View File
@@ -1,5 +1,5 @@
const cons = {
version: '2.2.4',
version: '2.2.6',
secretKey: {
ACCESS_TOKEN: 'cinny_access_token',
DEVICE_ID: 'cinny_device_id',
+3 -3
View File
@@ -4,7 +4,7 @@ import appDispatcher from '../dispatcher';
import cons from './cons';
function getSettings() {
const settings = localStorage.getItem('settings');
const settings = window.localStorage.getItem('settings');
if (settings === null) return null;
return JSON.parse(settings);
}
@@ -13,14 +13,14 @@ function setSettings(key, value) {
let settings = getSettings();
if (settings === null) settings = {};
settings[key] = value;
localStorage.setItem('settings', JSON.stringify(settings));
window.localStorage.setItem('settings', JSON.stringify(settings));
}
class Settings extends EventEmitter {
constructor() {
super();
this.themes = ['', 'silver-theme', 'dark-theme', 'butter-theme'];
this.themes = ['', 'silver-theme', 'dark-theme', 'butter-theme', 'nga-theme', 'nga-green-theme', 'troll'];
this.themeIndex = this.getThemeIndex();
this.translationIndex = this.getTranslationAPIIndex();
this.translationLanguage = this.getTranslationLanguage();
+52
View File
@@ -306,6 +306,24 @@
}
}
// .watermark {
// position: fixed;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// z-index: 9999;
// font-size: 24px;
// color: #ccc;
// text-align: center;
// opacity: 0.03;
// transform: rotate(-45deg);
// transform-origin: 0 0;
// user-select: none;
// pointer-events: none;
// }
.butter-theme {
/* background color | --bg-[background type]: value */
--bg-surface: hsl(64, 6%, 14%);
@@ -331,6 +349,40 @@
--ic-surface-low: rgba(255, 251, 222, 64%);
}
.nga-theme {
/* background color | --bg-[background type]: value */
--bg-surface: rgb(254,248,231);
--bg-surface-transparent: hsla(0, 0%, 95%, 0);
--bg-surface-low: rgb(244,232,202);
--bg-surface-low-transparent: hsla(0, 0%, 91%, 0);
--bg-surface-extra-low: rgb(244,232,202);
--bg-surface-extra-low-transparent: hsla(0, 0%, 91%, 0);
--bg-positive: rgb(214, 189, 127);
--bg-primary: rgb(86, 23, 1);
--bg-primary-hover: rgb(180, 116, 98);
--bg-primary-active: rgb(180, 116, 98);
--ic-surface-normal: rgb(86, 23, 1);
}
.nga-green-theme {
/* background color | --bg-[background type]: value */
--bg-surface: rgb(241,247,245);
--bg-surface-transparent: hsla(0, 0%, 95%, 0);
--bg-surface-low: rgb(226,239,235);
--bg-surface-low-transparent: hsla(0, 0%, 91%, 0);
--bg-surface-extra-low: rgb(226,239,235);
--bg-surface-extra-low-transparent: hsla(0, 0%, 91%, 0);
--bg-positive: rgb(212,222,219);
--bg-primary: rgb(42,67,73);
--bg-primary-hover: rgb(125,162,160);
--bg-primary-active: rgb(125,162,160);
--ic-surface-normal: rgb(42,67,73);
}
.troll {
filter: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='3d-glasses' x='0' y='0' width='1' height='1'%3E%3CfeGaussianBlur in='SourceGraphic' stdDeviation='2' /%3E%3CfeColorMatrix values='1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 100 0' /%3E%3CfeOffset dx='5' dy='5' /%3E%3CfeColorMatrix values='0.5 0 0 0 0 0 0.5 0 0 0 0 0 0.5 0 0 0 0 0 1 0' /%3E%3CfeBlend mode='multiply' in2='SourceGraphic' /%3E%3C/filter%3E%3C/svg%3E#3d-glasses");
}
.font-primary {
font-family: var(--font-primary);
+1 -1
View File
@@ -6,7 +6,7 @@ import parse from 'html-react-parser';
import twemoji from 'twemoji';
import { sanitizeText } from './sanitize';
export const TWEMOJI_BASE_URL = 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/';
export const TWEMOJI_BASE_URL = 'https://fastly.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/';
const Math = lazy(() => import('../app/atoms/math/Math'));