Skip to content

Commit

Permalink
Merge pull request #177 from skychatorg/dev/7ph
Browse files Browse the repository at this point in the history
Various improvements
  • Loading branch information
7PH authored Nov 26, 2023
2 parents e780743 + d1e7f15 commit 70f6066
Show file tree
Hide file tree
Showing 32 changed files with 622 additions and 75 deletions.
12 changes: 7 additions & 5 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
"root": true,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"parser": "@typescript-eslint/parser"
},
"extends": [
"plugin:vue/strongly-recommended",
"eslint:recommended",
"@vue/typescript/recommended",
"prettier",
"plugin:vue/vue3-recommended",
"prettier"
],
"plugins": ["@typescript-eslint", "prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": [
"error",
Expand All @@ -31,5 +30,8 @@
"endOfLine": "auto"
}
]
},
"globals": {
"NodeJS": true
}
}
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ The `config/preferences.json` file specifies application preferences. The availa

| field | type | default | description |
|-------|------|---------|-------------|
| minRightForPublicMessages | number | -1 | Min. right to send public messages |
| minRightForPrivateMessages | number | -1 | Min. right to send private messages |
| minRightForShortTermMessageHistory | number | -1 | Min. right to access short term room message history |
| minRightForMessageHistory | number | -1 | Min. right to access full room message history |
Expand All @@ -116,6 +117,7 @@ The `config/preferences.json` file specifies application preferences. The availa
| maxNewlinesPerMessage | number | 20 | Max. number of newlines per message |
| maxConsecutiveMessages | number | 1 | Max. number of consecutive messages in a room |
| maxMessageMergeDelayMin | number | 10 | Max. minutes before not merging consecutive messages |
| daysBeforeMessageFuzz | number | 7 | Number of days before messages are fuzzed, if ExtraSecurityPluginGroup is enabled |
| invertedBlacklist | boolean | false | Whether blacklisted users can not see messages from users who blacklisted them |
| messagesCooldown | ([number, number])[] | [ [ -1, 0] ] | Minimum duration between two consecutive messages for each right level, sorted by ascending right number |

Expand Down Expand Up @@ -153,6 +155,11 @@ When the source files change, the build processes re-runs automatically.

### Add features

