summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCarl Schwan <carl@carlschwan.eu>2022-08-30 19:27:37 +0200
committerCarl Schwan <carl@carlschwan.eu>2022-08-30 19:33:16 +0200
commit47cc783b637de75a72a6f8b721edb3cb34934ea0 (patch)
tree4fd983f9583a3770dbc98d20b66f9787d5f42f9d
parent753e88793e0525cadd8a544e3f4093cc3cf7b331 (diff)
Import existing attempt to port to vue
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
-rw-r--r--src/App.vue25
-rw-r--r--src/components/AddFeed.vue171
-rw-r--r--src/components/Explore.vue78
-rw-r--r--src/components/Sidebar.vue211
-rw-r--r--src/main.js132
-rw-r--r--templates/index.php5
-rw-r--r--webpack.config.js1
7 files changed, 623 insertions, 0 deletions
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 000000000..ad80f18d5
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,25 @@
+<template>
+ <Content app-name="news">
+ <Sidebar />
+ <AppContent>
+ <router-view />
+ </AppContent>
+ </Content>
+</template>
+
+<script>
+import Content from '@nextcloud/vue/dist/Components/Content'
+import AppContent from '@nextcloud/vue/dist/Components/AppContent'
+import Sidebar from './components/Sidebar.vue'
+
+export default {
+ components: {
+ Content,
+ Sidebar,
+ AppContent,
+ },
+ created() {
+ this.$store.dispatch('loadFolder')
+ },
+}
+</script>
diff --git a/src/components/AddFeed.vue b/src/components/AddFeed.vue
new file mode 100644
index 000000000..7ab421554
--- /dev/null
+++ b/src/components/AddFeed.vue
@@ -0,0 +1,171 @@
+<template>
+ <NcModal @close="$emit('close')">
+ <div id="new-feed" news-add-feed="Navigation.feed">
+ <form ng-submit="Navigation.createFeed(Navigation.feed)"
+ ng-init="Navigation.feed.autoDiscover=true"
+ name="feedform">
+ <fieldset ng-disabled="Navigation.addingFeed" style="padding: 16px">
+ <input type="text"
+ :value="feed"
+ ng-model="Navigation.feed.url"
+ ng-class="{'ng-invalid':
+ !Navigation.addingFeed &&
+ Navigation.feedUrlExists(Navigation.feed.url)
+ }"
+ :placeholder="t('news', 'Web address')"
+ name="address"
+ pattern="[^\s]+"
+ required
+ autofocus>
+
+ <p class="error"
+ ng-show="!Navigation.addingFeed &&
+ Navigation.feedUrlExists(Navigation.feed.url)">
+ {{ t("news", "Feed exists already!") }}
+ </p>
+
+ <!-- select a folder -->
+ <CheckboxRadioSwitch :checked.sync="createNewFolder" type="switch">
+ {{ t("news", "New folder") }}?
+ </CheckboxRadioSwitch>
+
+ <NcMultiselect v-if="!createNewFolder"
+ v-model="folder"
+ :options="folders"
+ track-by="id"
+ label="name" />
+
+ <!-- add a folder -->
+ <input v-if="createNewFolder"
+ type="text"
+ ng-model="Navigation.feed.newFolder"
+ ng-class="{'ng-invalid':
+ !Navigation.addingFeed &&
+ !Navigation.addingFeed &&
+ Navigation.showNewFolder &&
+ Navigation.folderNameExists(
+ Navigation.feed.newFolder
+ )
+ }"
+ :placeholder="t('news', 'Folder name')"
+ name="folderName"
+ style="width: 90%"
+ required>
+
+ <p class="error"
+ ng-show="!Navigation.addingFeed &&
+ Navigation.folderNameExists(Navigation.feed.newFolder)">
+ {{ t("news", "Folder exists already!") }}
+ </p>
+
+ <!-- basic auth -->
+
+ <CheckboxRadioSwitch :checked.sync="withBasicAuth" type="switch">
+ {{ t("news", "Credentials") }}?
+ </CheckboxRadioSwitch>
+
+ <div v-if="withBasicAuth" class="add-feed-basicauth">
+ <p class="warning">
+ {{
+ t(
+ "news",
+ "HTTP Basic Auth credentials must be stored unencrypted! Everyone with access to the server or database will be able to access them!"
+ )
+ }}>
+ </p>
+ <input type="text"
+ ng-model="Navigation.feed.user"
+ :placeholder="t('news', 'Username')"
+ name="user"
+ autofocus>
+
+ <input type="password"
+ ng-model="Navigation.feed.password"
+ :placeholder="t('news', 'Password')"
+ name="password"
+ autocomplete="new-password">
+ </div>
+
+ <NcCheckboxRadioSwitch :checked.sync="autoDiscover" type="switch">
+ {{ t("news", "Auto discover Feed") }}?
+ </NcCheckboxRadioSwitch>
+
+ <NcButton :wide="true"
+ type="primary"
+ ng-disabled="
+ Navigation.feedUrlExists(Navigation.feed.url) ||
+ (
+ Navigation.showNewFolder &&
+ Navigation.folderNameExists(folder.name)
+ )"
+ @click="addFeed()">
+ {{ t("news", "Subscribe") }}
+ </NcButton>
+ </fieldset>
+ </form>
+ </div>
+ </NcModal>
+</template>
+
+<script>
+/* eslint-disable vue/require-prop-type-constructor */
+
+import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
+import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
+import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect.js'
+
+export default {
+ name: 'AddFeed',
+ components: {
+ NcModal,
+ NcCheckboxRadioSwitch,
+ NcButton,
+ NcMultiselect,
+ },
+ props: {
+ feed: '',
+ },
+ emits: ['close'],
+ data() {
+ return {
+ folder: {},
+ autoDiscover: true,
+ createNewFolder: false,
+ withBasicAuth: false,
+ }
+ },
+ computed: {
+ folders() {
+ return this.$store.state.folders
+ },
+ },
+ methods: {
+ newFolder() {
+ this.createNewFolder = true
+ },
+ abortNewFolder() {
+ this.createNewFolder = false
+ },
+ addFeed() {
+ this.$store.dispatch('addFeed', {
+ feedReq: {
+ url: this.feed,
+ folder: this.folder,
+ autoDiscover: true,
+ },
+ })
+ },
+ },
+}
+</script>
+
+<style scoped>
+input {
+ width: 100%
+}
+
+.multiselect {
+ width: 100%
+}
+</style>
diff --git a/src/components/Explore.vue b/src/components/Explore.vue
new file mode 100644
index 000000000..562d72ae8
--- /dev/null
+++ b/src/components/Explore.vue
@@ -0,0 +1,78 @@
+<template>
+ <div id="explore">
+ <AddFeed v-if="showAddFeed" :feed="feed" @close="closeShowAddFeed()" />
+ <div class="grid-container">
+ <div v-for="entry in exploreSites"
+ :key="entry.title"
+ class="explore-feed grid-item">
+ <h2 v-if="entry.favicon"
+ class="explore-title"
+ :style="{ backgroundImage: 'url(' + entry.favicon + ')' }">
+ <a target="_blank" rel="noreferrer" :href="entry.url">
+ {{ entry.title }}
+ </a>
+ </h2>
+ <h2 v-if="!entry.favicon" class="icon-rss explore-title">
+ {{ entry.title }}
+ </h2>
+ <div class="explore-content">
+ <p>{{ entry.description }}</p>
+
+ <div class="explore-logo">
+ <img :src="entry.image">
+ </div>
+ </div>
+ <Button @click="subscribe(entry.feed)">
+ {{ t("news", "Subscribe to") }} {{ entry.title }}
+ </Button>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import axios from '@nextcloud/axios'
+import AddFeed from './AddFeed.vue'
+import { generateUrl } from '@nextcloud/router'
+
+export default {
+ name: 'ExploreComponent',
+ components: {
+ NcButton,
+ AddFeed,
+ },
+ data() {
+ return {
+ exploreSites: [],
+ feed: {},
+ showAddFeed: false,
+ }
+ },
+ created() {
+ this.sites()
+ },
+
+ methods: {
+ async sites() {
+ const settings = await axios.get(generateUrl('/apps/news/settings'))
+
+ const exploreUrl = settings.data.settings.exploreUrl + 'feeds.en.json'
+ const explore = await axios.get(exploreUrl)
+
+ Object.keys(explore.data).forEach((key) =>
+ explore.data[key].forEach((value) =>
+ this.exploreSites.push(value),
+ ),
+ )
+ },
+ async subscribe(feed) {
+ this.feed = feed
+ this.showAddFeed = true
+ },
+ closeShowAddFeed() {
+ this.showAddFeed = false
+ },
+ },
+}
+</script>
diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue
new file mode 100644
index 000000000..0b17b77ed
--- /dev/null
+++ b/src/components/Sidebar.vue
@@ -0,0 +1,211 @@
+<template>
+ <NcAppNavigation>
+ <AddFeed v-if="showAddFeed" @close="closeShowAddFeed()" />
+ <NcAppNavigationNew :text="t('news', 'Subscribe')"
+ button-id="new-feed-button"
+ button-class="icon-add"
+ @click="showShowAddFeed()" />
+
+ <div class="news-navigation">
+ <NcAppNavigationNewItem :title="t('news', 'New folder')" @new-item="newFolder">
+ <template #icon>
+ <PlusCircle :size="16" />
+ </template>
+ </NcAppNavigationNewItem>
+
+ <NcAppNavigationItem :title="t('news', 'Unread articles')" icon="icon-rss">
+ <template #actions>
+ <ActionButton icon="icon-checkmark" @click="alert('Edit')">
+ t('news','Mark read')
+ </ActionButton>
+ </template>
+ <template #counter>
+ <NcCounterBubble>5</NcCounterBubble>
+ </template>
+ </NcAppNavigationItem>
+ <NcAppNavigationItem :title="t('news', 'All articles')" icon="icon-rss">
+ <template #actions>
+ <NcActionButton icon="icon-checkmark" @click="alert('Edit')">
+ t('news','Mark read')
+ </NcActionButton>
+ </template>
+ </NcAppNavigationItem>
+ <NcAppNavigationItem :title="t('news', 'Starred')" icon="icon-starred">
+ <template #counter>
+ <NcCounterBubble>35</NcCounterBubble>
+ </template>
+ </NcAppNavigationItem>
+
+ <NcAppNavigationItem v-for="folder in folders"
+ :key="folder.name"
+ :title="folder.name"
+ icon="icon-folder"
+ :allow-collapse="true">
+ <template #default>
+ <NcAppNavigationItem v-for="feed in folder.feeds"
+ :key="feed.name"
+ :title="feed.title">
+ <template #icon>
+ <img v-if="feed.faviconLink"
+ :src="feed.faviconLink"
+ alt="feedIcon">
+ <div v-if="!feed.faviconLink" class="icon-rss" />
+ </template>
+ <template #actions>
+ <NcActionButton icon="icon-checkmark" @click="alert('Mark read')">
+ {{ t("news", "Mark read") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-pinned" @click="alert('Rename')">
+ {{ t("news", "Unpin from top") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-caret-dark"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Newest first") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-caret-dark"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Oldest first") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-caret-dark"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Default order") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-full-text-disabled"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Enable full text") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-full-text-enabled"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Disable full text") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-updatemode-default"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Unread updated") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-updatemode-unread"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Ignore updated") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-icon-rss" @click="deleteFolder(folder)">
+ {{ t("news", "Open feed URL") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-icon-rename"
+ @click="deleteFolder(folder)">
+ {{ t("news", "Rename") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-delete" @click="deleteFolder(folder)">
+ {{ t("news", "Delete") }}
+ </NcActionButton>
+ </template>
+ </NcAppNavigationItem>
+ </template>
+ <template v-if="folder.feedCount > 0" #counter>
+ <NcCounterBubble>{{ folder.feedCount }}</NcCounterBubble>
+ </template>
+ <template #actions>
+ <NcActionButton icon="icon-checkmark" @click="alert('Mark read')">
+ {{ t("news", "Mark read") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-rename" @click="alert('Rename')">
+ {{ t("news", "Rename") }}
+ </NcActionButton>
+ <NcActionButton icon="icon-delete" @click="deleteFolder(folder)">
+ {{ t("news", "Delete") }}
+ </NcActionButton>
+ </template>
+ </NcAppNavigationItem>
+
+ <NcAppNavigationItem :title="t('news', 'Explore')"
+ icon="icon-link"
+ :to="{ name: 'explore' }">
+ <template #counter>
+ <NcCounterBubble>35</NcCounterBubble>
+ </template>
+ </NcAppNavigationItem>
+ </div>
+ </NcAppNavigation>
+</template>
+
+<script>
+
+import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
+import NcAppNavigationNew from '@nextcloud/vue/dist/Components/NcAppNavigationNew.js'
+import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
+import NcAppNavigationNewItem from '@nextcloud/vue/dist/Components/NcAppNavigationNewItem.js'
+// import AppNavigationCounter from '@nextcloud/vue/dist/Components/AppNavigationCounter'
+import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
+import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
+import AddFeed from './AddFeed.vue'
+import PlusCircle from 'vue-material-design-icons/PlusCircle.vue'
+
+export default {
+ components: {
+ NcAppNavigation,
+ NcAppNavigationNew,
+ NcAppNavigationItem,
+ NcAppNavigationNewItem,
+ // AppNavigationCounter,
+ NcCounterBubble,
+ NcActionButton,
+ AddFeed,
+ PlusCircle,
+ },
+ data: () => {
+ return {
+ showAddFeed: false,
+ }
+ },
+ computed: {
+ folders() {
+ return this.$store.state.folders
+ },
+ },
+ created() {
+ // TODO?
+ },
+ methods: {
+ newFolder(value) {
+ const folderName = value.trim()
+ const folder = { name: folderName }
+ this.$store.dispatch('addFolder', { folder })
+ },
+ deleteFolder(folder) {
+ this.$store.dispatch('deleteFolder', { folder })
+ window.location.reload(true)
+ },
+ showShowAddFeed() {
+ this.showAddFeed = true
+ },
+ closeShowAddFeed() {
+ this.showAddFeed = false
+ },
+ alert(msg) {
+ window.alert(msg)
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.news-navigation {
+ display: flex;
+ flex-direction: column;
+ padding-left: 10px;
+ padding-right: 10px;
+ gap: 10px;
+
+ & ::v-deep .app-navigation-input-confirm {
+ form {
+ gap: 4px;
+ align-items: center;
+ }
+
+ button.button-vue--icon-only {
+ min-height: 36px !important;
+ width: 36px !important;
+ height: 36px !important;
+ min-width: 36px !important;
+ }
+ }
+}
+</style>
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 000000000..ae9478a02
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,132 @@
+
+import Vue from 'vue'
+import App from './App.vue'
+import VueRouter from 'vue-router'
+import Explore from './components/Explore.vue'
+import { generateUrl } from '@nextcloud/router'
+import Vuex, { Store } from 'vuex'
+import axios from '@nextcloud/axios'
+
+import { Tooltip } from '@nextcloud/vue'
+
+Vue.prototype.t = t
+Vue.prototype.n = n
+Vue.prototype.OC = OC
+Vue.prototype.OCA = OCA
+
+Vue.use(Vuex)
+Vue.use(VueRouter)
+
+Vue.directive('tooltip', Tooltip)
+
+const feedUrl = generateUrl('/apps/news/feeds')
+const folderUrl = generateUrl('/apps/news/folders')
+
+const routes = [
+ {
+ name: 'explore',
+ path: '#explore',
+ component: Explore,
+ },
+]
+
+const router = new VueRouter({
+ mode: 'history',
+ base: generateUrl('apps/news'),
+ routes,
+})
+
+const store = new Store({
+ state: {
+ folders: [],
+ feeds: [],
+ },
+ mutations: {
+ addFolders(state, folders) {
+ folders.forEach((it) => {
+ it.feedCount = 0
+ state.folders.push(it)
+ })
+ },
+ addFeeds(state, feeds) {
+ feeds.forEach((it) => {
+ state.feeds.push(it)
+ const folder = state.folders.find(
+ (folder) => folder.id === it.folderId,
+ )
+ if (folder) {
+ folder.feeds.push(it)
+ folder.feedCount += it.unreadCount
+ }
+ })
+ },
+ },
+ actions: {
+ addFolder({ commit }, { folder }) {
+ axios
+ .post(folderUrl, { folderName: folder.name })
+ .then((response) =>
+ commit('addFolders', response.data.folders),
+ )
+ },
+ deleteFolder({ commit }, { folder }) {
+ /**
+ this.getByFolderId(folderId).forEach(function (feed) {
+ promises.push(self.reversiblyDelete(feed.id, false, true));
+ });
+ this.updateUnreadCache();
+ */
+ axios.delete(folderUrl + '/' + folder.id).then()
+ },
+ loadFolder({ commit }) {
+ axios.get(folderUrl).then((response) => {
+ commit('addFolders', response.data.folders)
+ axios
+ .get(feedUrl)
+ .then((response) =>
+ commit('addFeeds', response.data.feeds),
+ )
+ })
+ },
+ addFeed({ commit }, { feedReq }) {
+ let url = feedReq.url.trim()
+ if (!url.startsWith('http')) {
+ url = 'https://' + url
+ }
+
+ /**
+ if (title !== undefined) {
+ title = title.trim();
+ }
+ */
+
+ const feed = {
+ url,
+ folderId: feedReq.folder.id || 0,
+ title: null,
+ unreadCount: 0,
+ }
+
+ // this.add(feed);
+ // this.updateFolderCache();
+
+ axios
+ .post(feedUrl, {
+ url: feed.url,
+ parentFolderId: feed.folderId,
+ title: null,
+ user: null,
+ password: null,
+ fullDiscover: feed.autoDiscover,
+ })
+ .then()
+ },
+ },
+})
+
+export default new Vue({
+ router,
+ store,
+ el: '#content',
+ render: (h) => h(App),
+})
diff --git a/templates/index.php b/templates/index.php
index 9ad38a0d5..38a3b3d27 100644
--- a/templates/index.php
+++ b/templates/index.php
@@ -1,4 +1,8 @@
<?php
+
+\OCP\Util::addScript('news', 'build/news-main');
+
+/*
use OCA\News\Plugin\Client\Plugin;
script('news', [
@@ -97,3 +101,4 @@ foreach (Plugin::getScripts() as $appName => $fileName) {
news-scroll-enabled-mark-read="Content.markReadEnabled()"
news-scroll-auto-page="Content.autoPage()"
news-scroll-mark-read="Content.scrollRead(itemIds)"></div>
+ */
diff --git a/webpack.config.js b/webpack.config.js
index c329f79d1..310550144 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -6,6 +6,7 @@ const webpackConfig = require('@nextcloud/webpack-vue-config')
webpackConfig.entry = {
'admin-settings': path.join(__dirname, 'src', 'main-admin.js'),
+ 'main': path.join(__dirname, 'src', 'main.js'),
}
webpackConfig.output.path = path.resolve('./js/build/')
webpackConfig.output.publicPath = path.join('/apps/', process.env.npm_package_name, '/js/build/')