diff --git a/client/components/app/SideRail.vue b/client/components/app/SideRail.vue index dbc295d20..60bbf2203 100644 --- a/client/components/app/SideRail.vue +++ b/client/components/app/SideRail.vue @@ -71,6 +71,14 @@
+ + connect_without_contact + +

Social

+ +
+ + warning @@ -143,6 +151,9 @@ export default { isAuthorsPage() { return this.$route.name === 'library-library-authors' }, + isSocialPage() { + return this.$route.name === 'social' + }, libraryBookshelfPage() { return this.$route.name === 'library-library-bookshelf-id' }, @@ -152,6 +163,9 @@ export default { filterBy() { return this.$store.getters['user/getUserSetting']('filterBy') }, + isGlobalSharing() { + return this.$store.getters['getServerSetting']('sharedListeningStats') + }, showingIssues() { if (!this.$route.query) return false return this.libraryBookshelfPage && this.$route.query.filter === 'issues' @@ -182,4 +196,4 @@ export default { }, mounted() {} } - \ No newline at end of file + diff --git a/client/pages/account.vue b/client/pages/account.vue index 5f689d772..044f2c227 100644 --- a/client/pages/account.vue +++ b/client/pages/account.vue @@ -19,6 +19,18 @@
+
+ + +

+ {{ $strings.ShareStats }} + info_outlined +

+
+
+ +
+

{{ $strings.HeaderChangePassword }}

@@ -47,6 +59,7 @@ export default { newPassword: null, confirmPassword: null, changingPassword: false, + isUserSharing: null, selectedLanguage: '' } }, @@ -68,9 +81,15 @@ export default { }, isGuest() { return this.usertype === 'guest' + }, + isGlobalSharing() { + return this.$store.getters['getServerSetting']('sharedListeningStats') } }, methods: { + init() { + this.isUserSharing = this.$store.getters['user/getUserSetting']('shareListeningActivity') + }, updateLocalLanguage(lang) { this.$setLanguageCode(lang) }, @@ -119,10 +138,14 @@ export default { this.$toast.error('Api call failed') this.changingPassword = false }) + }, + changeSharingSetting() { + this.$store.dispatch('user/updateUserSettings', { shareListeningActivity: this.isUserSharing}) } }, mounted() { + this.init() this.selectedLanguage = this.$languageCodes.current } } - \ No newline at end of file + diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue index 600a4bab6..f9b370075 100644 --- a/client/pages/config/index.vue +++ b/client/pages/config/index.vue @@ -48,6 +48,16 @@

{{ $strings.LabelSettingsChromecastSupport }}

+
+ + +

+ {{ $strings.EnableSharedStats}} + info_outlined +

+
+
+

{{ $strings.HeaderSettingsDisplay }}

@@ -425,4 +435,4 @@ export default { this.initServerSettings() } } - \ No newline at end of file + diff --git a/client/pages/social.vue b/client/pages/social.vue new file mode 100644 index 000000000..a8973af5f --- /dev/null +++ b/client/pages/social.vue @@ -0,0 +1,93 @@ + + + diff --git a/client/strings/en-us.json b/client/strings/en-us.json index a0666173e..99cc8d675 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -472,6 +472,11 @@ "PlaceholderNewCollection": "New collection name", "PlaceholderNewFolderPath": "New folder path", "PlaceholderSearch": "Search..", + "EnableSharedStatsHelp": "Allow users to share latest listening activity with other users on an opt-in basis", + "EnableSharedStats": "Shared Listening Stats", + "ShareStatsHelp": "Share your latest listening activity with other users in the social tab", + "ShareStats": "Share Listening Activity", + "Social": "Social", "ToastAccountUpdateFailed": "Failed to update account", "ToastAccountUpdateSuccess": "Account updated", "ToastAuthorImageRemoveFailed": "Failed to remove image", @@ -538,4 +543,4 @@ "WeekdayThursday": "Thursday", "WeekdayTuesday": "Tuesday", "WeekdayWednesday": "Wednesday" -} \ No newline at end of file +} diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index cfa1dc44c..2cac4eca2 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -242,6 +242,34 @@ class MiscController { }) res.json(tags) } + // GET: api/social + async getSocialStats(req, res) { + if (!this.db.serverSettings.sharedListeningStats) { + return res.sendStatus(404) + } + var filteredUsers = this.db.users.filter(c => c.settings.shareListeningActivity == true) + var userData = [] + for (let i = 0; i < filteredUsers.length; i++) { + var user = filteredUsers[i] + var listeningStats = await this.getUserListeningStatsHelpers(user.id) + var latestItem = user.mediaProgress.sort(p => p.lastUpdate) + var latestProgress = latestItem[latestItem.length - 1] + if (latestProgress) { + var latestItem = this.db.getLibraryItem(latestProgress.id) + } + var session = { + username: user.username, + id: user.id, + lastSeen: user.lastSeen, + latest: {'progress': latestProgress, 'item': latestItem} || null, + minutesListened: listeningStats.totalTime || 0, + } + if (session.minutesListened && session.latest) { + userData.push(session) + } + } + res.json(userData) + } validateCronExpression(req, res) { const expression = req.body.expression @@ -258,4 +286,4 @@ class MiscController { } } } -module.exports = new MiscController() \ No newline at end of file +module.exports = new MiscController() diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js index cb4b0faec..371928791 100644 --- a/server/objects/settings/ServerSettings.js +++ b/server/objects/settings/ServerSettings.js @@ -51,6 +51,7 @@ class ServerSettings { // Misc Flags this.chromecastEnabled = false + this.sharedListeningStats = false this.enableEReader = false this.dateFormat = 'MM/dd/yyyy' this.language = 'en-us' @@ -101,6 +102,7 @@ class ServerSettings { this.sortingIgnorePrefix = !!settings.sortingIgnorePrefix this.sortingPrefixes = settings.sortingPrefixes || ['the'] this.chromecastEnabled = !!settings.chromecastEnabled + this.sharedListeningStats = !!settings.sharedListeningStats this.enableEReader = !!settings.enableEReader this.dateFormat = settings.dateFormat || 'MM/dd/yyyy' this.language = settings.language || 'en-us' @@ -153,6 +155,7 @@ class ServerSettings { sortingIgnorePrefix: this.sortingIgnorePrefix, sortingPrefixes: [...this.sortingPrefixes], chromecastEnabled: this.chromecastEnabled, + sharedListeningStats: this.sharedListeningStats, enableEReader: this.enableEReader, dateFormat: this.dateFormat, language: this.language, diff --git a/server/objects/user/User.js b/server/objects/user/User.js index b2db254b9..69c6bd085 100644 --- a/server/objects/user/User.js +++ b/server/objects/user/User.js @@ -69,7 +69,8 @@ class User { filterBy: 'all', playbackRate: 1, bookshelfCoverSize: 120, - collapseSeries: false + collapseSeries: false, + shareListeningActivity: false } } diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index 3de794eaf..a66c200a8 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -240,6 +240,7 @@ class ApiRouter { this.router.get('/search/podcast', MiscController.findPodcasts.bind(this)) this.router.get('/search/authors', MiscController.findAuthor.bind(this)) this.router.get('/search/chapters', MiscController.findChapters.bind(this)) + this.router.get('/social', MiscController.getSocialStats.bind(this)) this.router.get('/tags', MiscController.getAllTags.bind(this)) this.router.post('/validate-cron', MiscController.validateCronExpression.bind(this)) }