refactor: Remove unused state and simplify sensitive info handling in modals
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { X, Copy, Tv2, Eye, EyeOff } from 'lucide-react';
|
||||
import { useContext, useState } from 'react';
|
||||
import { X, Copy, Tv2 } from 'lucide-react';
|
||||
import { useContext } from 'react';
|
||||
import { ToastContext } from './notifications/ToastContext';
|
||||
|
||||
interface TvPlaylistModalProps {
|
||||
@@ -11,7 +11,6 @@ interface TvPlaylistModalProps {
|
||||
function TvPlaylistModal({ isOpen, onClose, isAdmin = false }: TvPlaylistModalProps) {
|
||||
const { addToast } = useContext(ToastContext);
|
||||
const playlistUrl = `${import.meta.env.VITE_BACKEND_URL || window.location.origin}/api/channels/playlist`;
|
||||
const [showHiddenInfo, setShowHiddenInfo] = useState(false);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
@@ -73,26 +72,13 @@ function TvPlaylistModal({ isOpen, onClose, isAdmin = false }: TvPlaylistModalPr
|
||||
<div className="mt-6 border-t border-gray-700 pt-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<h3 className="text-lg font-medium">Admin Information</h3>
|
||||
<button
|
||||
onClick={() => setShowHiddenInfo(!showHiddenInfo)}
|
||||
className="flex items-center space-x-1 text-blue-400 hover:text-blue-300"
|
||||
>
|
||||
{showHiddenInfo ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||
<span>{showHiddenInfo ? 'Hide' : 'Show'} sensitive info</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-900 rounded-lg p-4">
|
||||
<p className="text-sm text-gray-300">
|
||||
{showHiddenInfo ? (
|
||||
<>
|
||||
This playlist contains the actual stream URLs. You can share a link to the
|
||||
application with non-admin users, and they will be able to watch the streams
|
||||
without seeing the actual stream URLs.
|
||||
</>
|
||||
) : (
|
||||
'Click "Show sensitive info" to view additional information about the playlist.'
|
||||
)}
|
||||
This playlist contains all stream URLs. You can share a link to the
|
||||
application with other users, and they will be able to watch the streams
|
||||
together with you.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { Plus, Trash2, X, Eye, EyeOff } from 'lucide-react';
|
||||
import { Plus, Trash2, X } from 'lucide-react';
|
||||
import socketService from '../../services/SocketService';
|
||||
import { CustomHeader, Channel, ChannelMode } from '../../types';
|
||||
import CustomHeaderInput from './CustomHeaderInput';
|
||||
@@ -16,7 +16,6 @@ function ChannelModal({ onClose, channel, isAdmin = false }: ChannelModalProps)
|
||||
const [type, setType] = useState<'channel' | 'playlist'>('playlist');
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
const [inputMethod, setInputMethod] = useState<'url' | 'text'>('url');
|
||||
const [showSensitiveInfo, setShowSensitiveInfo] = useState(false);
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [url, setUrl] = useState('');
|
||||
@@ -173,32 +172,6 @@ function ChannelModal({ onClose, channel, isAdmin = false }: ChannelModalProps)
|
||||
onClose();
|
||||
};
|
||||
|
||||
// Obfuscate part of the URL to hide sensitive information
|
||||
const getObfuscatedUrl = (fullUrl: string) => {
|
||||
if (!fullUrl || showSensitiveInfo) return fullUrl;
|
||||
|
||||
try {
|
||||
const url = new URL(fullUrl);
|
||||
// Hide username and password in URL if present
|
||||
if (url.username || url.password) {
|
||||
return fullUrl.replace(/\/\/([^:@]+:[^@]+@)/g, '//***:***@');
|
||||
}
|
||||
|
||||
// Hide tokens or API keys in query params
|
||||
if (url.search && (url.search.includes('token') || url.search.includes('key') || url.search.includes('password') || url.search.includes('auth'))) {
|
||||
return `${url.origin}${url.pathname}?***hidden***`;
|
||||
}
|
||||
|
||||
return fullUrl;
|
||||
} catch {
|
||||
// If URL is malformed, just return a partially obfuscated string
|
||||
if (fullUrl.length > 20) {
|
||||
return fullUrl.substring(0, 10) + '...' + fullUrl.substring(fullUrl.length - 10);
|
||||
}
|
||||
return fullUrl;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4">
|
||||
<div className="bg-gray-800 rounded-lg w-full max-w-md">
|
||||
@@ -255,25 +228,15 @@ function ChannelModal({ onClose, channel, isAdmin = false }: ChannelModalProps)
|
||||
<label htmlFor="url" className="block text-sm font-medium">
|
||||
Stream URL
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowSensitiveInfo(!showSensitiveInfo)}
|
||||
className="flex items-center text-xs text-blue-400 hover:text-blue-300"
|
||||
title="Toggle URL visibility for privacy"
|
||||
>
|
||||
{showSensitiveInfo ? <EyeOff className="w-3 h-3 mr-1" /> : <Eye className="w-3 h-3 mr-1" />}
|
||||
{showSensitiveInfo ? 'Hide' : 'Show'} URL
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
type="url"
|
||||
id="url"
|
||||
value={showSensitiveInfo ? url : getObfuscatedUrl(url)}
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
className="w-full bg-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="Enter stream URL"
|
||||
required
|
||||
readOnly={!showSensitiveInfo && url.length > 0}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -372,25 +335,15 @@ function ChannelModal({ onClose, channel, isAdmin = false }: ChannelModalProps)
|
||||
M3U Text
|
||||
</button>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowSensitiveInfo(!showSensitiveInfo)}
|
||||
className="flex items-center text-xs text-blue-400 hover:text-blue-300"
|
||||
title="Toggle URL visibility for privacy"
|
||||
>
|
||||
{showSensitiveInfo ? <EyeOff className="w-3 h-3 mr-1" /> : <Eye className="w-3 h-3 mr-1" />}
|
||||
{showSensitiveInfo ? 'Hide' : 'Show'} URL
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
type="url"
|
||||
id="playlistUrl"
|
||||
value={showSensitiveInfo ? playlistUrl : getObfuscatedUrl(playlistUrl)}
|
||||
value={playlistUrl}
|
||||
onChange={(e) => setPlaylistUrl(e.target.value)}
|
||||
className="w-full bg-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="Enter M3U playlist URL"
|
||||
required={inputMethod === 'url'}
|
||||
readOnly={!showSensitiveInfo && playlistUrl.length > 0}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -413,25 +366,15 @@ function ChannelModal({ onClose, channel, isAdmin = false }: ChannelModalProps)
|
||||
M3U Text
|
||||
</button>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowSensitiveInfo(!showSensitiveInfo)}
|
||||
className="flex items-center text-xs text-blue-400 hover:text-blue-300"
|
||||
title="Toggle content visibility for privacy"
|
||||
>
|
||||
{showSensitiveInfo ? <EyeOff className="w-3 h-3 mr-1" /> : <Eye className="w-3 h-3 mr-1" />}
|
||||
{showSensitiveInfo ? 'Hide' : 'Show'} content
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
id="playlistText"
|
||||
value={showSensitiveInfo ? playlistText : (playlistText ? "#EXTM3U\n# Content hidden for privacy\n# Click 'Show content' to view or edit" : "")}
|
||||
value={playlistText}
|
||||
onChange={(e) => setPlaylistText(e.target.value)}
|
||||
className="w-full bg-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 min-h-[200px] scroll-container overflow-y-auto"
|
||||
placeholder="#EXTM3U..."
|
||||
required={inputMethod === 'text'}
|
||||
style={{ resize: 'none' }}
|
||||
readOnly={!showSensitiveInfo && playlistText.length > 0}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -506,15 +449,6 @@ function ChannelModal({ onClose, channel, isAdmin = false }: ChannelModalProps)
|
||||
Custom Headers
|
||||
</label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowSensitiveInfo(!showSensitiveInfo)}
|
||||
className="flex items-center text-xs text-blue-400 hover:text-blue-300"
|
||||
title="Toggle headers visibility for privacy"
|
||||
>
|
||||
{showSensitiveInfo ? <EyeOff className="w-3 h-3 mr-1" /> : <Eye className="w-3 h-3 mr-1" />}
|
||||
{showSensitiveInfo ? 'Hide' : 'Show'} headers
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addHeader}
|
||||
@@ -529,13 +463,9 @@ function ChannelModal({ onClose, channel, isAdmin = false }: ChannelModalProps)
|
||||
{headers && headers.map((header, index) => (
|
||||
<div key={index} className="flex items-center space-x-2">
|
||||
<CustomHeaderInput
|
||||
header={{
|
||||
key: header.key,
|
||||
value: showSensitiveInfo ? header.value : "***hidden***"
|
||||
}}
|
||||
header={header}
|
||||
onKeyChange={(value) => updateHeader(index, 'key', value)}
|
||||
onValueChange={(value) => updateHeader(index, 'value', value)}
|
||||
readOnly={!showSensitiveInfo && header.value.length > 0}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -4,10 +4,9 @@ interface CustomHeaderInputProps {
|
||||
header: CustomHeader;
|
||||
onKeyChange: (value: string) => void;
|
||||
onValueChange: (value: string) => void;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
function CustomHeaderInput({ header, onKeyChange, onValueChange, readOnly = false }: CustomHeaderInputProps) {
|
||||
function CustomHeaderInput({ header, onKeyChange, onValueChange }: CustomHeaderInputProps) {
|
||||
return (
|
||||
<div className="flex-1 grid grid-cols-2 gap-2">
|
||||
<input
|
||||
@@ -15,16 +14,14 @@ function CustomHeaderInput({ header, onKeyChange, onValueChange, readOnly = fals
|
||||
value={header.key}
|
||||
onChange={(e) => onKeyChange(e.target.value)}
|
||||
placeholder="Header name"
|
||||
className={`bg-gray-700 rounded-lg px-3 py-1.5 text-sm focus:outline-none ${!readOnly ? 'focus:ring-2 focus:ring-blue-500' : ''}`}
|
||||
readOnly={readOnly}
|
||||
className="bg-gray-700 rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={header.value}
|
||||
onChange={(e) => onValueChange(e.target.value)}
|
||||
placeholder="Header value"
|
||||
className={`bg-gray-700 rounded-lg px-3 py-1.5 text-sm focus:outline-none ${!readOnly ? 'focus:ring-2 focus:ring-blue-500' : ''}`}
|
||||
readOnly={readOnly}
|
||||
className="bg-gray-700 rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user