From d59f3ae0b6588e209b438b2c89f79ae084e358b0 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 8 Jan 2023 15:32:15 -0600 Subject: [PATCH] Add:Haptic feedback device setting off/light/medium/heavy #472 --- .../audiobookshelf/app/data/DeviceClasses.kt | 10 ++++- components/app/AudioPlayer.vue | 20 ++++----- components/app/SideDrawer.vue | 10 ++--- components/cards/LazyBookCard.vue | 2 +- components/connection/ServerConnectForm.vue | 6 +-- components/home/BookshelfToolbar.vue | 2 +- components/modals/BookmarksModal.vue | 6 +-- components/modals/FilterModal.vue | 4 +- components/modals/LibrariesModal.vue | 2 +- components/modals/OrderModal.vue | 2 +- components/modals/SleepTimerModal.vue | 14 +++--- .../modals/playlists/AddCreateModal.vue | 4 +- components/tables/podcast/EpisodeRow.vue | 6 +-- components/ui/Dropdown.vue | 2 +- ios/App/App/AppDelegate.swift | 8 +++- ios/App/App/plugins/AbsDatabase.swift | 2 + ios/App/Shared/models/DeviceSettings.swift | 4 +- layouts/default.vue | 21 +++++---- pages/account.vue | 2 +- pages/item/_id.vue | 8 ++-- pages/localMedia/item/_id.vue | 4 +- pages/settings.vue | 45 ++++++++++++++----- plugins/capacitor/AbsDatabase.js | 2 +- plugins/haptics.js | 14 +++++- store/globals.js | 6 ++- 25 files changed, 133 insertions(+), 73 deletions(-) diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt index fdcff60a..0d872100 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt @@ -11,6 +11,10 @@ enum class LockOrientationSetting { NONE, PORTRAIT, LANDSCAPE } +enum class HapticFeedbackSetting { + OFF, LIGHT, MEDIUM, HEAVY +} + data class ServerConnectionConfig( var id:String, var index:Int, @@ -28,7 +32,8 @@ data class DeviceSettings( var jumpBackwardsTime:Int, var jumpForwardTime:Int, var disableShakeToResetSleepTimer:Boolean, - var lockOrientation:LockOrientationSetting + var lockOrientation:LockOrientationSetting, + var hapticFeedback: HapticFeedbackSetting ) { companion object { // Static method to get default device settings @@ -39,7 +44,8 @@ data class DeviceSettings( jumpBackwardsTime = 10, jumpForwardTime = 10, disableShakeToResetSleepTimer = false, - lockOrientation = LockOrientationSetting.NONE + lockOrientation = LockOrientationSetting.NONE, + hapticFeedback = HapticFeedbackSetting.LIGHT ) } } diff --git a/components/app/AudioPlayer.vue b/components/app/AudioPlayer.vue index b459b299..bcde6960 100644 --- a/components/app/AudioPlayer.vue +++ b/components/app/AudioPlayer.vue @@ -352,17 +352,17 @@ export default { } }, async touchstartTrack(e) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (!e || !e.touches || !this.$refs.track || !this.showFullscreen || this.lockUi) return this.touchTrackStart = true }, async selectChapter(chapter) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.seek(chapter.start) this.showChapterModal = false }, async castClick() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isLocalPlayMethod) { this.$eventBus.$emit('cast-local-item') return @@ -382,13 +382,13 @@ export default { this.forceCloseDropdownMenu() }, async jumpNextChapter() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isLoading) return if (!this.nextChapter) return this.seek(this.nextChapter.start) }, async jumpChapterStart() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isLoading) return if (!this.currentChapter) { return this.restart() @@ -409,7 +409,7 @@ export default { this.$emit('showSleepTimer') }, async setPlaybackSpeed(speed) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() console.log(`[AudioPlayer] Set Playback Rate: ${speed}`) this.currentPlaybackRate = speed AbsAudioPlayer.setPlaybackSpeed({ value: speed }) @@ -418,12 +418,12 @@ export default { this.seek(0) }, async jumpBackwards() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isLoading) return AbsAudioPlayer.seekBackward({ value: this.jumpBackwardsTime }) }, async jumpForward() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isLoading) return AbsAudioPlayer.seekForward({ value: this.jumpForwardTime }) }, @@ -563,7 +563,7 @@ export default { this.seek(time) }, async playPauseClick() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isLoading) return this.isPlaying = !!((await AbsAudioPlayer.playPause()) || {}).playing @@ -667,7 +667,7 @@ export default { } }, async clickMenuAction(action) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.showMoreMenuDialog = false this.$nextTick(() => { if (action === 'lock') { diff --git a/components/app/SideDrawer.vue b/components/app/SideDrawer.vue index 152dcf95..447d6497 100644 --- a/components/app/SideDrawer.vue +++ b/components/app/SideDrawer.vue @@ -115,10 +115,10 @@ export default { }) } else { items.push({ - icon: 'download', - iconOutlined: false, - text: 'Downloads', - to: '/downloads' + icon: 'download', + iconOutlined: false, + text: 'Downloads', + to: '/downloads' }) } items.push({ @@ -137,7 +137,7 @@ export default { this.show = false }, async logout() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.user) { await this.$axios.$post('/logout').catch((error) => { console.error(error) diff --git a/components/cards/LazyBookCard.vue b/components/cards/LazyBookCard.vue index ffd9302f..789ee884 100644 --- a/components/cards/LazyBookCard.vue +++ b/components/cards/LazyBookCard.vue @@ -407,7 +407,7 @@ export default { e.preventDefault() this.selectBtnClick() } else if (this.recentEpisode) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() var eventBus = this.$eventBus || this.$nuxt.$eventBus if (this.streamIsPlaying) { eventBus.$emit('pause-item') diff --git a/components/connection/ServerConnectForm.vue b/components/connection/ServerConnectForm.vue index ad220a7f..d87ffdc7 100644 --- a/components/connection/ServerConnectForm.vue +++ b/components/connection/ServerConnectForm.vue @@ -132,7 +132,7 @@ export default { } }, async connectToServer(config) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() console.log('[ServerConnectForm] connectToServer', config.address) this.processing = true this.serverConfig = { @@ -160,7 +160,7 @@ export default { }, async removeServerConfigClick() { if (!this.serverConfig.id) return - await this.$hapticsImpactMedium() + await this.$hapticsImpact() const { value } = await Dialog.confirm({ title: 'Confirm', @@ -192,7 +192,7 @@ export default { this.showAuth = true }, async newServerConfigClick() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.serverConfig = { address: '', userId: '', diff --git a/components/home/BookshelfToolbar.vue b/components/home/BookshelfToolbar.vue index 912aa0c7..63c0d2ec 100644 --- a/components/home/BookshelfToolbar.vue +++ b/components/home/BookshelfToolbar.vue @@ -104,7 +104,7 @@ export default { }, async changeView() { this.bookshelfListView = !this.bookshelfListView - await this.$hapticsImpactMedium() + await this.$hapticsImpact() } }, mounted() { diff --git a/components/modals/BookmarksModal.vue b/components/modals/BookmarksModal.vue index 19f7a891..5c7fc805 100644 --- a/components/modals/BookmarksModal.vue +++ b/components/modals/BookmarksModal.vue @@ -95,7 +95,7 @@ export default { this.showBookmarkTitleInput = true }, async deleteBookmark(bm) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() const { value } = await Dialog.confirm({ title: 'Remove Bookmark', message: `Are you sure you want to remove bookmark?` @@ -114,7 +114,7 @@ export default { this.show = false }, async clickBookmark(bm) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.$emit('select', bm) }, submitUpdateBookmark(updatedBookmark) { @@ -159,7 +159,7 @@ export default { this.showBookmarkTitleInput = true }, async submitBookmark() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.selectedBookmark) { var updatePayload = { ...this.selectedBookmark, diff --git a/components/modals/FilterModal.vue b/components/modals/FilterModal.vue index 4c47ce76..114f86a9 100644 --- a/components/modals/FilterModal.vue +++ b/components/modals/FilterModal.vue @@ -213,7 +213,7 @@ export default { }, methods: { async clearSelected() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.selected = 'all' this.show = false this.$nextTick(() => this.$emit('change', 'all')) @@ -232,7 +232,7 @@ export default { this.show = false return } - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.selected = val this.show = false this.$nextTick(() => this.$emit('change', val)) diff --git a/components/modals/LibrariesModal.vue b/components/modals/LibrariesModal.vue index 378f866a..390bca17 100644 --- a/components/modals/LibrariesModal.vue +++ b/components/modals/LibrariesModal.vue @@ -49,7 +49,7 @@ export default { }, methods: { async clickedOption(lib) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.show = false if (lib.id === this.currentLibraryId) return await this.$store.dispatch('libraries/fetch', lib.id) diff --git a/components/modals/OrderModal.vue b/components/modals/OrderModal.vue index c46120b4..15e21c91 100644 --- a/components/modals/OrderModal.vue +++ b/components/modals/OrderModal.vue @@ -127,7 +127,7 @@ export default { }, methods: { async clickedOption(val) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.selected === val) { this.selectedDesc = !this.selectedDesc } else { diff --git a/components/modals/SleepTimerModal.vue b/components/modals/SleepTimerModal.vue index 90f2e325..21cb55d5 100644 --- a/components/modals/SleepTimerModal.vue +++ b/components/modals/SleepTimerModal.vue @@ -90,36 +90,36 @@ export default { }, methods: { async clickedChapterOption() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.show = false this.$nextTick(() => this.$emit('change', { time: this.currentEndOfChapterTime * 1000, isChapterTime: true })) }, async clickedOption(timeoutMin) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() var timeout = timeoutMin * 1000 * 60 this.show = false this.manualTimerModal = false this.$nextTick(() => this.$emit('change', { time: timeout, isChapterTime: false })) }, async cancelSleepTimer() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.$emit('cancel') this.show = false }, async increaseSleepTime() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.$emit('increase') }, async decreaseSleepTime() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.$emit('decrease') }, async increaseManualTimeout() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.manualTimeoutMin++ }, async decreaseManualTimeout() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.manualTimeoutMin > 1) this.manualTimeoutMin-- } }, diff --git a/components/modals/playlists/AddCreateModal.vue b/components/modals/playlists/AddCreateModal.vue index 25cf827e..f74d3177 100644 --- a/components/modals/playlists/AddCreateModal.vue +++ b/components/modals/playlists/AddCreateModal.vue @@ -115,7 +115,7 @@ export default { }) }, async clickPlaylist(playlist) { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (playlist.isItemIncluded) { this.removeFromPlaylist(playlist) } else { @@ -165,7 +165,7 @@ export default { this.showPlaylistNameInput = true }, async submitCreatePlaylist() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (!this.newPlaylistName || !this.selectedPlaylistItems.length) { return } diff --git a/components/tables/podcast/EpisodeRow.vue b/components/tables/podcast/EpisodeRow.vue index 87d97352..1d7ea556 100644 --- a/components/tables/podcast/EpisodeRow.vue +++ b/components/tables/podcast/EpisodeRow.vue @@ -187,7 +187,7 @@ export default { }, async downloadClick() { if (this.downloadItem) return - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isIos) { // no local folders on iOS this.startDownload() @@ -248,7 +248,7 @@ export default { } }, async playClick() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.streamIsPlaying) { this.$eventBus.$emit('pause-item') } else { @@ -270,7 +270,7 @@ export default { } }, async toggleFinished() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.isProcessingReadUpdate = true if (this.isLocal || this.localEpisode) { var isFinished = !this.userIsFinished diff --git a/components/ui/Dropdown.vue b/components/ui/Dropdown.vue index 4c2aa647..9a2416e7 100644 --- a/components/ui/Dropdown.vue +++ b/components/ui/Dropdown.vue @@ -61,7 +61,7 @@ export default { return this.selectedItem ? this.selectedItem.text : '' }, buttonClass() { - var classes = [] + const classes = [] if (this.small) classes.push('h-9') else classes.push('h-10') diff --git a/ios/App/App/AppDelegate.swift b/ios/App/App/AppDelegate.swift index 6a7d2fe5..8737ccc3 100644 --- a/ios/App/App/AppDelegate.swift +++ b/ios/App/App/AppDelegate.swift @@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Override point for customization after application launch. let configuration = Realm.Configuration( - schemaVersion: 5, + schemaVersion: 6, migrationBlock: { [weak self] migration, oldSchemaVersion in if (oldSchemaVersion < 1) { self?.logger.log("Realm schema version was \(oldSchemaVersion)") @@ -36,6 +36,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { newObject?["lockOrientation"] = "NONE" } } + if (oldSchemaVersion < 6) { + self?.logger.log("Realm schema version was \(oldSchemaVersion)... Adding hapticFeedback setting") + migration.enumerateObjects(ofType: DeviceSettings.className()) { oldObject, newObject in + newObject?["hapticFeedback"] = "LIGHT" + } + } } ) Realm.Configuration.defaultConfiguration = configuration diff --git a/ios/App/App/plugins/AbsDatabase.swift b/ios/App/App/plugins/AbsDatabase.swift index 02a935c3..9ed5f3e9 100644 --- a/ios/App/App/plugins/AbsDatabase.swift +++ b/ios/App/App/plugins/AbsDatabase.swift @@ -237,12 +237,14 @@ public class AbsDatabase: CAPPlugin { let jumpBackwardsTime = call.getInt("jumpBackwardsTime") ?? 10 let jumpForwardTime = call.getInt("jumpForwardTime") ?? 10 let lockOrientation = call.getString("lockOrientation") ?? "NONE" + let hapticFeedback = call.getString("hapticFeedback") ?? "LIGHT" let settings = DeviceSettings() settings.disableAutoRewind = disableAutoRewind settings.enableAltView = enableAltView settings.jumpBackwardsTime = jumpBackwardsTime settings.jumpForwardTime = jumpForwardTime settings.lockOrientation = lockOrientation + settings.hapticFeedback = hapticFeedback Database.shared.setDeviceSettings(deviceSettings: settings) diff --git a/ios/App/Shared/models/DeviceSettings.swift b/ios/App/Shared/models/DeviceSettings.swift index dc687cd2..6ef07c79 100644 --- a/ios/App/Shared/models/DeviceSettings.swift +++ b/ios/App/Shared/models/DeviceSettings.swift @@ -14,6 +14,7 @@ class DeviceSettings: Object { @Persisted var jumpBackwardsTime: Int = 10 @Persisted var jumpForwardTime: Int = 10 @Persisted var lockOrientation: String = "NONE" + @Persisted var hapticFeedback: String = "LIGHT" } func getDefaultDeviceSettings() -> DeviceSettings { @@ -26,6 +27,7 @@ func deviceSettingsToJSON(settings: DeviceSettings) -> Dictionary { "enableAltView": settings.enableAltView, "jumpBackwardsTime": settings.jumpBackwardsTime, "jumpForwardTime": settings.jumpForwardTime, - "lockOrientation": settings.lockOrientation + "lockOrientation": settings.lockOrientation, + "hapticFeedback": settings.hapticFeedback ] } diff --git a/layouts/default.vue b/layouts/default.vue index 3c4754b5..e40f6f42 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -94,11 +94,16 @@ export default { } this.attemptingConnection = true - var deviceData = await this.$db.getDeviceData() - var serverConfig = null - if (deviceData && deviceData.lastServerConnectionConfigId && deviceData.serverConnectionConfigs.length) { - serverConfig = deviceData.serverConnectionConfigs.find((scc) => scc.id == deviceData.lastServerConnectionConfigId) + const deviceData = await this.$db.getDeviceData() + let serverConfig = null + if (deviceData) { + this.$store.commit('globals/setHapticFeedback', deviceData.hapticFeedback) + + if (deviceData.lastServerConnectionConfigId && deviceData.serverConnectionConfigs.length) { + serverConfig = deviceData.serverConnectionConfigs.find((scc) => scc.id == deviceData.lastServerConnectionConfigId) + } } + if (!serverConfig) { // No last server config set this.attemptingConnection = false @@ -107,9 +112,9 @@ export default { console.log(`[default] Got server config, attempt authorize ${serverConfig.address}`) - var authRes = await this.$axios.$post(`${serverConfig.address}/api/authorize`, null, { headers: { Authorization: `Bearer ${serverConfig.token}` }, timeout: 3000 }).catch((error) => { + const authRes = await this.$axios.$post(`${serverConfig.address}/api/authorize`, null, { headers: { Authorization: `Bearer ${serverConfig.token}` }, timeout: 3000 }).catch((error) => { console.error('[Server] Server auth failed', error) - var errorMsg = error.response ? error.response.data || 'Unknown Error' : 'Unknown Error' + const errorMsg = error.response ? error.response.data || 'Unknown Error' : 'Unknown Error' this.error = errorMsg return false }) @@ -122,13 +127,13 @@ export default { this.$store.commit('setServerSettings', serverSettings) // Set library - Use last library if set and available fallback to default user library - var lastLibraryId = await this.$localStore.getLastLibraryId() + const lastLibraryId = await this.$localStore.getLastLibraryId() if (lastLibraryId && (!user.librariesAccessible.length || user.librariesAccessible.includes(lastLibraryId))) { this.$store.commit('libraries/setCurrentLibrary', lastLibraryId) } else if (userDefaultLibraryId) { this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId) } - var serverConnectionConfig = await this.$db.setServerConnectionConfig(serverConfig) + const serverConnectionConfig = await this.$db.setServerConnectionConfig(serverConfig) this.$store.commit('user/setUser', user) this.$store.commit('user/setServerConnectionConfig', serverConnectionConfig) diff --git a/pages/account.vue b/pages/account.vue index 27fb5ff6..ef3f31d1 100644 --- a/pages/account.vue +++ b/pages/account.vue @@ -53,7 +53,7 @@ export default { }, methods: { async logout() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.user) { await this.$axios.$post('/logout').catch((error) => { console.error(error) diff --git a/pages/item/_id.vue b/pages/item/_id.vue index 3df842e5..e9c1b93c 100644 --- a/pages/item/_id.vue +++ b/pages/item/_id.vue @@ -402,7 +402,7 @@ export default { }, async playClick() { let episodeId = null - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isPodcast) { this.episodes.sort((a, b) => { @@ -456,7 +456,7 @@ export default { this.$eventBus.$emit('play-item', { libraryItemId: this.libraryItemId, episodeId }) }, async clearProgressClick() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() const { value } = await Dialog.confirm({ title: 'Confirm', message: 'Are you sure you want to reset your progress?' @@ -509,7 +509,7 @@ export default { if (!this.numTracks) { return } - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (this.isIos) { // no local folders on iOS this.startDownload() @@ -577,7 +577,7 @@ export default { } }, async toggleFinished() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.isProcessingReadUpdate = true if (this.isLocal) { var isFinished = !this.userIsFinished diff --git a/pages/localMedia/item/_id.vue b/pages/localMedia/item/_id.vue index 72cb0f57..fd7ab3eb 100644 --- a/pages/localMedia/item/_id.vue +++ b/pages/localMedia/item/_id.vue @@ -256,7 +256,7 @@ export default { this.showDialog = true }, async play() { - await this.$hapticsImpactMedium() + await this.$hapticsImpact() this.$eventBus.$emit('play-item', { libraryItemId: this.localLibraryItemId }) }, getCapImageSrc(contentUrl) { @@ -264,7 +264,7 @@ export default { }, async dialogAction(action) { console.log('Dialog action', action) - await this.$hapticsImpactMedium() + await this.$hapticsImpact() if (action == 'scan') { this.scanItem() } else if (action == 'rescan') { diff --git a/pages/settings.vue b/pages/settings.vue index 1787acdc..3688eeb7 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -1,6 +1,7 @@