mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
fixed addon reorder issue
This commit is contained in:
parent
a32fb39743
commit
2f404d7c99
4 changed files with 101 additions and 38 deletions
|
|
@ -1346,7 +1346,7 @@ const VideoPlayer: React.FC = () => {
|
|||
if (type !== 'series' || !nextEpisode || duration <= 0) {
|
||||
if (showNextEpisodeButton) {
|
||||
// Hide button with animation
|
||||
Animated.parallel([
|
||||
fi Animated.parallel([
|
||||
Animated.timing(nextEpisodeButtonOpacity, {
|
||||
toValue: 0,
|
||||
duration: 200,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { syncService } from '../services/SyncService';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
// Simple event emitter for settings changes
|
||||
|
|
@ -122,12 +123,40 @@ export const useSettings = () => {
|
|||
const loadSettings = async () => {
|
||||
try {
|
||||
const scope = (await AsyncStorage.getItem('@user:current')) || 'local';
|
||||
const storedSettings = await AsyncStorage.getItem(`@user:${scope}:${SETTINGS_STORAGE_KEY}`);
|
||||
if (storedSettings) {
|
||||
const parsedSettings = JSON.parse(storedSettings);
|
||||
// Merge with defaults to ensure all properties exist
|
||||
setSettings({ ...DEFAULT_SETTINGS, ...parsedSettings });
|
||||
const scopedKey = `@user:${scope}:${SETTINGS_STORAGE_KEY}`;
|
||||
const [scopedJson, legacyJson] = await Promise.all([
|
||||
AsyncStorage.getItem(scopedKey),
|
||||
AsyncStorage.getItem(SETTINGS_STORAGE_KEY),
|
||||
]);
|
||||
const parsedScoped = scopedJson ? JSON.parse(scopedJson) : null;
|
||||
const parsedLegacy = legacyJson ? JSON.parse(legacyJson) : null;
|
||||
|
||||
let merged = parsedScoped || parsedLegacy;
|
||||
|
||||
// Fallback: scan any existing user-scoped settings if current scope not set yet
|
||||
if (!merged) {
|
||||
try {
|
||||
const allKeys = await AsyncStorage.getAllKeys();
|
||||
const candidateKeys = (allKeys || []).filter(k => k.endsWith(`:${SETTINGS_STORAGE_KEY}`));
|
||||
if (candidateKeys.length > 0) {
|
||||
const pairs = await AsyncStorage.multiGet(candidateKeys);
|
||||
for (const [, value] of pairs) {
|
||||
if (value) {
|
||||
try {
|
||||
const candidate = JSON.parse(value);
|
||||
if (candidate && typeof candidate === 'object') {
|
||||
merged = candidate;
|
||||
break;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (merged) setSettings({ ...DEFAULT_SETTINGS, ...merged });
|
||||
else setSettings(DEFAULT_SETTINGS);
|
||||
} catch (error) {
|
||||
console.error('Failed to load settings:', error);
|
||||
// Fallback to default settings on error
|
||||
|
|
@ -143,7 +172,14 @@ export const useSettings = () => {
|
|||
const newSettings = { ...settings, [key]: value };
|
||||
try {
|
||||
const scope = (await AsyncStorage.getItem('@user:current')) || 'local';
|
||||
await AsyncStorage.setItem(`@user:${scope}:${SETTINGS_STORAGE_KEY}`, JSON.stringify(newSettings));
|
||||
const scopedKey = `@user:${scope}:${SETTINGS_STORAGE_KEY}`;
|
||||
// Write to both scoped key (multi-user aware) and legacy key for backward compatibility
|
||||
await Promise.all([
|
||||
AsyncStorage.setItem(scopedKey, JSON.stringify(newSettings)),
|
||||
AsyncStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(newSettings)),
|
||||
]);
|
||||
// Ensure a current scope exists to avoid future loads missing the chosen scope
|
||||
await AsyncStorage.setItem('@user:current', scope);
|
||||
setSettings(newSettings);
|
||||
console.log(`Setting updated: ${key}`, value);
|
||||
|
||||
|
|
@ -152,6 +188,9 @@ export const useSettings = () => {
|
|||
console.log('Emitting settings change event');
|
||||
settingsEmitter.emit();
|
||||
}
|
||||
|
||||
// If authenticated, push settings to server to prevent overwrite on next pull
|
||||
try { syncService.pushSettings(); } catch {}
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -612,7 +612,7 @@ class SyncService {
|
|||
try { map.set('org.stremio.opensubtitlesv3', await stremioService.getManifest('https://opensubtitles-v3.strem.io/manifest.json')); } catch {}
|
||||
|
||||
(stremioService as any).installedAddons = map;
|
||||
const order = (addons as any[]).map(a => a.addon_id);
|
||||
let order = (addons as any[]).map(a => a.addon_id);
|
||||
const ensureFront = (arr: string[], id: string) => {
|
||||
const idx = arr.indexOf(id);
|
||||
if (idx === -1) arr.unshift(id);
|
||||
|
|
@ -620,11 +620,42 @@ class SyncService {
|
|||
};
|
||||
ensureFront(order, 'com.linvo.cinemeta');
|
||||
ensureFront(order, 'org.stremio.opensubtitlesv3');
|
||||
// Keep order strictly from server after preinstalled
|
||||
// Do not append missing local-only ids to avoid resurrecting removed addons
|
||||
// Prefer local order if it exists; otherwise use remote
|
||||
try {
|
||||
const userScope = `@user:${userId}:stremio-addon-order`;
|
||||
const [localScopedOrder, localLegacyOrder, localGuestOrder] = await Promise.all([
|
||||
AsyncStorage.getItem(userScope),
|
||||
AsyncStorage.getItem('stremio-addon-order'),
|
||||
AsyncStorage.getItem('@user:local:stremio-addon-order'),
|
||||
]);
|
||||
const localOrderRaw = localScopedOrder || localLegacyOrder || localGuestOrder;
|
||||
if (localOrderRaw) {
|
||||
const localOrder = JSON.parse(localOrderRaw) as string[];
|
||||
// Filter to only installed ids
|
||||
const localFiltered = localOrder.filter(id => map.has(id));
|
||||
if (localFiltered.length > 0) {
|
||||
order = localFiltered;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
(stremioService as any).addonOrder = order;
|
||||
await (stremioService as any).saveInstalledAddons();
|
||||
await (stremioService as any).saveAddonOrder();
|
||||
// Push merged order to server to preserve across devices
|
||||
try {
|
||||
const rows = order.map((addonId: string, idx: number) => ({
|
||||
user_id: userId,
|
||||
addon_id: addonId,
|
||||
position: idx,
|
||||
}));
|
||||
const { error } = await supabase
|
||||
.from('installed_addons')
|
||||
.upsert(rows, { onConflict: 'user_id,addon_id' });
|
||||
if (error) logger.warn('[SyncService] push merged addon order error', error);
|
||||
} catch (e) {
|
||||
logger.warn('[SyncService] push merged addon order exception', e);
|
||||
}
|
||||
}
|
||||
|
||||
async pushWatchProgress(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -301,40 +301,23 @@ class StremioService {
|
|||
}
|
||||
}
|
||||
|
||||
// Load addon order if exists
|
||||
const storedOrder = await AsyncStorage.getItem(`@user:${scope}:${this.ADDON_ORDER_KEY}`);
|
||||
// Load addon order if exists (scoped first, then legacy, then @user:local for migration safety)
|
||||
let storedOrder = await AsyncStorage.getItem(`@user:${scope}:${this.ADDON_ORDER_KEY}`);
|
||||
if (!storedOrder) storedOrder = await AsyncStorage.getItem(this.ADDON_ORDER_KEY);
|
||||
if (!storedOrder) storedOrder = await AsyncStorage.getItem(`@user:local:${this.ADDON_ORDER_KEY}`);
|
||||
if (storedOrder) {
|
||||
this.addonOrder = JSON.parse(storedOrder);
|
||||
// Filter out any ids that aren't in installedAddons
|
||||
this.addonOrder = this.addonOrder.filter(id => this.installedAddons.has(id));
|
||||
}
|
||||
|
||||
// Ensure Cinemeta is first in the order
|
||||
if (!this.addonOrder.includes(cinemetaId)) {
|
||||
this.addonOrder.unshift(cinemetaId);
|
||||
} else {
|
||||
// Move Cinemeta to the front if it's not already there
|
||||
const cinemetaIndex = this.addonOrder.indexOf(cinemetaId);
|
||||
if (cinemetaIndex > 0) {
|
||||
this.addonOrder.splice(cinemetaIndex, 1);
|
||||
this.addonOrder.unshift(cinemetaId);
|
||||
}
|
||||
// Ensure required pre-installed addons are present without forcing their position
|
||||
if (!this.addonOrder.includes(cinemetaId) && this.installedAddons.has(cinemetaId)) {
|
||||
this.addonOrder.push(cinemetaId);
|
||||
}
|
||||
if (!this.addonOrder.includes(opensubsId) && this.installedAddons.has(opensubsId)) {
|
||||
this.addonOrder.push(opensubsId);
|
||||
}
|
||||
|
||||
// Ensure OpenSubtitles v3 is present right after Cinemeta (if not already ordered)
|
||||
const ensureOpensubsPosition = () => {
|
||||
const idx = this.addonOrder.indexOf(opensubsId);
|
||||
const cinIdx = this.addonOrder.indexOf(cinemetaId);
|
||||
if (idx === -1) {
|
||||
// Insert after Cinemeta
|
||||
this.addonOrder.splice(cinIdx + 1, 0, opensubsId);
|
||||
} else if (idx <= cinIdx) {
|
||||
// Move it to right after Cinemeta
|
||||
this.addonOrder.splice(idx, 1);
|
||||
this.addonOrder.splice(cinIdx + 1, 0, opensubsId);
|
||||
}
|
||||
};
|
||||
ensureOpensubsPosition();
|
||||
|
||||
// Add any missing addons to the order
|
||||
const installedIds = Array.from(this.installedAddons.keys());
|
||||
|
|
@ -399,7 +382,11 @@ class StremioService {
|
|||
private async saveAddonOrder(): Promise<void> {
|
||||
try {
|
||||
const scope = (await AsyncStorage.getItem('@user:current')) || 'local';
|
||||
await AsyncStorage.setItem(`@user:${scope}:${this.ADDON_ORDER_KEY}`, JSON.stringify(this.addonOrder));
|
||||
// Write to both scoped and legacy keys for compatibility
|
||||
await Promise.all([
|
||||
AsyncStorage.setItem(`@user:${scope}:${this.ADDON_ORDER_KEY}`, JSON.stringify(this.addonOrder)),
|
||||
AsyncStorage.setItem(this.ADDON_ORDER_KEY, JSON.stringify(this.addonOrder)),
|
||||
]);
|
||||
} catch (error) {
|
||||
logger.error('Failed to save addon order:', error);
|
||||
}
|
||||
|
|
@ -446,6 +433,7 @@ class StremioService {
|
|||
|
||||
await this.saveInstalledAddons();
|
||||
await this.saveAddonOrder();
|
||||
try { (require('./SyncService').syncService as any).pushAddons?.(); } catch {}
|
||||
// Emit an event that an addon was added
|
||||
addonEmitter.emit(ADDON_EVENTS.ADDON_ADDED, manifest.id);
|
||||
} else {
|
||||
|
|
@ -466,6 +454,7 @@ class StremioService {
|
|||
this.addonOrder = this.addonOrder.filter(addonId => addonId !== id);
|
||||
this.saveInstalledAddons();
|
||||
this.saveAddonOrder();
|
||||
try { (require('./SyncService').syncService as any).pushAddons?.(); } catch {}
|
||||
// Emit an event that an addon was removed
|
||||
addonEmitter.emit(ADDON_EVENTS.ADDON_REMOVED, id);
|
||||
}
|
||||
|
|
@ -1238,6 +1227,8 @@ class StremioService {
|
|||
[this.addonOrder[index - 1], this.addonOrder[index]] =
|
||||
[this.addonOrder[index], this.addonOrder[index - 1]];
|
||||
this.saveAddonOrder();
|
||||
// Immediately push to server to avoid resets on restart
|
||||
try { (require('./SyncService').syncService as any).pushAddons?.(); } catch {}
|
||||
// Emit an event that the order has changed
|
||||
addonEmitter.emit(ADDON_EVENTS.ORDER_CHANGED);
|
||||
return true;
|
||||
|
|
@ -1252,6 +1243,8 @@ class StremioService {
|
|||
[this.addonOrder[index], this.addonOrder[index + 1]] =
|
||||
[this.addonOrder[index + 1], this.addonOrder[index]];
|
||||
this.saveAddonOrder();
|
||||
// Immediately push to server to avoid resets on restart
|
||||
try { (require('./SyncService').syncService as any).pushAddons?.(); } catch {}
|
||||
// Emit an event that the order has changed
|
||||
addonEmitter.emit(ADDON_EVENTS.ORDER_CHANGED);
|
||||
return true;
|
||||
|
|
|
|||
Loading…
Reference in a new issue