Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:Fancy-Mumble/FancyMumbleV2 into …
Browse files Browse the repository at this point in the history
…develop
  • Loading branch information
SetZero committed Jun 28, 2023
2 parents e6a3428 + f900662 commit 450703c
Show file tree
Hide file tree
Showing 25 changed files with 572 additions and 32 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@
"@tauri-apps/api": "^1.2.0",
"@types/dompurify": "^3.0.1",
"@types/marked": "^5.0.0",
"@types/quill": "^2.0.10",
"@types/react-color": "^3.0.6",
"@types/tinycolor2": "^1.4.3",
"dayjs": "^1.11.7",
"dompurify": "^3.0.2",
"fuse.js": "^6.6.2",
"highlight.js": "^11.8.0",
"html-react-parser": "^4.0.0",
"lru-cache": "^10.0.0",
"marked": "^5.0.2",
"marked-highlight": "^2.0.0",
"quill": "1.3.6",
"react": "^18.2.0",
"react-color": "^2.19.3",
"react-dom": "^18.2.0",
Expand Down
Binary file added public/static_fonts/Comfortaa/Comfortaa-Bold.ttf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ image = "0.24.6"
directories = "5.0.1"
num-traits = "0.2"
brotli = "3.3.4"
webbrowser = "0.8.10"

[dev-dependencies]
tempfile = "3.5.0"
Expand Down
14 changes: 14 additions & 0 deletions src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use base64::{engine::general_purpose, Engine};
use tauri::State;
use tokio::sync::Mutex;
use tracing::{error, info, trace};
use webbrowser::{Browser, BrowserOptions};

