Merge branch 'develop' into internationalization2
This commit is contained in:
74
src/components/atoms/DatePicker.tsx
Normal file
74
src/components/atoms/DatePicker.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSocketStore } from '../../context/socket';
|
||||
import { Coordinates } from './canvas';
|
||||
|
||||
interface DatePickerProps {
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const DatePicker: React.FC<DatePickerProps> = ({ coordinates, selector, onClose }) => {
|
||||
const { socket } = useSocketStore();
|
||||
const [selectedDate, setSelectedDate] = useState<string>('');
|
||||
|
||||
const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedDate(e.target.value);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (socket && selectedDate) {
|
||||
socket.emit('input:date', {
|
||||
selector,
|
||||
value: selectedDate
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: `${coordinates.x}px`,
|
||||
top: `${coordinates.y}px`,
|
||||
zIndex: 1000,
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
||||
padding: '10px',
|
||||
borderRadius: '4px'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<input
|
||||
type="date"
|
||||
onChange={handleDateChange}
|
||||
value={selectedDate}
|
||||
className="p-2 border rounded"
|
||||
autoFocus
|
||||
/>
|
||||
<div className="flex justify-end space-x-2">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-3 py-1 text-sm text-gray-600 hover:text-gray-800 border rounded"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirm}
|
||||
disabled={!selectedDate}
|
||||
className={`px-3 py-1 text-sm rounded ${
|
||||
selectedDate
|
||||
? 'bg-blue-500 text-white hover:bg-blue-600'
|
||||
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePicker;
|
||||
74
src/components/atoms/DateTimeLocalPicker.tsx
Normal file
74
src/components/atoms/DateTimeLocalPicker.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSocketStore } from '../../context/socket';
|
||||
import { Coordinates } from './canvas';
|
||||
|
||||
interface DateTimeLocalPickerProps {
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const DateTimeLocalPicker: React.FC<DateTimeLocalPickerProps> = ({ coordinates, selector, onClose }) => {
|
||||
const { socket } = useSocketStore();
|
||||
const [selectedDateTime, setSelectedDateTime] = useState<string>('');
|
||||
|
||||
const handleDateTimeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedDateTime(e.target.value);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (socket && selectedDateTime) {
|
||||
socket.emit('input:datetime-local', {
|
||||
selector,
|
||||
value: selectedDateTime
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: `${coordinates.x}px`,
|
||||
top: `${coordinates.y}px`,
|
||||
zIndex: 1000,
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
||||
padding: '10px',
|
||||
borderRadius: '4px'
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<input
|
||||
type="datetime-local"
|
||||
onChange={handleDateTimeChange}
|
||||
value={selectedDateTime}
|
||||
className="p-2 border rounded"
|
||||
autoFocus
|
||||
/>
|
||||
<div className="flex justify-end space-x-2">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-3 py-1 text-sm text-gray-600 hover:text-gray-800 border rounded"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirm}
|
||||
disabled={!selectedDateTime}
|
||||
className={`px-3 py-1 text-sm rounded ${
|
||||
selectedDateTime
|
||||
? 'bg-blue-500 text-white hover:bg-blue-600'
|
||||
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateTimeLocalPicker;
|
||||
85
src/components/atoms/Dropdown.tsx
Normal file
85
src/components/atoms/Dropdown.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSocketStore } from '../../context/socket';
|
||||
import { Coordinates } from './canvas';
|
||||
|
||||
interface DropdownProps {
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
options: Array<{
|
||||
value: string;
|
||||
text: string;
|
||||
disabled: boolean;
|
||||
selected: boolean;
|
||||
}>;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const Dropdown = ({ coordinates, selector, options, onClose }: DropdownProps) => {
|
||||
const { socket } = useSocketStore();
|
||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||
|
||||
const handleSelect = (value: string) => {
|
||||
if (socket) {
|
||||
socket.emit('input:dropdown', { selector, value });
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
|
||||
const containerStyle: React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
left: coordinates.x,
|
||||
top: coordinates.y,
|
||||
zIndex: 1000,
|
||||
width: '200px',
|
||||
backgroundColor: 'white',
|
||||
border: '1px solid rgb(169, 169, 169)',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.15)',
|
||||
};
|
||||
|
||||
const scrollContainerStyle: React.CSSProperties = {
|
||||
maxHeight: '180px',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
};
|
||||
|
||||
const getOptionStyle = (option: any, index: number): React.CSSProperties => ({
|
||||
fontSize: '13.333px',
|
||||
lineHeight: '18px',
|
||||
padding: '0 3px',
|
||||
cursor: option.disabled ? 'default' : 'default',
|
||||
backgroundColor: hoveredIndex === index ? '#0078D7' :
|
||||
option.selected ? '#0078D7' :
|
||||
option.disabled ? '#f8f8f8' : 'white',
|
||||
color: (hoveredIndex === index || option.selected) ? 'white' :
|
||||
option.disabled ? '#a0a0a0' : 'black',
|
||||
userSelect: 'none',
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
style={containerStyle}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<div style={scrollContainerStyle}>
|
||||
{options.map((option, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={getOptionStyle(option, index)}
|
||||
onMouseEnter={() => !option.disabled && setHoveredIndex(index)}
|
||||
onMouseLeave={() => setHoveredIndex(null)}
|
||||
onClick={() => !option.disabled && handleSelect(option.value)}
|
||||
>
|
||||
{option.text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dropdown;
|
||||
130
src/components/atoms/TimePicker.tsx
Normal file
130
src/components/atoms/TimePicker.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSocketStore } from '../../context/socket';
|
||||
import { Coordinates } from './canvas';
|
||||
|
||||
interface TimePickerProps {
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const TimePicker = ({ coordinates, selector, onClose }: TimePickerProps) => {
|
||||
const { socket } = useSocketStore();
|
||||
const [hoveredHour, setHoveredHour] = useState<number | null>(null);
|
||||
const [hoveredMinute, setHoveredMinute] = useState<number | null>(null);
|
||||
const [selectedHour, setSelectedHour] = useState<number | null>(null);
|
||||
const [selectedMinute, setSelectedMinute] = useState<number | null>(null);
|
||||
|
||||
const handleHourSelect = (hour: number) => {
|
||||
setSelectedHour(hour);
|
||||
// If minute is already selected, complete the selection
|
||||
if (selectedMinute !== null) {
|
||||
const formattedHour = hour.toString().padStart(2, '0');
|
||||
const formattedMinute = selectedMinute.toString().padStart(2, '0');
|
||||
if (socket) {
|
||||
socket.emit('input:time', {
|
||||
selector,
|
||||
value: `${formattedHour}:${formattedMinute}`
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const handleMinuteSelect = (minute: number) => {
|
||||
setSelectedMinute(minute);
|
||||
// If hour is already selected, complete the selection
|
||||
if (selectedHour !== null) {
|
||||
const formattedHour = selectedHour.toString().padStart(2, '0');
|
||||
const formattedMinute = minute.toString().padStart(2, '0');
|
||||
if (socket) {
|
||||
socket.emit('input:time', {
|
||||
selector,
|
||||
value: `${formattedHour}:${formattedMinute}`
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const containerStyle: React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
left: coordinates.x,
|
||||
top: coordinates.y,
|
||||
zIndex: 1000,
|
||||
display: 'flex',
|
||||
backgroundColor: 'white',
|
||||
border: '1px solid rgb(169, 169, 169)',
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.15)',
|
||||
};
|
||||
|
||||
const columnStyle: React.CSSProperties = {
|
||||
width: '60px',
|
||||
maxHeight: '180px',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
borderRight: '1px solid rgb(169, 169, 169)',
|
||||
};
|
||||
|
||||
const getOptionStyle = (value: number, isHour: boolean): React.CSSProperties => {
|
||||
const isHovered = isHour ? hoveredHour === value : hoveredMinute === value;
|
||||
const isSelected = isHour ? selectedHour === value : selectedMinute === value;
|
||||
|
||||
return {
|
||||
fontSize: '13.333px',
|
||||
lineHeight: '18px',
|
||||
padding: '0 3px',
|
||||
cursor: 'default',
|
||||
backgroundColor: isSelected ? '#0078D7' : isHovered ? '#0078D7' : 'white',
|
||||
color: (isSelected || isHovered) ? 'white' : 'black',
|
||||
userSelect: 'none',
|
||||
};
|
||||
};
|
||||
|
||||
const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||
const minutes = Array.from({ length: 60 }, (_, i) => i);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
style={containerStyle}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{/* Hours column */}
|
||||
<div style={columnStyle}>
|
||||
{hours.map((hour) => (
|
||||
<div
|
||||
key={hour}
|
||||
style={getOptionStyle(hour, true)}
|
||||
onMouseEnter={() => setHoveredHour(hour)}
|
||||
onMouseLeave={() => setHoveredHour(null)}
|
||||
onClick={() => handleHourSelect(hour)}
|
||||
>
|
||||
{hour.toString().padStart(2, '0')}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Minutes column */}
|
||||
<div style={{...columnStyle, borderRight: 'none'}}>
|
||||
{minutes.map((minute) => (
|
||||
<div
|
||||
key={minute}
|
||||
style={getOptionStyle(minute, false)}
|
||||
onMouseEnter={() => setHoveredMinute(minute)}
|
||||
onMouseLeave={() => setHoveredMinute(null)}
|
||||
onClick={() => handleMinuteSelect(minute)}
|
||||
>
|
||||
{minute.toString().padStart(2, '0')}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimePicker;
|
||||
@@ -3,6 +3,10 @@ import { useSocketStore } from '../../context/socket';
|
||||
import { getMappedCoordinates } from "../../helpers/inputHelpers";
|
||||
import { useGlobalInfoStore } from "../../context/globalInfo";
|
||||
import { useActionContext } from '../../context/browserActions';
|
||||
import DatePicker from './DatePicker';
|
||||
import Dropdown from './Dropdown';
|
||||
import TimePicker from './TimePicker';
|
||||
import DateTimeLocalPicker from './DateTimeLocalPicker';
|
||||
|
||||
interface CreateRefCallback {
|
||||
(ref: React.RefObject<HTMLCanvasElement>): void;
|
||||
@@ -31,6 +35,32 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => {
|
||||
const getTextRef = useRef(getText);
|
||||
const getListRef = useRef(getList);
|
||||
|
||||
const [datePickerInfo, setDatePickerInfo] = React.useState<{
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
} | null>(null);
|
||||
|
||||
const [dropdownInfo, setDropdownInfo] = React.useState<{
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
options: Array<{
|
||||
value: string;
|
||||
text: string;
|
||||
disabled: boolean;
|
||||
selected: boolean;
|
||||
}>;
|
||||
} | null>(null);
|
||||
|
||||
const [timePickerInfo, setTimePickerInfo] = React.useState<{
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
} | null>(null);
|
||||
|
||||
const [dateTimeLocalInfo, setDateTimeLocalInfo] = React.useState<{
|
||||
coordinates: Coordinates;
|
||||
selector: string;
|
||||
} | null>(null);
|
||||
|
||||
const notifyLastAction = (action: string) => {
|
||||
if (lastAction !== action) {
|
||||
setLastAction(action);
|
||||
@@ -44,6 +74,42 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => {
|
||||
getListRef.current = getList;
|
||||
}, [getText, getList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
socket.on('showDatePicker', (info: {coordinates: Coordinates, selector: string}) => {
|
||||
setDatePickerInfo(info);
|
||||
});
|
||||
|
||||
socket.on('showDropdown', (info: {
|
||||
coordinates: Coordinates,
|
||||
selector: string,
|
||||
options: Array<{
|
||||
value: string;
|
||||
text: string;
|
||||
disabled: boolean;
|
||||
selected: boolean;
|
||||
}>;
|
||||
}) => {
|
||||
setDropdownInfo(info);
|
||||
});
|
||||
|
||||
socket.on('showTimePicker', (info: {coordinates: Coordinates, selector: string}) => {
|
||||
setTimePickerInfo(info);
|
||||
});
|
||||
|
||||
socket.on('showDateTimePicker', (info: {coordinates: Coordinates, selector: string}) => {
|
||||
setDateTimeLocalInfo(info);
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off('showDatePicker');
|
||||
socket.off('showDropdown');
|
||||
socket.off('showTimePicker');
|
||||
socket.off('showDateTimePicker');
|
||||
};
|
||||
}
|
||||
}, [socket]);
|
||||
|
||||
const onMouseEvent = useCallback((event: MouseEvent) => {
|
||||
if (socket && canvasRef.current) {
|
||||
// Get the canvas bounding rectangle
|
||||
@@ -146,6 +212,35 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => {
|
||||
width={900}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
{datePickerInfo && (
|
||||
<DatePicker
|
||||
coordinates={datePickerInfo.coordinates}
|
||||
selector={datePickerInfo.selector}
|
||||
onClose={() => setDatePickerInfo(null)}
|
||||
/>
|
||||
)}
|
||||
{dropdownInfo && (
|
||||
<Dropdown
|
||||
coordinates={dropdownInfo.coordinates}
|
||||
selector={dropdownInfo.selector}
|
||||
options={dropdownInfo.options}
|
||||
onClose={() => setDropdownInfo(null)}
|
||||
/>
|
||||
)}
|
||||
{timePickerInfo && (
|
||||
<TimePicker
|
||||
coordinates={timePickerInfo.coordinates}
|
||||
selector={timePickerInfo.selector}
|
||||
onClose={() => setTimePickerInfo(null)}
|
||||
/>
|
||||
)}
|
||||
{dateTimeLocalInfo && (
|
||||
<DateTimeLocalPicker
|
||||
coordinates={dateTimeLocalInfo.coordinates}
|
||||
selector={dateTimeLocalInfo.selector}
|
||||
onClose={() => setDateTimeLocalInfo(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import { useTranslation } from "react-i18next"; // Import useTranslation hook
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
@@ -62,7 +50,7 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
return version;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch latest version:", error);
|
||||
return null; // Handle errors gracefully
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,297 +100,207 @@ export const NavBar: React.FC<NavBarProps> = ({
|
||||
};
|
||||
|
||||
const changeLanguage = (lang: string) => {
|
||||
i18n.changeLanguage(lang); // Change language dynamically
|
||||
localStorage.setItem("language", lang); // Persist language to localStorage
|
||||
i18n.changeLanguage(lang);
|
||||
localStorage.setItem("language", lang);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const checkForUpdates = async () => {
|
||||
const latestVersion = await fetchLatestVersion();
|
||||
setLatestVersion(latestVersion); // Set the latest version state
|
||||
setLatestVersion(latestVersion);
|
||||
if (latestVersion && latestVersion !== currentVersion) {
|
||||
setIsUpdateAvailable(true); // Show a notification or highlight the "Upgrade" button
|
||||
setIsUpdateAvailable(true);
|
||||
}
|
||||
};
|
||||
checkForUpdates();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
<NavBarWrapper>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={MaxunLogo}
|
||||
width={45}
|
||||
height={40}
|
||||
style={{ borderRadius: "5px", margin: "5px 0px 5px 15px" }}
|
||||
/>
|
||||
<div style={{ padding: "11px" }}>
|
||||
<ProjectName>Maxun</ProjectName>
|
||||
<NavBarWrapper>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
}}>
|
||||
<img src={MaxunLogo} width={45} height={40} style={{ borderRadius: '5px', margin: '5px 0px 5px 15px' }} />
|
||||
<div style={{ padding: '11px' }}><ProjectName>Maxun</ProjectName></div>
|
||||
<Chip
|
||||
label={`${currentVersion}`}
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
sx={{ marginTop: '10px' }}
|
||||
/>
|
||||
</div>
|
||||
<Chip
|
||||
label="beta"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
sx={{ marginTop: "10px" }}
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
{user ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
{!isRecording ? (
|
||||
<>
|
||||
<IconButton
|
||||
component="a"
|
||||
href="https://discord.gg/5GbPjBUkws"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
borderRadius: "5px",
|
||||
padding: "8px",
|
||||
marginRight: "30px",
|
||||
}}
|
||||
>
|
||||
<DiscordIcon sx={{ marginRight: "5px" }} />
|
||||
</IconButton>
|
||||
<iframe
|
||||
src="https://ghbtns.com/github-btn.html?user=getmaxun&repo=maxun&type=star&count=true&size=large"
|
||||
frameBorder="0"
|
||||
scrolling="0"
|
||||
width="170"
|
||||
height="30"
|
||||
title="GitHub"
|
||||
></iframe>
|
||||
<IconButton
|
||||
onClick={handleMenuOpen}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
borderRadius: "5px",
|
||||
padding: "8px",
|
||||
marginRight: "10px",
|
||||
"&:hover": { backgroundColor: "white", color: "#ff00c3" },
|
||||
}}
|
||||
>
|
||||
<AccountCircle sx={{ marginRight: "5px" }} />
|
||||
<Typography variant="body1">{user.email}</Typography>
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleMenuClose();
|
||||
logout();
|
||||
}}
|
||||
>
|
||||
<Logout sx={{ marginRight: "5px" }} /> {t("logout")}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={goToMainMenu}
|
||||
sx={{
|
||||
borderRadius: "5px",
|
||||
padding: "8px",
|
||||
background: "red",
|
||||
color: "white",
|
||||
marginRight: "10px",
|
||||
"&:hover": { color: "white", backgroundColor: "red" },
|
||||
}}
|
||||
>
|
||||
<Clear sx={{ marginRight: "5px" }} />
|
||||
{t("discard")}
|
||||
</IconButton>
|
||||
<SaveRecording fileName={recordingName} />
|
||||
</>
|
||||
)}
|
||||
<IconButton
|
||||
onClick={handleLangMenuOpen}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
borderRadius: "5px",
|
||||
padding: "8px",
|
||||
marginRight: "10px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1">
|
||||
<Language />
|
||||
</Typography>
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={langAnchorEl}
|
||||
open={Boolean(langAnchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("en");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
English
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("es");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
Español
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("ja");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
日本語
|
||||
</MenuItem>
|
||||
{/* <MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("ar");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
العربية
|
||||
</MenuItem> */}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("zh");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
中文
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("de");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
Deutsch
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
) : (
|
||||
<><IconButton
|
||||
onClick={handleLangMenuOpen}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
borderRadius: "5px",
|
||||
padding: "8px",
|
||||
marginRight: "10px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1">{t("language")}</Typography>
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={langAnchorEl}
|
||||
open={Boolean(langAnchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "right",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("en");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
English
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("es");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
Español
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("ja");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
日本語
|
||||
</MenuItem>
|
||||
{/* <MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("ar");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
العربية
|
||||
</MenuItem> */}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("zh");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
中文
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
changeLanguage("de");
|
||||
handleMenuClose();
|
||||
}}
|
||||
>
|
||||
Deutsch
|
||||
</MenuItem>
|
||||
</Menu></>
|
||||
)}
|
||||
|
||||
|
||||
</NavBarWrapper>
|
||||
|
||||
|
||||
{
|
||||
user ? (
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
|
||||
{!isRecording ? (
|
||||
<>
|
||||
<Button variant="outlined" onClick={handleUpdateOpen} sx={{
|
||||
marginRight: '40px',
|
||||
color: "#00000099",
|
||||
border: "#00000099 1px solid",
|
||||
'&:hover': { color: '#ff00c3', border: '#ff00c3 1px solid' }
|
||||
}}>
|
||||
<Update sx={{ marginRight: '5px' }} /> Upgrade Maxun
|
||||
</Button>
|
||||
<Modal open={open} onClose={handleUpdateClose}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 500,
|
||||
bgcolor: "background.paper",
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
{latestVersion === null ? (
|
||||
<Typography>Checking for updates...</Typography>
|
||||
) : currentVersion === latestVersion ? (
|
||||
<Typography variant="h6" textAlign="center">
|
||||
🎉 You're up to date!
|
||||
</Typography>
|
||||
) : (
|
||||
<>
|
||||
<Typography variant="body1" textAlign="left" sx={{ marginLeft: '30px' }}>
|
||||
A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features!
|
||||
<br />
|
||||
View all the new updates
|
||||
<a href="https://github.com/getmaxun/maxun/releases/" target="_blank" style={{ textDecoration: 'none' }}>{' '}here.</a>
|
||||
</Typography>
|
||||
<Tabs
|
||||
value={tab}
|
||||
onChange={handleUpdateTabChange}
|
||||
sx={{ marginTop: 2, marginBottom: 2 }}
|
||||
centered
|
||||
>
|
||||
<Tab label="Manual Setup Upgrade" />
|
||||
<Tab label="Docker Compose Setup Upgrade" />
|
||||
</Tabs>
|
||||
{tab === 0 && (
|
||||
<Box sx={{ marginLeft: '30px', background: '#cfd0d1', padding: 1, borderRadius: 3 }}>
|
||||
<code style={{ color: 'black' }}>
|
||||
<p>Run the commands below</p>
|
||||
# cd to project directory (eg: maxun)
|
||||
<br />
|
||||
cd maxun
|
||||
<br />
|
||||
<br />
|
||||
# pull latest changes
|
||||
<br />
|
||||
git pull origin master
|
||||
<br />
|
||||
<br />
|
||||
# install dependencies
|
||||
<br />
|
||||
npm install
|
||||
<br />
|
||||
<br />
|
||||
# start maxun
|
||||
<br />
|
||||
npm run start
|
||||
</code>
|
||||
</Box>
|
||||
)}
|
||||
{tab === 1 && (
|
||||
<Box sx={{ marginLeft: '30px', background: '#cfd0d1', padding: 1, borderRadius: 3 }}>
|
||||
<code style={{ color: 'black' }}>
|
||||
<p>Run the commands below</p>
|
||||
# cd to project directory (eg: maxun)
|
||||
<br />
|
||||
cd maxun
|
||||
<br />
|
||||
<br />
|
||||
# stop the working containers
|
||||
<br />
|
||||
docker-compose down
|
||||
<br />
|
||||
<br />
|
||||
# pull latest docker images
|
||||
<br />
|
||||
docker-compose pull
|
||||
<br />
|
||||
<br />
|
||||
# start maxun
|
||||
<br />
|
||||
docker-compose up -d
|
||||
</code>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Modal>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=getmaxun&repo=maxun&type=star&count=true&size=large" frameBorder="0" scrolling="0" width="170" height="30" title="GitHub"></iframe>
|
||||
<IconButton onClick={handleMenuOpen} sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
borderRadius: '5px',
|
||||
padding: '8px',
|
||||
marginRight: '10px',
|
||||
'&:hover': { backgroundColor: 'white', color: '#ff00c3' }
|
||||
}}>
|
||||
<AccountCircle sx={{ marginRight: '5px' }} />
|
||||
<Typography variant="body1">{user.email}</Typography>
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
PaperProps={{ sx: { width: '180px' } }}
|
||||
>
|
||||
<MenuItem onClick={() => { handleMenuClose(); logout(); }}>
|
||||
<Logout sx={{ marginRight: '5px' }} /> Logout
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
window.open('https://discord.gg/5GbPjBUkws', '_blank');
|
||||
}}>
|
||||
<DiscordIcon sx={{ marginRight: '5px' }} /> Discord
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
window.open('https://www.youtube.com/@MaxunOSS/videos?ref=app', '_blank');
|
||||
}}>
|
||||
<YouTube sx={{ marginRight: '5px' }} /> YouTube
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
window.open('https://x.com/maxun_io?ref=app', '_blank');
|
||||
}}>
|
||||
<X sx={{ marginRight: '5px' }} /> Twiiter (X)
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<IconButton onClick={goToMainMenu} sx={{
|
||||
borderRadius: '5px',
|
||||
padding: '8px',
|
||||
background: 'red',
|
||||
color: 'white',
|
||||
marginRight: '10px',
|
||||
'&:hover': { color: 'white', backgroundColor: 'red' }
|
||||
}}>
|
||||
<Clear sx={{ marginRight: '5px' }} />
|
||||
Discard
|
||||
</IconButton>
|
||||
<SaveRecording fileName={recordingName} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : ""
|
||||
}
|
||||
</NavBarWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user