Compare commits
No commits in common. "feature/ayu-like-settings" and "feature/ayu-like-ad-filter" have entirely different histories.
feature/ay
...
feature/ay
@ -23,7 +23,6 @@ import SettingsCustomEmoji from './SettingsCustomEmoji';
|
||||
import SettingsDataStorage from './SettingsDataStorage';
|
||||
import SettingsDoNotTranslate from './SettingsDoNotTranslate';
|
||||
import SettingsEditProfile from './SettingsEditProfile';
|
||||
import SettingsAyuLike from './SettingsAyuLike';
|
||||
import SettingsExperimental from './SettingsExperimental';
|
||||
import SettingsGeneral from './SettingsGeneral';
|
||||
import SettingsGeneralBackground from './SettingsGeneralBackground';
|
||||
@ -308,10 +307,6 @@ const Settings: FC<OwnProps> = ({
|
||||
return (
|
||||
<SettingsStickers isActive={isScreenActive} onReset={handleReset} />
|
||||
);
|
||||
case SettingsScreens.AyuLikeSettings:
|
||||
return (
|
||||
<SettingsAyuLike isActive={isScreenActive} onReset={handleReset} />
|
||||
);
|
||||
case SettingsScreens.Experimental:
|
||||
return (
|
||||
<SettingsExperimental isActive={isScreenActive} onReset={handleReset} />
|
||||
|
||||
@ -1,159 +0,0 @@
|
||||
import { memo, useRef } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { MessageFilterRule } from '../../../global/types/sharedState';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import download from '../../../util/download';
|
||||
import { exportToAyuGram, importFromAyuGram } from '../../../util/ayuLike/messageFilters';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
import Island from '../../gili/layout/Island';
|
||||
import Checkbox from '../../ui/Checkbox';
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
|
||||
type OwnProps = {
|
||||
isActive?: boolean;
|
||||
onReset: () => void;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
hideSponsoredMessages: boolean;
|
||||
messageFilters: MessageFilterRule[];
|
||||
};
|
||||
|
||||
const EXPORT_FILENAME = 'ayu-filters.json';
|
||||
|
||||
const SettingsAyuLike = ({
|
||||
isActive,
|
||||
hideSponsoredMessages,
|
||||
messageFilters,
|
||||
onReset,
|
||||
}: OwnProps & StateProps) => {
|
||||
const { setSharedSettingOption, showNotification } = getActions();
|
||||
const fileInputRef = useRef<HTMLInputElement>();
|
||||
const [isClearConfirmOpen, openClearConfirm, closeClearConfirm] = useFlag(false);
|
||||
|
||||
useHistoryBack({ isActive, onBack: onReset });
|
||||
|
||||
const handleExport = useLastCallback(() => {
|
||||
const json = exportToAyuGram(messageFilters);
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
download(url, EXPORT_FILENAME);
|
||||
setTimeout(() => URL.revokeObjectURL(url), 10_000);
|
||||
});
|
||||
|
||||
const handleImportClick = useLastCallback(() => {
|
||||
fileInputRef.current?.click();
|
||||
});
|
||||
|
||||
const handleFileChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const imported = importFromAyuGram(JSON.parse(reader.result as string));
|
||||
if (imported.length === 0) {
|
||||
showNotification({ message: 'No valid filter rules found in file' });
|
||||
} else {
|
||||
setSharedSettingOption({
|
||||
ayuLike: { hideSponsoredMessages, messageFilters: imported },
|
||||
});
|
||||
showNotification({ message: `Imported ${imported.length} filter rules` });
|
||||
}
|
||||
} catch {
|
||||
showNotification({ message: 'Invalid file: not a valid AyuGram filter export' });
|
||||
}
|
||||
// Reset so the same file can be re-imported
|
||||
e.target.value = '';
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
|
||||
const handleClearConfirmed = useLastCallback(() => {
|
||||
setSharedSettingOption({
|
||||
ayuLike: { hideSponsoredMessages, messageFilters: [] },
|
||||
});
|
||||
closeClearConfirm();
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="settings-content custom-scroll">
|
||||
<div className="settings-content-header no-border">
|
||||
<p className="settings-item-description pt-3" dir="auto">
|
||||
Local message filters and ad settings. Changes take effect immediately and are stored locally.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Island>
|
||||
<Checkbox
|
||||
label="Hide Sponsored Messages"
|
||||
checked={hideSponsoredMessages}
|
||||
onCheck={(checked) => setSharedSettingOption({
|
||||
ayuLike: { hideSponsoredMessages: checked, messageFilters },
|
||||
})}
|
||||
/>
|
||||
</Island>
|
||||
|
||||
<Island>
|
||||
<ListItem
|
||||
icon="download"
|
||||
onClick={handleImportClick}
|
||||
>
|
||||
<div className="title">Import Filters from AyuGram</div>
|
||||
{messageFilters.length > 0 && (
|
||||
<span className="settings-item__current-value">{messageFilters.length} rules</span>
|
||||
)}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="document"
|
||||
disabled={messageFilters.length === 0}
|
||||
onClick={handleExport}
|
||||
>
|
||||
<div className="title">Export Filters</div>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="delete"
|
||||
disabled={messageFilters.length === 0}
|
||||
onClick={openClearConfirm}
|
||||
>
|
||||
<div className="title">Clear All Filters</div>
|
||||
</ListItem>
|
||||
</Island>
|
||||
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept=".json,application/json"
|
||||
style="display: none"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
isOpen={isClearConfirmOpen}
|
||||
title="Clear All Filters"
|
||||
text={`Remove all ${messageFilters.length} filter rules?`}
|
||||
confirmLabel="Clear"
|
||||
confirmIsDestructive
|
||||
onClose={closeClearConfirm}
|
||||
confirmHandler={handleClearConfirmed}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): Complete<StateProps> => {
|
||||
const { ayuLike } = selectSharedSettings(global);
|
||||
return {
|
||||
hideSponsoredMessages: ayuLike.hideSponsoredMessages,
|
||||
messageFilters: ayuLike.messageFilters,
|
||||
};
|
||||
},
|
||||
)(SettingsAyuLike));
|
||||
@ -167,13 +167,6 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
{lang('MenuStickers')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="hand-stop"
|
||||
narrow
|
||||
onClick={() => openSettingsScreen({ screen: SettingsScreens.AyuLikeSettings })}
|
||||
>
|
||||
Message Filters
|
||||
</ListItem>
|
||||
</Island>
|
||||
<Island>
|
||||
{canBuyPremium && (
|
||||
|
||||
@ -273,7 +273,6 @@ export enum SettingsScreens {
|
||||
PasscodeTurnOff,
|
||||
PasscodeCongratulations,
|
||||
Experimental,
|
||||
AyuLikeSettings,
|
||||
Stickers,
|
||||
QuickReaction,
|
||||
CustomEmoji,
|
||||
|
||||
@ -31,57 +31,6 @@ function getMessageMediaTypes(message: ApiMessage): string[] {
|
||||
].filter(Boolean) as string[];
|
||||
}
|
||||
|
||||
// AyuGram Desktop export format (version 2)
|
||||
interface AyuGramExport {
|
||||
filters: {
|
||||
id: string;
|
||||
text: string;
|
||||
enabled: boolean;
|
||||
reversed: boolean;
|
||||
caseInsensitive: boolean;
|
||||
dialogId: string | null;
|
||||
}[];
|
||||
exclusions?: unknown[];
|
||||
version?: number;
|
||||
}
|
||||
|
||||
export function importFromAyuGram(json: unknown): MessageFilterRule[] {
|
||||
const data = json as AyuGramExport;
|
||||
if (!data || typeof data !== 'object' || !Array.isArray(data.filters)) {
|
||||
throw new Error('Invalid AyuGram export format');
|
||||
}
|
||||
|
||||
return data.filters.flatMap((f) => {
|
||||
if (!f || typeof f !== 'object') return [];
|
||||
if (typeof f.id !== 'string' || !f.id) return [];
|
||||
if (typeof f.text !== 'string' || !f.text) return [];
|
||||
return [{
|
||||
id: f.id,
|
||||
enabled: Boolean(f.enabled),
|
||||
reversed: Boolean(f.reversed),
|
||||
caseInsensitive: Boolean(f.caseInsensitive),
|
||||
regex: f.text,
|
||||
chatIds: f.dialogId ? [String(f.dialogId)] : undefined,
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
export function exportToAyuGram(rules: MessageFilterRule[]): string {
|
||||
const data: AyuGramExport = {
|
||||
exclusions: [],
|
||||
filters: rules.map((r) => ({
|
||||
id: r.id,
|
||||
text: r.regex ?? r.keyword ?? '',
|
||||
enabled: r.enabled,
|
||||
reversed: Boolean(r.reversed),
|
||||
caseInsensitive: Boolean(r.caseInsensitive),
|
||||
dialogId: r.chatIds?.[0] ?? null,
|
||||
})),
|
||||
version: 2,
|
||||
};
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
export function shouldHideMessageByRules(
|
||||
message: ApiMessage,
|
||||
rules: MessageFilterRule[] = [],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user