linkfeed-pro 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +9 -0
- package/.output/chrome-mv3/_locales/de/messages.json +214 -0
- package/.output/chrome-mv3/_locales/en/messages.json +214 -0
- package/.output/chrome-mv3/_locales/es/messages.json +214 -0
- package/.output/chrome-mv3/_locales/fr/messages.json +214 -0
- package/.output/chrome-mv3/_locales/hi/messages.json +214 -0
- package/.output/chrome-mv3/_locales/id/messages.json +214 -0
- package/.output/chrome-mv3/_locales/it/messages.json +214 -0
- package/.output/chrome-mv3/_locales/nl/messages.json +214 -0
- package/.output/chrome-mv3/_locales/pl/messages.json +214 -0
- package/.output/chrome-mv3/_locales/pt_BR/messages.json +214 -0
- package/.output/chrome-mv3/_locales/pt_PT/messages.json +214 -0
- package/.output/chrome-mv3/_locales/tr/messages.json +214 -0
- package/.output/chrome-mv3/assets/popup-Z_g1HFs5.css +1 -0
- package/.output/chrome-mv3/background.js +42 -0
- package/.output/chrome-mv3/chunks/popup-IxiPwS1E.js +42 -0
- package/.output/chrome-mv3/content-scripts/content.js +179 -0
- package/.output/chrome-mv3/icon-128.png +0 -0
- package/.output/chrome-mv3/icon-16.png +0 -0
- package/.output/chrome-mv3/icon-48.png +0 -0
- package/.output/chrome-mv3/icon.svg +9 -0
- package/.output/chrome-mv3/manifest.json +1 -0
- package/.output/chrome-mv3/popup.html +247 -0
- package/.wxt/eslint-auto-imports.mjs +56 -0
- package/.wxt/tsconfig.json +28 -0
- package/.wxt/types/globals.d.ts +15 -0
- package/.wxt/types/i18n.d.ts +593 -0
- package/.wxt/types/imports-module.d.ts +20 -0
- package/.wxt/types/imports.d.ts +50 -0
- package/.wxt/types/paths.d.ts +32 -0
- package/.wxt/wxt.d.ts +7 -0
- package/entrypoints/background.ts +112 -0
- package/entrypoints/content.ts +656 -0
- package/entrypoints/popup/main.ts +452 -0
- package/entrypoints/popup/modules/auth-modal.ts +219 -0
- package/entrypoints/popup/modules/settings.ts +78 -0
- package/entrypoints/popup/modules/ui-state.ts +95 -0
- package/entrypoints/popup/style.css +844 -0
- package/entrypoints/popup.html +261 -0
- package/lib/constants.ts +9 -0
- package/lib/device-meta.ts +173 -0
- package/lib/i18n.ts +201 -0
- package/lib/license.ts +470 -0
- package/lib/selectors.ts +24 -0
- package/lib/storage.ts +95 -0
- package/lib/telemetry.ts +94 -0
- package/package.json +30 -0
- package/public/_locales/de/messages.json +214 -0
- package/public/_locales/en/messages.json +214 -0
- package/public/_locales/es/messages.json +214 -0
- package/public/_locales/fr/messages.json +214 -0
- package/public/_locales/hi/messages.json +214 -0
- package/public/_locales/id/messages.json +214 -0
- package/public/_locales/it/messages.json +214 -0
- package/public/_locales/nl/messages.json +214 -0
- package/public/_locales/pl/messages.json +214 -0
- package/public/_locales/pt_BR/messages.json +214 -0
- package/public/_locales/pt_PT/messages.json +214 -0
- package/public/_locales/tr/messages.json +214 -0
- package/public/icon-128.png +0 -0
- package/public/icon-16.png +0 -0
- package/public/icon-48.png +0 -0
- package/public/icon.svg +9 -0
- package/tsconfig.json +3 -0
- package/wxt.config.ts +50 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { settingsStorage, type Settings } from '../../../lib/storage';
|
|
2
|
+
|
|
3
|
+
export async function loadSettings() {
|
|
4
|
+
const settings = await settingsStorage.getValue();
|
|
5
|
+
const hideSidebars = settings.hideSidebars ?? (settings as Settings & { removeSidebars?: boolean }).removeSidebars ?? true;
|
|
6
|
+
|
|
7
|
+
// Update UI
|
|
8
|
+
setCheckboxState('globalEnabled', settings.globalEnabled);
|
|
9
|
+
setCheckboxState('hideSidebars', hideSidebars);
|
|
10
|
+
setCheckboxState('hidePromoted', settings.hidePromoted ?? false);
|
|
11
|
+
setSliderState('feedWidth', settings.feedWidth);
|
|
12
|
+
setSliderState('feedSpacing', settings.feedSpacing ?? 24);
|
|
13
|
+
setCheckboxState('autoExpandPosts', settings.autoExpandPosts);
|
|
14
|
+
setCheckboxState('hideRemovedFeedCards', settings.hideRemovedFeedCards ?? true);
|
|
15
|
+
setCheckboxState('hideStartPost', settings.hideStartPost);
|
|
16
|
+
setCheckboxState('hideMessenger', settings.hideMessenger);
|
|
17
|
+
setCheckboxState('hideNavBar', settings.hideNavBar);
|
|
18
|
+
setSliderState('fontSize', settings.fontSize);
|
|
19
|
+
|
|
20
|
+
updateSettingsContainerState(settings.globalEnabled);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function setCheckboxState(id: string, checked: boolean) {
|
|
24
|
+
const checkbox = document.getElementById(id) as HTMLInputElement;
|
|
25
|
+
if (checkbox) checkbox.checked = checked;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function setSliderState(id: string, value: number) {
|
|
29
|
+
const slider = document.getElementById(id) as HTMLInputElement;
|
|
30
|
+
if (slider) {
|
|
31
|
+
slider.value = String(value);
|
|
32
|
+
updateSliderDisplay(id, value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function updateSliderDisplay(id: string, value: number) {
|
|
37
|
+
const label = document.getElementById(`${id}-value`);
|
|
38
|
+
if (label) {
|
|
39
|
+
label.textContent = `${value}px`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function handleSettingChange() {
|
|
44
|
+
const globalEnabled = getCheckboxChecked('globalEnabled');
|
|
45
|
+
|
|
46
|
+
const settings: Settings = {
|
|
47
|
+
globalEnabled,
|
|
48
|
+
hideSidebars: getCheckboxChecked('hideSidebars'),
|
|
49
|
+
hidePromoted: getCheckboxChecked('hidePromoted'),
|
|
50
|
+
feedWidth: Number((document.getElementById('feedWidth') as HTMLInputElement).value),
|
|
51
|
+
feedSpacing: Number((document.getElementById('feedSpacing') as HTMLInputElement).value),
|
|
52
|
+
autoExpandPosts: getCheckboxChecked('autoExpandPosts'),
|
|
53
|
+
hideRemovedFeedCards: getCheckboxChecked('hideRemovedFeedCards'),
|
|
54
|
+
hideStartPost: getCheckboxChecked('hideStartPost'),
|
|
55
|
+
hideMessenger: getCheckboxChecked('hideMessenger'),
|
|
56
|
+
hideNavBar: getCheckboxChecked('hideNavBar'),
|
|
57
|
+
fontSize: Number((document.getElementById('fontSize') as HTMLInputElement).value),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
await settingsStorage.setValue(settings);
|
|
61
|
+
updateSettingsContainerState(globalEnabled);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function updateSettingsContainerState(enabled: boolean) {
|
|
65
|
+
const containers = document.querySelectorAll<HTMLElement>('.feature-container');
|
|
66
|
+
containers.forEach((container) => {
|
|
67
|
+
if (enabled) {
|
|
68
|
+
container.classList.remove('disabled');
|
|
69
|
+
} else {
|
|
70
|
+
container.classList.add('disabled');
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function getCheckboxChecked(id: string): boolean {
|
|
76
|
+
const checkbox = document.getElementById(id) as HTMLInputElement;
|
|
77
|
+
return checkbox ? checkbox.checked : false;
|
|
78
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { userEmailStorage } from '../../../lib/storage';
|
|
2
|
+
import { APP_URLS } from '../../../lib/constants';
|
|
3
|
+
import { t } from '../../../lib/i18n';
|
|
4
|
+
import type { LicenseState } from '@linkfeed/shared';
|
|
5
|
+
|
|
6
|
+
export type LicenseUIState = 'free' | 'trial' | 'pro' | 'expired';
|
|
7
|
+
|
|
8
|
+
export function getLicenseUIState(state: LicenseState): LicenseUIState {
|
|
9
|
+
if (state.status === 'active') return 'pro';
|
|
10
|
+
if (state.status === 'trial-active') return 'trial';
|
|
11
|
+
if (state.status === 'trial-expired') return 'expired';
|
|
12
|
+
return 'free';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function updateUIForLicenseState(state: LicenseState) {
|
|
16
|
+
const uiState = getLicenseUIState(state);
|
|
17
|
+
const badge = document.getElementById('status-badge');
|
|
18
|
+
const premiumFeatures = document.querySelectorAll('.premium-feature');
|
|
19
|
+
|
|
20
|
+
premiumFeatures.forEach((el) => {
|
|
21
|
+
const proBadge = el.querySelector('.pro-badge');
|
|
22
|
+
if (proBadge) proBadge.classList.remove('hidden');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Update badge
|
|
26
|
+
if (badge) {
|
|
27
|
+
badge.className = `status-badge ${uiState}`;
|
|
28
|
+
|
|
29
|
+
switch (uiState) {
|
|
30
|
+
case 'pro':
|
|
31
|
+
badge.textContent = t('statusPro');
|
|
32
|
+
userEmailStorage.getValue().then((email) => {
|
|
33
|
+
badge.setAttribute('data-title', t('licensedTo', email || t('genericYou')));
|
|
34
|
+
});
|
|
35
|
+
break;
|
|
36
|
+
case 'trial':
|
|
37
|
+
badge.textContent = t('statusTrial');
|
|
38
|
+
if (state.expiresAt) {
|
|
39
|
+
badge.setAttribute('data-title', t('trialExpiresIn', formatDuration(state.expiresAt)));
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
case 'expired':
|
|
43
|
+
badge.textContent = t('statusFree');
|
|
44
|
+
badge.className = 'status-badge free';
|
|
45
|
+
badge.setAttribute('data-title', t('trialExpiredBadge'));
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
badge.textContent = t('statusFree');
|
|
49
|
+
badge.setAttribute('data-title', t('upgradeToPremium'));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Show appropriate UI based on state
|
|
54
|
+
if (uiState === 'trial') {
|
|
55
|
+
// Enable premium features
|
|
56
|
+
premiumFeatures.forEach(el => el.classList.remove('locked'));
|
|
57
|
+
} else if (uiState === 'pro') {
|
|
58
|
+
// Enable premium features, hide PRO badges
|
|
59
|
+
premiumFeatures.forEach(el => {
|
|
60
|
+
el.classList.remove('locked');
|
|
61
|
+
const badge = el.querySelector('.pro-badge');
|
|
62
|
+
if (badge) badge.classList.add('hidden');
|
|
63
|
+
});
|
|
64
|
+
} else if (uiState === 'expired') {
|
|
65
|
+
// Expired state - lock premiums but show "Free" badge (conceptually free tier now)
|
|
66
|
+
premiumFeatures.forEach(el => el.classList.add('locked'));
|
|
67
|
+
} else {
|
|
68
|
+
// Free state - lock premium features
|
|
69
|
+
premiumFeatures.forEach(el => el.classList.add('locked'));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function formatDuration(expiresAt: string): string {
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const end = new Date(expiresAt);
|
|
76
|
+
const diff = end.getTime() - now.getTime();
|
|
77
|
+
|
|
78
|
+
if (diff <= 0) return t('expiredShort');
|
|
79
|
+
|
|
80
|
+
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
81
|
+
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
82
|
+
|
|
83
|
+
if (days > 0) {
|
|
84
|
+
const dayLabel = t(days === 1 ? 'unitDay' : 'unitDays');
|
|
85
|
+
if (hours > 0) {
|
|
86
|
+
const hourLabel = t(hours === 1 ? 'unitHour' : 'unitHours');
|
|
87
|
+
return `${days} ${dayLabel} ${hours} ${hourLabel}`;
|
|
88
|
+
}
|
|
89
|
+
return `${days} ${dayLabel}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const safeHours = Math.max(hours, 1);
|
|
93
|
+
const hourLabel = t(safeHours === 1 ? 'unitHour' : 'unitHours');
|
|
94
|
+
return `${safeHours} ${hourLabel}`;
|
|
95
|
+
}
|