mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-15 11:40:29 +03:00
initial commit
This commit is contained in:
commit
026f835a87
176 changed files with 10613 additions and 0 deletions
145
src/client/action/auth.js
Normal file
145
src/client/action/auth.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import * as sdk from 'matrix-js-sdk';
|
||||
import cons from '../state/cons';
|
||||
import { getBaseUrl } from '../../util/matrixUtil';
|
||||
|
||||
async function login(username, homeserver, password) {
|
||||
const baseUrl = await getBaseUrl(homeserver);
|
||||
|
||||
if (typeof baseUrl === 'undefined') throw new Error('Homeserver not found');
|
||||
|
||||
const client = sdk.createClient({ baseUrl });
|
||||
|
||||
const response = await client.login('m.login.password', {
|
||||
user: `@${username}:${homeserver}`,
|
||||
password,
|
||||
initial_device_display_name: cons.DEVICE_DISPLAY_NAME,
|
||||
});
|
||||
|
||||
localStorage.setItem(cons.secretKey.ACCESS_TOKEN, response.access_token);
|
||||
localStorage.setItem(cons.secretKey.DEVICE_ID, response.device_id);
|
||||
localStorage.setItem(cons.secretKey.USER_ID, response.user_id);
|
||||
localStorage.setItem(cons.secretKey.BASE_URL, response.well_known['m.homeserver'].base_url);
|
||||
}
|
||||
|
||||
async function getAdditionalInfo(baseUrl, content) {
|
||||
try {
|
||||
const res = await fetch(`${baseUrl}/_matrix/client/r0/register`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(content),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
const data = await res.json();
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
async function verifyEmail(baseUrl, content) {
|
||||
try {
|
||||
const res = await fetch(`${baseUrl}/_matrix/client/r0/register/email/requestToken `, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(content),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
const data = await res.json();
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
let session = null;
|
||||
let clientSecret = null;
|
||||
let sid = null;
|
||||
async function register(username, homeserver, password, email, recaptchaValue, terms, verified) {
|
||||
const baseUrl = await getBaseUrl(homeserver);
|
||||
|
||||
if (typeof baseUrl === 'undefined') throw new Error('Homeserver not found');
|
||||
|
||||
const client = sdk.createClient({ baseUrl });
|
||||
|
||||
const isAvailable = await client.isUsernameAvailable(username);
|
||||
if (!isAvailable) throw new Error('Username not available');
|
||||
|
||||
if (typeof recaptchaValue === 'string') {
|
||||
await getAdditionalInfo(baseUrl, {
|
||||
auth: {
|
||||
type: 'm.login.recaptcha',
|
||||
session,
|
||||
response: recaptchaValue,
|
||||
},
|
||||
});
|
||||
} else if (terms === true) {
|
||||
await getAdditionalInfo(baseUrl, {
|
||||
auth: {
|
||||
type: 'm.login.terms',
|
||||
session,
|
||||
},
|
||||
});
|
||||
} else if (verified !== true) {
|
||||
session = null;
|
||||
clientSecret = client.generateClientSecret();
|
||||
console.log(clientSecret);
|
||||
const verifyData = await verifyEmail(baseUrl, {
|
||||
email,
|
||||
client_secret: clientSecret,
|
||||
send_attempt: 1,
|
||||
});
|
||||
if (typeof verifyData.error === 'string') {
|
||||
throw new Error(verifyData.error);
|
||||
}
|
||||
sid = verifyData.sid;
|
||||
}
|
||||
|
||||
const additionalInfo = await getAdditionalInfo(baseUrl, {
|
||||
auth: { session: (session !== null) ? session : undefined },
|
||||
});
|
||||
session = additionalInfo.session;
|
||||
if (typeof additionalInfo.completed === 'undefined' || additionalInfo.completed.length === 0) {
|
||||
return ({
|
||||
type: 'recaptcha',
|
||||
public_key: additionalInfo.params['m.login.recaptcha'].public_key,
|
||||
});
|
||||
}
|
||||
if (additionalInfo.completed.find((process) => process === 'm.login.recaptcha') === 'm.login.recaptcha'
|
||||
&& !additionalInfo.completed.find((process) => process === 'm.login.terms')) {
|
||||
return ({
|
||||
type: 'terms',
|
||||
en: additionalInfo.params['m.login.terms'].policies.privacy_policy.en,
|
||||
});
|
||||
}
|
||||
if (verified || additionalInfo.completed.find((process) => process === 'm.login.terms') === 'm.login.terms') {
|
||||
const tpc = {
|
||||
client_secret: clientSecret,
|
||||
sid,
|
||||
};
|
||||
const verifyData = await getAdditionalInfo(baseUrl, {
|
||||
auth: {
|
||||
session,
|
||||
type: 'm.login.email.identity',
|
||||
threepidCreds: tpc,
|
||||
threepid_creds: tpc,
|
||||
},
|
||||
username,
|
||||
password,
|
||||
});
|
||||
if (verifyData.errcode === 'M_UNAUTHORIZED') {
|
||||
return { type: 'email' };
|
||||
}
|
||||
|
||||
localStorage.setItem(cons.secretKey.ACCESS_TOKEN, verifyData.access_token);
|
||||
localStorage.setItem(cons.secretKey.DEVICE_ID, verifyData.device_id);
|
||||
localStorage.setItem(cons.secretKey.USER_ID, verifyData.user_id);
|
||||
localStorage.setItem(cons.secretKey.BASE_URL, baseUrl);
|
||||
return { type: 'done' };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export { login, register };
|
||||
12
src/client/action/logout.js
Normal file
12
src/client/action/logout.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import initMatrix from '../initMatrix';
|
||||
|
||||
function logout() {
|
||||
const mx = initMatrix.matrixClient;
|
||||
mx.logout().then(() => {
|
||||
mx.clearStores();
|
||||
window.localStorage.clear();
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
export default logout;
|
||||
64
src/client/action/navigation.js
Normal file
64
src/client/action/navigation.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import appDispatcher from '../dispatcher';
|
||||
import cons from '../state/cons';
|
||||
|
||||
function handleTabChange(tabId) {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.CHANGE_TAB,
|
||||
tabId,
|
||||
});
|
||||
}
|
||||
|
||||
function selectRoom(roomId) {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.SELECT_ROOM,
|
||||
roomId,
|
||||
});
|
||||
}
|
||||
|
||||
function togglePeopleDrawer() {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.TOGGLE_PEOPLE_DRAWER,
|
||||
});
|
||||
}
|
||||
|
||||
function openInviteList() {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.OPEN_INVITE_LIST,
|
||||
});
|
||||
}
|
||||
|
||||
function openPublicChannels() {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.OPEN_PUBLIC_CHANNELS,
|
||||
});
|
||||
}
|
||||
|
||||
function openCreateChannel() {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.OPEN_CREATE_CHANNEL,
|
||||
});
|
||||
}
|
||||
|
||||
function openInviteUser(roomId) {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.OPEN_INVITE_USER,
|
||||
roomId,
|
||||
});
|
||||
}
|
||||
|
||||
function openSettings() {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.navigation.OPEN_SETTINGS,
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
handleTabChange,
|
||||
selectRoom,
|
||||
togglePeopleDrawer,
|
||||
openInviteList,
|
||||
openPublicChannels,
|
||||
openCreateChannel,
|
||||
openInviteUser,
|
||||
openSettings,
|
||||
};
|
||||
189
src/client/action/room.js
Normal file
189
src/client/action/room.js
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import initMatrix from '../initMatrix';
|
||||
import appDispatcher from '../dispatcher';
|
||||
import cons from '../state/cons';
|
||||
|
||||
/**
|
||||
* https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L73
|
||||
* @param {string} roomId Id of room to add
|
||||
* @param {string} userId User id to which dm
|
||||
* @returns {Promise} A promise
|
||||
*/
|
||||
function addRoomToMDirect(roomId, userId) {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const mDirectsEvent = mx.getAccountData('m.direct');
|
||||
let userIdToRoomIds = {};
|
||||
|
||||
if (typeof mDirectsEvent !== 'undefined') userIdToRoomIds = mDirectsEvent.getContent();
|
||||
|
||||
// remove it from the lists of any others users
|
||||
// (it can only be a DM room for one person)
|
||||
Object.keys(userIdToRoomIds).forEach((thisUserId) => {
|
||||
const roomIds = userIdToRoomIds[thisUserId];
|
||||
|
||||
if (thisUserId !== userId) {
|
||||
const indexOfRoomId = roomIds.indexOf(roomId);
|
||||
if (indexOfRoomId > -1) {
|
||||
roomIds.splice(indexOfRoomId, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// now add it, if it's not already there
|
||||
if (userId) {
|
||||
const roomIds = userIdToRoomIds[userId] || [];
|
||||
if (roomIds.indexOf(roomId) === -1) {
|
||||
roomIds.push(roomId);
|
||||
}
|
||||
userIdToRoomIds[userId] = roomIds;
|
||||
}
|
||||
|
||||
return mx.setAccountData('m.direct', userIdToRoomIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a room, estimate which of its members is likely to
|
||||
* be the target if the room were a DM room and return that user.
|
||||
* https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L117
|
||||
*
|
||||
* @param {Object} room Target room
|
||||
* @param {string} myUserId User ID of the current user
|
||||
* @returns {string} User ID of the user that the room is probably a DM with
|
||||
*/
|
||||
function guessDMRoomTargetId(room, myUserId) {
|
||||
let oldestMemberTs;
|
||||
let oldestMember;
|
||||
|
||||
// Pick the joined user who's been here longest (and isn't us),
|
||||
room.getJoinedMembers().forEach((member) => {
|
||||
if (member.userId === myUserId) return;
|
||||
|
||||
if (typeof oldestMemberTs === 'undefined' || (member.events.member && member.events.member.getTs() < oldestMemberTs)) {
|
||||
oldestMember = member;
|
||||
oldestMemberTs = member.events.member.getTs();
|
||||
}
|
||||
});
|
||||
if (oldestMember) return oldestMember.userId;
|
||||
|
||||
// if there are no joined members other than us, use the oldest member
|
||||
room.currentState.getMembers().forEach((member) => {
|
||||
if (member.userId === myUserId) return;
|
||||
|
||||
if (typeof oldestMemberTs === 'undefined' || (member.events.member && member.events.member.getTs() < oldestMemberTs)) {
|
||||
oldestMember = member;
|
||||
oldestMemberTs = member.events.member.getTs();
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof oldestMember === 'undefined') return myUserId;
|
||||
return oldestMember.userId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} roomId
|
||||
* @param {boolean} isDM
|
||||
*/
|
||||
function join(roomId, isDM) {
|
||||
const mx = initMatrix.matrixClient;
|
||||
mx.joinRoom(roomId)
|
||||
.then(async () => {
|
||||
if (isDM) {
|
||||
const targetUserId = guessDMRoomTargetId(mx.getRoom(roomId), mx.getUserId());
|
||||
await addRoomToMDirect(roomId, targetUserId);
|
||||
}
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.room.JOIN,
|
||||
roomId,
|
||||
isDM,
|
||||
});
|
||||
}).catch();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} roomId
|
||||
* @param {boolean} isDM
|
||||
*/
|
||||
function leave(roomId, isDM) {
|
||||
const mx = initMatrix.matrixClient;
|
||||
mx.leave(roomId)
|
||||
.then(() => {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.room.LEAVE,
|
||||
roomId,
|
||||
isDM,
|
||||
});
|
||||
}).catch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a room.
|
||||
* @param {Object} opts
|
||||
* @param {string} [opts.name]
|
||||
* @param {string} [opts.topic]
|
||||
* @param {boolean} [opts.isPublic=false] Sets room visibility to public
|
||||
* @param {string} [opts.roomAlias] Sets the room address
|
||||
* @param {boolean} [opts.isEncrypted=false] Makes room encrypted
|
||||
* @param {boolean} [opts.isDirect=false] Makes room as direct message
|
||||
* @param {string[]} [opts.invite=[]] An array of userId's to invite
|
||||
*/
|
||||
async function create(opts) {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const options = {
|
||||
name: opts.name,
|
||||
topic: opts.topic,
|
||||
visibility: opts.isPublic === true ? 'public' : 'private',
|
||||
room_alias_name: opts.roomAlias,
|
||||
is_direct: opts.isDirect === true,
|
||||
invite: opts.invite || [],
|
||||
initial_state: [],
|
||||
};
|
||||
|
||||
if (opts.isPublic !== true && opts.isEncrypted === true) {
|
||||
options.initial_state.push({
|
||||
type: 'm.room.encryption',
|
||||
state_key: '',
|
||||
content: {
|
||||
algorithm: 'm.megolm.v1.aes-sha2',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await mx.createRoom(options);
|
||||
if (opts.isDirect === true && typeof opts.invite[0] !== 'undefined') {
|
||||
await addRoomToMDirect(result.room_id, opts.invite[0]);
|
||||
}
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.room.CREATE,
|
||||
roomId: result.room_id,
|
||||
isDM: opts.isDirect === true,
|
||||
});
|
||||
return result;
|
||||
} catch (e) {
|
||||
const errcodes = ['M_UNKNOWN', 'M_BAD_JSON', 'M_ROOM_IN_USE', 'M_INVALID_ROOM_STATE', 'M_UNSUPPORTED_ROOM_VERSION'];
|
||||
if (errcodes.find((errcode) => errcode === e.errcode)) {
|
||||
appDispatcher.dispatch({
|
||||
type: cons.actions.room.error.CREATE,
|
||||
error: e,
|
||||
});
|
||||
throw new Error(e);
|
||||
}
|
||||
throw new Error('Something went wrong!');
|
||||
}
|
||||
}
|
||||
|
||||
async function invite(roomId, userId) {
|
||||
const mx = initMatrix.matrixClient;
|
||||
|
||||
try {
|
||||
const result = await mx.invite(roomId, userId);
|
||||
return result;
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
join, leave, create, invite,
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue