mirror of
https://github.com/MyLuxy/overdrive.git
synced 2026-10-19 02:10:55 -05:00
feat: vibration intensity levels
This commit is contained in:
1 parent
30f7899531
commit
1b2daddaa9
17 files changed
+67
-22
No files matched your search
@@ -353,6 +353,7 @@
|
||||
"settings.title": "الإعدادات",
|
||||
"settings.vibration": "الاهتزاز أثناء التصفح",
|
||||
"settings.vibrationDetail": "نبضة خفيفة عند التنقل والاختيار والرجوع",
|
||||
"settings.vibrationOff": "إيقاف",
|
||||
"shadowMount.offAction": "تظهر اللعبة بعد تحميله. أضفه إلى التحميل التلقائي لكسر الحماية ليبقى يعمل دائمًا.",
|
||||
"shadowMount.offTitle": "ShadowMount لا يعمل",
|
||||
"sort.label": "ترتيب",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "সেটিংস",
|
||||
"settings.vibration": "ব্রাউজ করার সময় কম্পন",
|
||||
"settings.vibrationDetail": "সরানো, বেছে নেওয়া ও ফিরে যাওয়ার সময় হালকা কম্পন",
|
||||
"settings.vibrationOff": "বন্ধ",
|
||||
"shadowMount.offAction": "এটি লোড করলে গেমটি দেখা যাবে। সবসময় চালু রাখতে জেলব্রেকের অটোলোডে যোগ করুন।",
|
||||
"shadowMount.offTitle": "ShadowMount চলছে না",
|
||||
"sort.label": "বাছাই করুন",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "Einstellungen",
|
||||
"settings.vibration": "Vibration beim Navigieren",
|
||||
"settings.vibrationDetail": "Leichter Impuls beim Bewegen, Wählen und Zurück",
|
||||
"settings.vibrationOff": "Aus",
|
||||
"shadowMount.offAction": "Das Spiel erscheint, sobald du es lädst. Füge es zum Autoload deines Jailbreaks hinzu, damit es immer läuft.",
|
||||
"shadowMount.offTitle": "ShadowMount läuft nicht",
|
||||
"sort.label": "Sortieren",
|
||||
|
||||
@@ -356,6 +356,7 @@
|
||||
"settings.title": "Settings",
|
||||
"settings.vibration": "Vibration while browsing",
|
||||
"settings.vibrationDetail": "A light pulse as you move, select and go back",
|
||||
"settings.vibrationOff": "Off",
|
||||
"shadowMount.offAction": "The game shows up once you load it. Add it to your jailbreak's autoload to keep it on.",
|
||||
"shadowMount.offTitle": "ShadowMount isn't running",
|
||||
"sort.label": "Sort",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "Ajustes",
|
||||
"settings.vibration": "Vibración al navegar",
|
||||
"settings.vibrationDetail": "Un leve pulso al moverte, seleccionar y volver",
|
||||
"settings.vibrationOff": "Apagada",
|
||||
"shadowMount.offAction": "El juego aparece cuando lo cargues. Añádelo al autoload del jailbreak para que siempre esté activo.",
|
||||
"shadowMount.offTitle": "ShadowMount no se está ejecutando",
|
||||
"sort.label": "Ordenar",
|
||||
|
||||
@@ -354,6 +354,7 @@
|
||||
"settings.title": "تنظیمات",
|
||||
"settings.vibration": "لرزش هنگام مرور",
|
||||
"settings.vibrationDetail": "ضربهٔ ملایم هنگام حرکت، انتخاب و بازگشت",
|
||||
"settings.vibrationOff": "خاموش",
|
||||
"shadowMount.offAction": "بازی وقتی بارگذاری شود ظاهر میشود. آن را به autoload جیلبریک اضافه کنید تا روشن بماند.",
|
||||
"shadowMount.offTitle": "ShadowMount در حال اجرا نیست",
|
||||
"sort.label": "مرتبسازی",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "Réglages",
|
||||
"settings.vibration": "Vibration pendant la navigation",
|
||||
"settings.vibrationDetail": "Une légère impulsion quand vous vous déplacez, sélectionnez ou revenez",
|
||||
"settings.vibrationOff": "Désactivée",
|
||||
"shadowMount.offAction": "Le jeu apparaît une fois ShadowMount chargé. Ajoutez-le à l'autoload du jailbreak pour qu'il reste actif.",
|
||||
"shadowMount.offTitle": "ShadowMount n'est pas lancé",
|
||||
"sort.label": "Trier",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "सेटिंग्स",
|
||||
"settings.vibration": "ब्राउज़ करते समय वाइब्रेशन",
|
||||
"settings.vibrationDetail": "आगे बढ़ने, चुनने और वापस जाने पर हल्का कंपन",
|
||||
"settings.vibrationOff": "बंद",
|
||||
"shadowMount.offAction": "इसे लोड करने पर गेम दिखेगा। इसे हमेशा चालू रखने के लिए जेलब्रेक के ऑटोलोड में जोड़ें।",
|
||||
"shadowMount.offTitle": "ShadowMount नहीं चल रहा",
|
||||
"sort.label": "क्रमबद्ध करें",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "Pengaturan",
|
||||
"settings.vibration": "Getaran saat menjelajah",
|
||||
"settings.vibrationDetail": "Getaran ringan saat berpindah, memilih, dan kembali",
|
||||
"settings.vibrationOff": "Mati",
|
||||
"shadowMount.offAction": "Game muncul setelah kamu memuatnya. Tambahkan ke autoload jailbreak agar selalu aktif.",
|
||||
"shadowMount.offTitle": "ShadowMount tidak berjalan",
|
||||
"sort.label": "Urutkan",
|
||||
|
||||
@@ -361,6 +361,7 @@
|
||||
"settings.title": "Impostazioni",
|
||||
"settings.vibration": "Vibrazione durante la navigazione",
|
||||
"settings.vibrationDetail": "Un leggero impulso quando ti sposti, selezioni e torni indietro",
|
||||
"settings.vibrationOff": "Spenta",
|
||||
"shadowMount.offAction": "Il gioco compare quando lo carichi. Aggiungilo all'autoload del tuo jailbreak per tenerlo sempre attivo.",
|
||||
"shadowMount.offTitle": "ShadowMount non è attivo",
|
||||
"sort.label": "Ordina",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "設定",
|
||||
"settings.vibration": "操作時の振動",
|
||||
"settings.vibrationDetail": "移動、選択、戻る操作のときに軽く振動します",
|
||||
"settings.vibrationOff": "オフ",
|
||||
"shadowMount.offAction": "ShadowMount を読み込むとゲームが表示されます。脱獄の自動読み込みに追加すると常に起動します。",
|
||||
"shadowMount.offTitle": "ShadowMount が起動していません",
|
||||
"sort.label": "並べ替え",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "Configurações",
|
||||
"settings.vibration": "Vibração ao navegar",
|
||||
"settings.vibrationDetail": "Um pulso leve ao mover, selecionar e voltar",
|
||||
"settings.vibrationOff": "Desligada",
|
||||
"shadowMount.offAction": "O jogo aparece quando você carregar ele. Coloque no autoload do jailbreak pra ficar sempre ligado.",
|
||||
"shadowMount.offTitle": "O ShadowMount não está rodando",
|
||||
"sort.label": "Ordenar",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "Настройки",
|
||||
"settings.vibration": "Вибрация при навигации",
|
||||
"settings.vibrationDetail": "Лёгкий отклик при перемещении, выборе и возврате",
|
||||
"settings.vibrationOff": "Выкл.",
|
||||
"shadowMount.offAction": "Игра появится, когда вы его загрузите. Добавьте его в автозагрузку джейлбрейка, чтобы он всегда работал.",
|
||||
"shadowMount.offTitle": "ShadowMount не запущен",
|
||||
"sort.label": "Сортировка",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "ترتیبات",
|
||||
"settings.vibration": "براؤز کرتے وقت وائبریشن",
|
||||
"settings.vibrationDetail": "حرکت، انتخاب اور واپس جانے پر ہلکی سی وائبریشن",
|
||||
"settings.vibrationOff": "بند",
|
||||
"shadowMount.offAction": "اسے لوڈ کرنے پر گیم نظر آئے گی۔ ہمیشہ چلانے کے لیے جیل بریک کے آٹو لوڈ میں شامل کریں۔",
|
||||
"shadowMount.offTitle": "ShadowMount نہیں چل رہا",
|
||||
"sort.label": "ترتیب دیں",
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
"settings.title": "设置",
|
||||
"settings.vibration": "浏览时振动",
|
||||
"settings.vibrationDetail": "移动、选择和返回时轻微振动",
|
||||
"settings.vibrationOff": "关闭",
|
||||
"shadowMount.offAction": "加载 ShadowMount 后游戏才会出现。将它加入越狱的自动加载即可保持运行。",
|
||||
"shadowMount.offTitle": "ShadowMount 未运行",
|
||||
"sort.label": "排序",
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
|
||||
import '../assets/Inter-Regular.ttf';
|
||||
import { Fragment, jsx, jsxs } from 'react/jsx-runtime';
|
||||
import { formatPercent2 } from '../i18n/core.js';
|
||||
import { chooseLanguage, languageChoice, systemLocale, useT } from '../i18n/index.js';
|
||||
import { LOCALES } from '../i18n/locales.js';
|
||||
import { FocusRing } from './FocusRing.jsx';
|
||||
import { Icon } from './Icon.jsx';
|
||||
import { SideSheet } from './SideSheet.jsx';
|
||||
import { colors } from './colors.js';
|
||||
import { setNavigationHaptics, useNavigationHaptics } from './haptics.js';
|
||||
import { LEVELS, setVibrationLevel, useVibrationLevel } from './haptics.js';
|
||||
import { LIMITS, setSpeedLimit, useSpeedLimit } from '../state/speed-limit.js';
|
||||
import { Text, View, useIsFocused } from '@ps5-react/core';
|
||||
|
||||
@@ -52,6 +53,29 @@ import { Text, View, useIsFocused } from '@ps5-react/core';
|
||||
/* @__PURE__ */ (0, jsx)(View, { style: { flexDirection: "row", gap: 9 }, children: LIMITS.map((value) => /* @__PURE__ */ (0, jsx)(SpeedSegment, { limit: value, chosen: value === limit }, value)) })
|
||||
] });
|
||||
}
|
||||
// The vibration level (issue #32), laid out like the speed limit: every step in view, Cross picks.
|
||||
var levelName = (t2, level) => level ? formatPercent2(level) : t2("settings.vibrationOff");
|
||||
function VibrationSegment({ level, chosen }) {
|
||||
const t2 = useT();
|
||||
return /* @__PURE__ */ (0, jsxs)(View, { focusKey: `settings:vibration:${level}`, onPress: () => setVibrationLevel(level), style: { position: "relative", flex: level ? 1 : 2, height: 60, alignItems: "center", justifyContent: "center", borderRadius: 12, backgroundColor: chosen ? colors.fg : colors.surface2 }, children: [
|
||||
/* @__PURE__ */ (0, jsx)(FocusRing, {}),
|
||||
/* @__PURE__ */ (0, jsx)(Text, { style: { fontFamily: "Inter-Regular", fontSize: 21, fontWeight: 600, color: chosen ? colors.accentFg : colors.muted, numberOfLines: 1 }, children: levelName(t2, level) })
|
||||
] });
|
||||
}
|
||||
function VibrationLevel() {
|
||||
const t2 = useT();
|
||||
const level = useVibrationLevel();
|
||||
return /* @__PURE__ */ (0, jsxs)(View, { style: { gap: 18, paddingHorizontal: 18, paddingTop: 6, paddingBottom: 18 }, children: [
|
||||
/* @__PURE__ */ (0, jsxs)(View, { style: { gap: 3 }, children: [
|
||||
/* @__PURE__ */ (0, jsxs)(View, { style: { flexDirection: "row", justifyContent: "space-between", alignItems: "center" }, children: [
|
||||
/* @__PURE__ */ (0, jsx)(Text, { style: { fontFamily: "Inter-Regular", fontSize: 23, fontWeight: 600, color: colors.fg }, children: t2("settings.vibration") }),
|
||||
/* @__PURE__ */ (0, jsx)(Text, { style: { fontFamily: "Inter-Regular", fontSize: 21, color: colors.fg }, children: levelName(t2, level) })
|
||||
] }),
|
||||
/* @__PURE__ */ (0, jsx)(Text, { style: { fontFamily: "Inter-Regular", fontSize: 21, color: colors.muted, lineHeight: 30 }, children: t2("settings.vibrationDetail") })
|
||||
] }),
|
||||
/* @__PURE__ */ (0, jsx)(View, { style: { flexDirection: "row", gap: 9 }, children: LEVELS.map((value) => /* @__PURE__ */ (0, jsx)(VibrationSegment, { level: value, chosen: value === level }, value)) })
|
||||
] });
|
||||
}
|
||||
function SectionLabel({ icon, title, detail: detail2 }) {
|
||||
const text = /* @__PURE__ */ (0, jsxs)(View, { style: { flex: 1, gap: 3 }, children: [
|
||||
/* @__PURE__ */ (0, jsx)(Text, { style: { fontFamily: "Inter-Regular", fontSize: 19, fontWeight: 600, color: colors.muted, letterSpacing: 1.5 }, children: title }),
|
||||
@@ -65,7 +89,6 @@ import { Text, View, useIsFocused } from '@ps5-react/core';
|
||||
function LanguageSheet({ onClose }) {
|
||||
const t2 = useT();
|
||||
const choice2 = languageChoice();
|
||||
const vibration = useNavigationHaptics();
|
||||
const pick2 = (code) => {
|
||||
chooseLanguage(code);
|
||||
onClose();
|
||||
@@ -76,7 +99,7 @@ import { Text, View, useIsFocused } from '@ps5-react/core';
|
||||
/* @__PURE__ */ (0, jsx)(SectionLabel, { icon: "download", title: t2("settings.downloads") }),
|
||||
/* @__PURE__ */ (0, jsx)(SpeedLimit, {}),
|
||||
/* @__PURE__ */ (0, jsx)(SectionLabel, { title: t2("settings.controller") }),
|
||||
/* @__PURE__ */ (0, jsx)(View, { focusKey: "settings:vibration", onPress: () => setNavigationHaptics(!vibration), style: (_state) => [{ position: "relative", flexDirection: "row", alignItems: "center", gap: 24, borderRadius: 12, paddingHorizontal: 18, paddingVertical: 18 }, _state.focused ? { backgroundColor: "#212125" } : null], children: /* @__PURE__ */ (0, jsx)(LanguageFace, { name: t2("settings.vibration"), detail: t2("settings.vibrationDetail"), checked: vibration }) }),
|
||||
/* @__PURE__ */ (0, jsx)(VibrationLevel, {}),
|
||||
/* @__PURE__ */ (0, jsx)(SectionLabel, { icon: "languages", title: t2("language.title"), detail: t2("language.subtitle") }),
|
||||
/* @__PURE__ */ (0, jsx)(Row4, { code: "system", name: t2("language.system"), detail: nameOf(systemLocale()), checked: choice2 === null, onPress: () => pick2(null) }),
|
||||
LOCALES.map(
|
||||
|
||||
+26
-19
@@ -18,38 +18,45 @@ import { Controller, useNavigationEvents } from '@ps5-react/core';
|
||||
// A pulse on every focus move, press and page change is a lot to hold for a whole browse, so
|
||||
// it starts off and lives in settings.json beside the language. The finished-download pulse
|
||||
// is a notice, not navigation, and stays on either way.
|
||||
// Navigation pulses are scaled by a level in percent, 0 being off (issue #32). The setting
|
||||
// used to be `vibration: true`, which reads as full strength.
|
||||
var LEVELS = [0, 25, 50, 75, 100];
|
||||
var SETTINGS = "settings.json";
|
||||
var navigationHaptics = loadList(SETTINGS, "settings").vibration === true;
|
||||
var saved = loadList(SETTINGS, "settings").vibration;
|
||||
var vibrationLevel = saved === true ? 100 : LEVELS.includes(saved) ? saved : 0;
|
||||
var listeners = /* @__PURE__ */ new Set();
|
||||
function setNavigationHaptics(on) {
|
||||
navigationHaptics = on;
|
||||
var navigationPulse = ([strength, ms]) => pulse2([strength * vibrationLevel / 100, ms]);
|
||||
// A chosen level is felt at once, so the player can tell the steps apart while picking one.
|
||||
function setVibrationLevel(level) {
|
||||
vibrationLevel = level;
|
||||
const { vibration, ...settings } = loadList(SETTINGS, "settings");
|
||||
saveList(SETTINGS, "settings", on ? { ...settings, vibration: true } : settings);
|
||||
for (const listener of listeners) listener(on);
|
||||
saveList(SETTINGS, "settings", level ? { ...settings, vibration: level } : settings);
|
||||
for (const listener of listeners) listener(level);
|
||||
if (level) navigationPulse(PAGE);
|
||||
}
|
||||
function useNavigationHaptics() {
|
||||
const [on, setOn] = (0, useState)(navigationHaptics);
|
||||
function useVibrationLevel() {
|
||||
const [level, setLevel] = (0, useState)(vibrationLevel);
|
||||
(0, useEffect)(() => {
|
||||
listeners.add(setOn);
|
||||
listeners.add(setLevel);
|
||||
return () => {
|
||||
listeners.delete(setOn);
|
||||
listeners.delete(setLevel);
|
||||
};
|
||||
}, []);
|
||||
return on;
|
||||
return level;
|
||||
}
|
||||
function useInterfaceHaptics() {
|
||||
useNavigationEvents((event) => {
|
||||
if (!navigationHaptics) return;
|
||||
if (event.type === "move") pulse2(TICK);
|
||||
if (!vibrationLevel) return;
|
||||
if (event.type === "move") navigationPulse(TICK);
|
||||
else if (event.type === "blocked") {
|
||||
pulse2(EDGE);
|
||||
setTimeout(() => pulse2(EDGE), EDGE_GAP_MS);
|
||||
} else if (event.type === "press") pulse2(event.handled ? PRESS : EDGE);
|
||||
else if (event.handled && event.type === "back") pulse2(BACK);
|
||||
else if (event.handled && PAGE_ACTIONS.has(event.action)) pulse2(PAGE);
|
||||
else if (event.handled) pulse2(PRESS);
|
||||
navigationPulse(EDGE);
|
||||
setTimeout(() => navigationPulse(EDGE), EDGE_GAP_MS);
|
||||
} else if (event.type === "press") navigationPulse(event.handled ? PRESS : EDGE);
|
||||
else if (event.handled && event.type === "back") navigationPulse(BACK);
|
||||
else if (event.handled && PAGE_ACTIONS.has(event.action)) navigationPulse(PAGE);
|
||||
else if (event.handled) navigationPulse(PRESS);
|
||||
});
|
||||
}
|
||||
var pulseNotice = () => pulse2(NOTICE);
|
||||
|
||||
export { pulseNotice, setNavigationHaptics, useInterfaceHaptics, useNavigationHaptics };
|
||||
export { LEVELS, pulseNotice, setVibrationLevel, useInterfaceHaptics, useVibrationLevel };
|
||||
Reference in new issue
Block a user