Refer to [the Wiki](https://github.com/skychatorg/skychat/wiki) guides to contribute:
- [Write a plugin](https://github.com/skychatorg/skychat/wiki/SkyChat-Plugin-Development-Documentation)
- [Example: Write the TypingList Plugin](https://github.com/skychatorg/skychat/wiki/Room-Plugin-Example:-Writing-the-TypingList-Plugin)
- [Plugin hooks](https://github.com/skychatorg/skychat/wiki/SkyChat-Plugin-Hooks-Documentation)

Please use only one of the following to suggest new features (or bug fixes):
- Create a pull request
- Open an issue with your proposal
22 changes: 18 additions & 4 deletions app/api/SkyChatClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class SkyChatClient extends EventEmitter {
static readonly CURSOR_DECAY_DELAY = 10 * 1e3;

static readonly LOCAL_STORAGE_TOKEN_KEY = 'skychat-token';
static readonly LOCAL_STORAGE_ROOM_ID = 'skychat-room-id';

private _websocket: WebSocket | null = null;

Expand Down Expand Up @@ -96,7 +97,11 @@ export class SkyChatClient extends EventEmitter {
private _currentPlayerChannelId: number | null = null;
private _currentPlayerChannel: SanitizedPlayerChannel | null = null;
private _playerApiSearchResult: { type: string; items: Array<VideoInfo> } | null = null;
private _player: { current: QueuedVideoInfo | null; queue: QueuedVideoInfo[]; cursor: number } = { current: null, queue: [], cursor: 0 };
private _player: { current: QueuedVideoInfo | null; queue: QueuedVideoInfo[]; cursor: number } = {
current: null,
queue: [],
cursor: 0,
};
private _playerLastUpdate: Date | null = null;

constructor(public readonly url: string) {
Expand Down Expand Up @@ -230,16 +235,20 @@ export class SkyChatClient extends EventEmitter {
this._user = ownUser.user;
}
({ messageIdToLastSeenUsers: this._messageIdToLastSeenUsers } = this._generateMessageIdToLastSeenUsers());
({ roomConnectedUsers: this._roomConnectedUsers, playerChannelUsers: this._playerChannelUsers } = this._generateRoomConnectedUsersAndPlayerChannelUsers());
({ roomConnectedUsers: this._roomConnectedUsers, playerChannelUsers: this._playerChannelUsers } =
this._generateRoomConnectedUsersAndPlayerChannelUsers());
}

private _onRoomList(rooms: Array<SanitizedRoom>) {
this._rooms = rooms;
this.emit('update', this.state);
}

private _onCurrentRoomId(currentRoomId: number) {
private _onCurrentRoomId(currentRoomId: number | null) {
this._currentRoomId = currentRoomId;
if (typeof localStorage !== 'undefined' && currentRoomId !== null) {
localStorage.setItem(SkyChatClient.LOCAL_STORAGE_ROOM_ID, currentRoomId.toString());
}
this.emit('update', this.state);
// Ask for message history if joined a room
currentRoomId !== null && this.sendMessage('/messagehistory');
Expand Down Expand Up @@ -507,7 +516,12 @@ export class SkyChatClient extends EventEmitter {
if (typeof localStorage !== 'undefined') {
const authToken = localStorage.getItem(SkyChatClient.LOCAL_STORAGE_TOKEN_KEY);
if (authToken) {
this._sendEvent('set-token', JSON.parse(authToken));
const rawRoomId = localStorage.getItem(SkyChatClient.LOCAL_STORAGE_ROOM_ID);
const roomId = rawRoomId ? parseInt(rawRoomId) : null;
this._sendEvent('set-token', {
...JSON.parse(authToken),
roomId: roomId ?? undefined,
});
}
}
this.emit('update', this.state);
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ProfileModal from '@/components/modal/ProfileModal.vue';
import PlayerQueueModal from '@/components/modal/PlayerQueueModal.vue';
import OngoingConvertsModal from '@/components/modal/OngoingConvertsModal.vue';
import VideoConverterModal from '@/components/modal/VideoConverterModal.vue';
import ManageRoomsModal from '@/components/modal/ManageRoomsModal.vue';
</script>

<template>
Expand All @@ -19,6 +20,7 @@ import VideoConverterModal from '@/components/modal/VideoConverterModal.vue';
<VideoConverterModal />
<GalleryModal />
<PlayerQueueModal />
<ManageRoomsModal />
<RouterView class="grow" />
</div>
</template>
29 changes: 20 additions & 9 deletions app/client/src/components/message/NewMessageForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useAppStore } from '@/stores/app';
import { useClientStore } from '@/stores/client';
import { AudioRecorder } from '@/lib/AudioRecorder';
import { RisiBank } from 'risibank-web-api';
import HoverCard from '@/components/util/HoverCard.vue';
const MESSAGE_HISTORY_LENGTH = 500;
Expand All @@ -22,7 +21,7 @@ onMounted(() => {
message.value.focus();
});
const typingListStr = computed(() => {
const typingListText = computed(() => {
let typingUsers = client.state.typingList;
typingUsers = typingUsers.filter((user) => user.username.toLowerCase() !== client.state.user.username.toLowerCase());
Expand All @@ -43,10 +42,21 @@ const typingListStr = computed(() => {
return 'multiple users are currently typing..';
});
const textAreaPlaceholder = computed(() => {
if (!client.state.currentRoom) {
return '❌ Disconnected';
}
let placeholder = `New message / ${client.state.currentRoom.name}`;
if (client.state.currentRoom.plugins.messagelimiter) {
placeholder += ` (char limit: ${client.state.currentRoom.plugins.messagelimiter})`;
}
return placeholder;
});
// Watch when the new message input changes. The change does not necessarily come from this component, as the message input can be prepared elsewhere.
watch(
() => app.newMessage,
(newValue, oldValue) => {
(newValue) => {
// Auto-set the message value
if (newValue !== message.value.value) {
message.value.value = newValue;
Expand Down Expand Up @@ -188,7 +198,7 @@ const recordingAudioStopCb = ref(null);
const uploadAudio = async function () {
if (recordingAudio.value) {
// Stop recording
const { blob, uri, audio } = await recordingAudioStopCb.value();
const { blob } = await recordingAudioStopCb.value();
client.sendAudio(blob);
} else {
// Start recording
Expand Down Expand Up @@ -229,7 +239,7 @@ const cancelAudio = function () {
<label class="form-control cursor-pointer w-12" for="file-input">
<fa icon="upload" />
</label>
<input ref="fileUploadInput" @change="onFileInputChange" type="file" id="file-input" class="hidden" />
<input id="file-input" ref="fileUploadInput" type="file" class="hidden" @change="onFileInputChange" />
</div>

<!-- Send audio -->
Expand All @@ -246,7 +256,7 @@ const cancelAudio = function () {

<!-- RisiBank -->
<div title="Add a media from RisiBank" class="flex flex-col justify-end">
<button @click="openRisiBank" class="form-control ml-2 w-12 h-10 align-bottom">
<button class="form-control ml-2 w-12 h-10 align-bottom" @click="openRisiBank">
<img src="/assets/images/icons/risibank.png" class="w-4 h-4" />
</button>
</div>
Expand All @@ -266,15 +276,16 @@ const cancelAudio = function () {
<div class="grow flex flex-col">
<!-- Typing list -->
<p class="h-5 pl-2 text-xs text-skygray-lightest">
{{ typingListStr }}
{{ typingListText }}
</p>
<textarea
ref="message"
:rows="messageTextAreaRows"
class="mousetrap form-control lg:ml-2 scrollbar resize-none"
type="text"
:placeholder="client.state.currentRoom ? `New message / ${client.state.currentRoom.name}` : '❌ Disconnected'"
:placeholder="textAreaPlaceholder"
:disabled="!client.state.currentRoom"
:maxlength="client.state.currentRoom.plugins.messagelimiter ?? null"
@input="onMessageInput"
@keyup.up.exact="onNavigateIntoHistory($event, -1)"
@keyup.down.exact="onNavigateIntoHistory($event, 1)"
Expand All @@ -286,7 +297,7 @@ const cancelAudio = function () {

<!-- Send button -->
<div title="Send" class="flex flex-col justify-end">
<button @click="sendMessage" class="form-control ml-2 h-fit align-bottom">
<button class="form-control ml-2 h-fit align-bottom" @click="sendMessage">
<fa icon="paper-plane" />
</button>
</div>
Expand Down
38 changes: 38 additions & 0 deletions app/client/src/components/modal/ManageRoomsModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup>
import { computed, watch, onMounted, ref } from 'vue';
import { useClientStore } from '@/stores/client';
import ModalTemplate from '@/components/modal/ModalTemplate.vue';
import SingleRoom from '@/components/room/SingleRoom.vue';
import draggable from 'vuedraggable';
const client = useClientStore();
const publicRooms = computed(() => {
return client.state.rooms.filter((room) => !room.isPrivate);
});
let sortedRooms = ref(publicRooms.value);
onMounted(() => {
sortedRooms.value = publicRooms.value;
});
watch(publicRooms, () => {
sortedRooms.value = publicRooms.value;
});
function onMoved() {
const sortedRoomIds = sortedRooms.value.map((room) => room.id);
client.sendMessage('/roomorder ' + sortedRoomIds.join(','));
}
</script>

<template>
<ModalTemplate id="manageRooms" title="Manage rooms">
Manage rooms

<draggable v-model="sortedRooms" group="rooms" item-key="id" @end="onMoved">
<template #item="{ element }">
<SingleRoom :room="element">{{ element.name }}</SingleRoom>
</template>
</draggable>
</ModalTemplate>
</template>
9 changes: 9 additions & 0 deletions app/client/src/components/room/RoomList.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script setup>
import { computed } from 'vue';
import { useClientStore } from '@/stores/client';
import { useAppStore } from '@/stores/app';
import SingleRoom from '@/components/room/SingleRoom.vue';
import SectionTitle from '@/components/util/SectionTitle.vue';
const app = useAppStore();
const client = useClientStore();
// Whether has unread
Expand All @@ -21,6 +23,10 @@ const hasUnread = computed(() => {
return false;
});
function manageRooms() {
app.toggleModal('manageRooms');
}
</script>
<template>
Expand All @@ -32,6 +38,9 @@ const hasUnread = computed(() => {
>
Rooms
<fa v-if="hasUnread" icon="comments" />
<a v-if="client.state.op" class="ml-auto cursor-pointer" @click="manageRooms">
<fa icon="gear" />
</a>
</SectionTitle>
<div class="px-2 h-0 grow overflow-y-auto scrollbar">
<SingleRoom v-for="room in client.state.rooms" :key="room.id" :room="room" />
Expand Down
24 changes: 22 additions & 2 deletions app/client/src/components/room/SingleRoom.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ const formattedName = computed(() => {
}
});
/**
* Return whether this room is protected
*/
const isProtected = computed(() => {
return Boolean(props.room.plugins?.roomprotect);
});
// Choose icon to show and icon color
const icon = computed(() => {
if (props.room.isPrivate) {
Expand All @@ -71,21 +78,34 @@ const icon = computed(() => {
classes: selected.value ? 'text-skygray-white' : 'text-skygray-lightest',
};
}
return {
name: 'user',
classes: selected.value ? 'text-skygray-white' : 'text-skygray-lightest',
};
}
if (isProtected.value) {
return {
name: 'lock',
classes: selected.value ? 'text-skygray-white' : 'text-skygray-lightest',
title: `This room is protected. The minimum right to join is ${props.room.plugins.roomprotect}`,
};
}
return null;
});
</script>
<template>
<HoverCard :use-border-radius="true" :border-color="'rgb(var(' + borderColor + '))'" :selectable="true" :selected="selected" class="cursor-pointer">
<HoverCard
:use-border-radius="true"
:border-color="'rgb(var(' + borderColor + '))'"
:selectable="true"
:selected="selected"
class="cursor-pointer"
>
<div @click="client.state.currentRoomId !== room.id && client.join(room.id)" class="py-2 px-3 flex flex-row select-none">
<!-- Room name -->
<div class="grow whitespace-nowrap w-0 overflow-hidden text-ellipsis pr-2">
<fa v-if="icon" class="mr-1" :class="icon.classes" :icon="icon.name" />
<fa v-if="icon" class="mr-1" :class="icon.classes" :icon="icon.name" :title="icon.title" />
{{ formattedName }}
</div>
Expand Down
4 changes: 4 additions & 0 deletions app/client/src/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ import {
faFileVideo,
faInfo,
faSpinner,
faUser,
faGear,
} from '@fortawesome/free-solid-svg-icons';

library.add(
Expand Down Expand Up @@ -119,6 +121,8 @@ library.add(
faFileVideo,
faInfo,
faSpinner,
faUser,
faGear,
);

export default library;
5 changes: 5 additions & 0 deletions app/client/src/stores/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ export const useAppStore = defineStore('app', {
* List of next videos to play
*/
playerQueue: false,

/**
* Room management modal
*/
manageRooms: false,
},
}),

Expand Down
Loading

0 comments on commit 70f6066

Please sign in to comment.