pub struct ConnectionState {
pub connection: Mutex<Option<Connection>>,
Expand Down Expand Up @@ -316,3 +317,16 @@ pub fn unzip_data_from_utf8(data: &str) -> Result<String, String> {
let result = String::from_utf8(output).map_err(|e| e.to_string())?;
Ok(result)
}

#[tauri::command]
pub fn open_browser(url: &str) -> Result<(), String> {
if let Err(e) = webbrowser::open_browser_with_options(
Browser::Default,
url,
BrowserOptions::new().with_suppress_output(false),
) {
return Err(format!("{e:?}"));
}

Ok(())
}
6 changes: 4 additions & 2 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use tracing_subscriber::fmt;

use crate::commands::{
change_user_state, connect_to_server, get_audio_devices, get_server_list, like_message, logout,
save_server, send_message, set_user_image, unzip_data_from_utf8, zip_data_to_utf8,
open_browser, save_server, send_message, set_user_image, unzip_data_from_utf8,
zip_data_to_utf8,
};

fn init_logging() {
Expand Down Expand Up @@ -68,7 +69,8 @@ fn main() {
change_user_state,
get_audio_devices,
zip_data_to_utf8,
unzip_data_from_utf8
unzip_data_from_utf8,
open_browser
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down
12 changes: 6 additions & 6 deletions src/components/ChannelViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ function ChannelViewer() {
}

function joinChannel(channelId: number) {
invoke('change_user_state', { userState: { channel_id: channelId }});
invoke('change_user_state', { userState: { channel_id: channelId } });
}

function displayUserInfo(user: UsersState): ReactNode {
return (
<Box sx={{display: 'flex'}}>
{user.self_mute ? (<MicOffIcon fontSize="small" />) : (<span />)}
{user.self_deaf ? (<VolumeOffIcon fontSize="small" />) : (<span />)}
{user.mute ? (<MicOffIcon color="error" fontSize="small" />) : (<span />)}
{user.deaf ? (<VolumeOffIcon color="error" fontSize="small" />) : (<span />)}
<Box sx={{ display: 'flex' }}>
{user.self_mute ? (<MicOffIcon color="error" fontSize="small" />) : (<span />)}
{user.self_deaf ? (<VolumeOffIcon color="error" fontSize="small" />) : (<span />)}
{user.mute ? (<MicOffIcon color="info" fontSize="small" />) : (<span />)}
{user.deaf ? (<VolumeOffIcon color="info" fontSize="small" />) : (<span />)}
</Box>
)
}
Expand Down
21 changes: 14 additions & 7 deletions src/components/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { RootState } from "../store/store";
import { useSelector } from "react-redux";
import "./styles/ChatMessage.css";
import UserInfoPopover from "./UserInfoPopover";
import MessageUIHelper from "../helper/MessageUIHelper";
import { m } from "@tauri-apps/api/dialog-15855a2f";


interface ChatMessageProps {
Expand All @@ -33,13 +35,18 @@ const parseMessage = (message: string | undefined) => {
.parseForImages()
.parseForLinks()
)
.build();
.buildString();

return (
<div>
{messageParser}
</div>
)
return messageParser;
}

return message;
}
const parseUI = (message: string | undefined) => {
if (message && message.includes('<')) {
let messageParser = new MessageUIHelper(message);

return messageParser.build();
}

return message;
Expand All @@ -66,7 +73,7 @@ const ChatMessage: React.FC<ChatMessageProps> = React.memo(({ message, messageId
userList.users.find(e => e.id === message.sender.user_id)
, [userList, message.sender.user_id]);

const parsedMessage = React.useMemo(() => parseMessage(message.message), [message.message]);
const parsedMessage = React.useMemo(() => parseUI(parseMessage(message.message)), [message.message]);
const date = React.useMemo(() => generateDate(message.timestamp), [message.timestamp]);

const deleteMessageEvent = React.useCallback(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/CurrentUserInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../store/store";
import { getBackgroundFromComment, getProfileImage } from "../helper/UserInfoHelper";
import { Avatar, Badge, Box, Container } from "@mui/material";
import { Avatar, Box, Typography } from "@mui/material";
import ChannelSearch from "./ChannelSearch";
import MicIcon from '@mui/icons-material/Mic';
import VolumeUpIcon from '@mui/icons-material/VolumeUp';
Expand Down Expand Up @@ -80,7 +80,7 @@ function CurrentUserInfo() {
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', padding: '2px 10px', flexDirection: 'column', alignItems: 'center', width: '100%', textShadow: '1px 1px #000' }}>
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
{userInfo.currentUser?.name ?? 'Unknown'}
<Typography sx={{ fontWeight: 'bold', textShadow: '2px 2px #000' }}>{userInfo.currentUser?.name ?? 'Unknown'}</Typography>
<Box onClick={muteToggleUser}>
{MicrophoneState}
</Box>
Expand Down
41 changes: 41 additions & 0 deletions src/components/LightBoxImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Backdrop, Box, Container, Link, Popover, } from "@mui/material";
import { UsersState } from "../store/features/users/userSlice";
import "./styles/UserInfo.css";
import "./styles/common.css"
import UserInfo from "./UserInfo";
import React, { useState } from "react";
import { openInBrowser } from "../helper/BrowserUtils";

interface LightBoxImageProps {
src: string;
}

function LightBoxImage(props: LightBoxImageProps) {
const [open, setOpen]: any = useState(false);

const handleClose = () => {
setOpen(false);
};

return (
<Box>
<img src={props.src} onClick={() => setOpen(true)} style={{ maxWidth: '100%', maxHeight: '600px', cursor: 'pointer' }} />
<Backdrop
sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1, backdropFilter: 'blur(5px)', padding: '50px 10px 10px 10px' }}
open={open}
onClick={handleClose}
>
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }}>
<Box sx={{ flexShrink: 0, display: 'contents' }}>
<img src={props.src} style={{ height: 'auto', width: 'auto', maxWidth: '100%', maxHeight: 'calc(100% - 2em)', objectFit: 'contain' }} />
</Box>
<Box sx={{ flexShrink: 1, textAlign: 'center'}}>
<Link href="#" color="inherit" underline="hover" onClick={() => openInBrowser(props.src)}>Open In Browser</Link>
</Box>
</Box>
</Backdrop>
</Box>
);
}

export default LightBoxImage;
119 changes: 119 additions & 0 deletions src/components/QuillEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useEffect, useRef, useState } from 'react';
import Quill, { TextChangeHandler } from 'quill';
import 'quill/dist/quill.snow.css';

interface QuillEditorProps {
theme?: 'bubble' | 'snow' | string;
placeholder?: string;
readOnly?: boolean;
bounds?: string;
debug?: 'error' | 'warn' | 'log' | boolean;
formats?: string[];
modules?: any;
scrollingContainer?: string | HTMLElement | undefined;
style?: React.CSSProperties;
onKeyDown?: (this: HTMLDivElement, ev: KeyboardEvent) => any;
onChange?: (content: string) => void;
onPaste?: (this: HTMLDivElement, ev: ClipboardEvent) => any;
multiline?: boolean;
value?: string;
}

export const QuillEditor: React.FC<QuillEditorProps> = ({
theme = 'snow',
placeholder = 'Compose an epic...',
readOnly = false,
bounds = 'document.body',
debug = 'warn',
formats = [],
modules = {},
scrollingContainer = undefined,
style = {},
onKeyDown,
onChange,
onPaste,
multiline = false,
value = ''
}) => {
const editorRef = useRef<HTMLDivElement | null>(null);
const quillRef = useRef(); // This will hold the Quill instance
const [toolbarVisible, setToolbarVisible] = useState(multiline);
let quill: Quill | undefined = undefined;
let textChangeListener: TextChangeHandler | undefined = undefined;

// This runs once on component mount
useEffect(() => {
if (editorRef.current) {
quill = new Quill(editorRef.current, {
theme,
placeholder,
readOnly,
bounds,
debug,
formats,
modules,
scrollingContainer
});

// Set the initial value
quill.root.innerHTML = value;

// Add event listeners
if (onKeyDown)
quill.root.addEventListener('keydown', onKeyDown);
if (onPaste)
quill.root.addEventListener('paste', onPaste);

if (onChange) {
textChangeListener = () => {
onChange(quill?.root.innerHTML || '');
}

quill.on('text-change', textChangeListener);
}


// Check for multiline to show/hide toolbar
if (multiline) {
quill.on('text-change', () => {
const text = quill?.getText();
const lineCount = (text?.match(/\n/g) || []).length;
setToolbarVisible(lineCount > 1);
});
}
}

// Clean up on component unmount
return () => {
if (quill) {
if (onKeyDown) {
quill.root.removeEventListener('keydown', onKeyDown);
}

if (textChangeListener) {
quill?.off('text-change', textChangeListener);
}

if (onPaste) {
quill.root.removeEventListener('paste', onPaste);
}
}
};
}, []); // Empty array means this effect runs once on mount and clean up on unmount


useEffect(() => {
if (editorRef.current) {


/*if (quillRef) {
quillRef.current = quill;
}*/
}
}, [theme, placeholder, readOnly, bounds, debug, formats, modules, scrollingContainer, quillRef]);

return <div className="quill-wrapper" style={style}>
{toolbarVisible && <div id="toolbar">/* Add toolbar contents here */</div>}
<div ref={editorRef} />
</div>;
};
2 changes: 2 additions & 0 deletions src/components/UserInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function UserInfo(props: UserInfoProps) {

let mutedText = props.userInfo?.mutedSince ? dayjs(props.userInfo?.mutedSince).fromNow() : '';
let deafenedText = props.userInfo?.deafenedSince ? dayjs(props.userInfo?.deafenedSince).fromNow() : '';
let joinedText = props.userInfo?.joinedSince ? dayjs(props.userInfo?.joinedSince).fromNow() : '';

function generateCardMedia() {
if (background) {
Expand Down Expand Up @@ -95,6 +96,7 @@ function UserInfo(props: UserInfoProps) {
</Box>
{showStatusBox("Muted", mutedText)}
{showStatusBox("Deafened", deafenedText)}
{showStatusBox("Joined", joinedText)}
</Box>
<Divider sx={{ margin: '10px 0' }} />
<Box className="user-info-text">
Expand Down
5 changes: 5 additions & 0 deletions src/helper/BrowserUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { invoke } from "@tauri-apps/api";

export function openInBrowser(url: string) {
invoke('open_browser', { url: url });
}
5 changes: 4 additions & 1 deletion src/helper/ChatMessage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { invoke } from "@tauri-apps/api";
import { TextMessage, addChatMessage } from "../store/features/users/chatMessageSlice";
import { TextMessage, addChatMessage, deleteAllMessages } from "../store/features/users/chatMessageSlice";
import { UsersState } from "../store/features/users/userSlice";
import MessageParser from "./MessageParser";
import { Dispatch } from "react";
import { AnyAction } from "@reduxjs/toolkit";

export class ChatMessageHandler {
deleteMessages() {
this.dispatch(deleteAllMessages());
}

constructor(private dispatch: Dispatch<AnyAction>, private setChatMessage: any) {

Expand Down
8 changes: 4 additions & 4 deletions src/helper/MessageParser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class DOMMessageParser {
private replacementUrl: LinkReplacement[] = [
{ regex: /https:\/\/store\.steampowered\.com\/app\/([0-9]+)\/?.*/, replacement: 'steam://advertise/$1', inline: false },
{ regex: /https:\/\/www\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/, replacement: 'https://www.youtube.com/embed/$1', inline: true },
{ regex: /https:\/\/www\.twitch\.tv\/videos\/([0-9]+)/, replacement: 'https://player.twitch.tv/?video=$1&parent='+(window.location.hostname), inline: true },
{ regex: /https:\/\/clips\.twitch\.tv\/([a-zA-Z0-9_-]+)/, replacement: 'https://clips.twitch.tv/embed?clip=$1&parent='+(window.location.hostname), inline: true },
{ regex: /https:\/\/www\.twitch\.tv\/([a-zA-Z0-9_-]+)/, replacement: 'https://player.twitch.tv/?channel=$1&parent='+(window.location.hostname), inline: true },
{ regex: /https:\/\/www\.twitch\.tv\/videos\/([0-9]+)/, replacement: 'https://player.twitch.tv/?video=$1&parent=' + (window.location.hostname), inline: true },
{ regex: /https:\/\/clips\.twitch\.tv\/([a-zA-Z0-9_-]+)/, replacement: 'https://clips.twitch.tv/embed?clip=$1&parent=' + (window.location.hostname), inline: true },
{ regex: /https:\/\/www\.twitch\.tv\/([a-zA-Z0-9_-]+)/, replacement: 'https://player.twitch.tv/?channel=$1&parent=' + (window.location.hostname), inline: true },
{ regex: /https:\/\/twitter\.com\/([a-zA-Z0-9_]+)\/status\/([0-9]+)/, replacement: 'https://twitframe.com/show?url=https://twitter.com/$1/status/$2', inline: true },
{ regex: /https:\/\/twitter\.com\/([a-zA-Z0-9_]+)/, replacement: 'https://twitframe.com/show?url=https://twitter.com/$1', inline: true },
{ regex: /https:\/\/giphy.com\/gifs\/.*-([a-zA-Z0-9_-]+)/, replacement: 'https://giphy.com/embed/$1', inline: true },
Expand Down Expand Up @@ -58,7 +58,7 @@ class DOMMessageParser {
}

build() {
return this.document.documentElement.innerHTML;
return this.document.body.innerHTML;
}
}

Expand Down
Loading

0 comments on commit 450703c

Please sign in to comment.