summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/App.vue16
-rw-r--r--src/api.js54
-rw-r--r--src/components/AddFeed.vue188
-rw-r--r--src/components/Explore.vue58
4 files changed, 206 insertions, 110 deletions
diff --git a/src/App.vue b/src/App.vue
index ad80f18d5..6ff5bf5b7 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,22 +1,22 @@
<template>
- <Content app-name="news">
+ <NcContent app-name="news">
<Sidebar />
- <AppContent>
+ <NcAppContent>
<router-view />
- </AppContent>
- </Content>
+ </NcAppContent>
+ </NcContent>
</template>
<script>
-import Content from '@nextcloud/vue/dist/Components/Content'
-import AppContent from '@nextcloud/vue/dist/Components/AppContent'
+import NcContent from '@nextcloud/vue/dist/Components/NcContent.js'
+import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
import Sidebar from './components/Sidebar.vue'
export default {
components: {
- Content,
+ NcContent,
+ NcAppContent,
Sidebar,
- AppContent,
},
created() {
this.$store.dispatch('loadFolder')
diff --git a/src/api.js b/src/api.js
new file mode 100644
index 000000000..b7a4e0df3
--- /dev/null
+++ b/src/api.js
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
+// SPDX-Licence-Identifier: AGPL-3.0-or-later
+
+import axios from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+
+export default {
+ folder: {
+ async create(name) {
+ const { data } = await axios.post(generateUrl('/apps/news/api/v2/folders'), {
+ name,
+ })
+ return data.folders
+ },
+ async update(folderId, name) {
+ await axios.patch(generateUrl('/apps/news/api/v2/folders/{folderId}', {
+ folderId,
+ name,
+ }))
+ },
+ async delete(folderId) {
+ await axios.post(generateUrl('/apps/news/api/v2/folders/{folderId}', {
+ folderId,
+ }))
+ },
+ async index() {
+ const { data } = await axios.post(generateUrl('/apps/news/api/v1-3/folders'))
+ return data.folders
+ },
+ async read(folderId, newestItemId = 0) {
+ const { data } = await axios.post(generateUrl('/apps/news/api/v1-3/folders/{folderId}/read', {
+ folderId,
+ }, {
+ newestItemId,
+ }))
+ },
+ },
+ feed: {
+ async get() {
+ const { data } = await axios.get(generateUrl('/apps/news/feeds'))
+ return data.feeds
+ }
+ async add({ feedUrl, folderId }) {
+ let url = feedUrl.trim()
+ if (!url.startsWith('http')) {
+ url = 'https://' + url
+ }
+ const { data } = await axios.post(generateUrl('/apps/news/feeds'), {
+ folderId,
+ url,
+ })
+ },
+ },
+}
diff --git a/src/components/AddFeed.vue b/src/components/AddFeed.vue
index 7ab421554..4718cb846 100644
--- a/src/components/AddFeed.vue
+++ b/src/components/AddFeed.vue
@@ -1,107 +1,82 @@
<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"
+ <NcModal @close="$emit('close')" :title="t('news', 'Add new feed')">
+ <div class="add-feed">
+ <h2>{{ t('news', 'Add new feed') }}</h2>
+ <form ng-submit="Navigation.createFeed(Navigation.feed)" name="feedform">
+ <NcTextField type="text"
+ :value.sync="feedUrl"
+ :label="t('news', 'Web address')"
+ placeholder="https://..."
+ :labelVisible="true"
+ pattern="[^\s]+"
+ required
+ autofocus
+ :error="feedAlreadyExists" />
+
+ <!-- select a folder -->
+ <NcCheckboxRadioSwitch :checked.sync="createNewFolder" type="switch">
+ {{ t("news", "New folder") }}?
+ </NcCheckboxRadioSwitch>
+
+ <div v-if="!createNewFolder">
+ <label for="name">{{ t('news', 'Folder name') }}</label>
+ <NcMultiselect 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!") }}
+ </div>
+
+ <!-- add a folder -->
+ <NcTextField v-if="createNewFolder"
+ :label="t('news', 'Folder name')"
+ :labelVisible="true"
+ :value.sync="folderName"
+ required />
+
+ <!-- basic auth -->
+ <NcCheckboxRadioSwitch :checked.sync="withBasicAuth" type="switch">
+ {{ t("news", "Credentials") }}?
+ </NcCheckboxRadioSwitch>
+
+ <fieldset 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>
-
- <!-- 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>
+ <NcTextField type="text"
+ :label="t('news', 'Username')"
+ :labelVisible="true"
+ :value="username"
+ name="user"
+ autofocus />
+
+ <NcTextField type="password"
+ :label="t('news', 'Password')"
+ :labelVisible="true"
+ :value="password"
+ name="password"
+ autocomplete="new-password" />
</fieldset>
+
+ <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>
</form>
</div>
</NcModal>
@@ -114,6 +89,7 @@ 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'
+import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
export default {
name: 'AddFeed',
@@ -122,6 +98,7 @@ export default {
NcCheckboxRadioSwitch,
NcButton,
NcMultiselect,
+ NcTextField,
},
props: {
feed: '',
@@ -129,10 +106,15 @@ export default {
emits: ['close'],
data() {
return {
+ feedUrl: '',
+ folderName: '',
+ password: '',
+ username: '',
folder: {},
autoDiscover: true,
createNewFolder: false,
withBasicAuth: false,
+ feedAlreadyExists: false,
}
},
computed: {
@@ -160,7 +142,15 @@ export default {
}
</script>
-<style scoped>
+<style scoped lang="scss">
+.add-feed {
+ padding: 1rem;
+ form {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ }
+}
input {
width: 100%
}
diff --git a/src/components/Explore.vue b/src/components/Explore.vue
index 562d72ae8..a491ca406 100644
--- a/src/components/Explore.vue
+++ b/src/components/Explore.vue
@@ -22,9 +22,12 @@
<img :src="entry.image">
</div>
</div>
- <Button @click="subscribe(entry.feed)">
- {{ t("news", "Subscribe to") }} {{ entry.title }}
- </Button>
+ <NcButton @click="subscribe(entry.feed)"
+ type="primary"
+ class="grid-item__button"
+ :wide="true">
+ {{ t("news", "Subscribe to {title}", {title: entry.title}) }}
+ </NcButton>
</div>
</div>
</div>
@@ -76,3 +79,52 @@ export default {
},
}
</script>
+
+<style lang="scss" scoped>
+.grid-container {
+ padding: 1rem;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ /* This is better for small screens, once min() is better supported */
+ /* grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); */
+ gap: 1rem;
+
+ .grid-item {
+ border: 2px solid var(--color-border);
+ border-radius: var(--border-radius-large);
+ padding: 1rem;
+ display: flex;
+ flex-direction: column;
+
+ &__button {
+ margin-top: auto;
+ }
+ }
+ .grid-item .explore-title {
+ background-repeat: no-repeat;
+ background-position: 0 center;
+ background-size: 24px;
+ padding-left: 32px;
+ }
+ .grid-item .explore-title a {
+ word-wrap: break-word;
+ }
+ .grid-item .explore-title a:hover, #explore .grid-item .explore-title a:focus {
+ text-decoration: underline;
+ }
+ .grid-item .explore-logo {
+ text-align: center;
+ margin-top: 25px;
+ }
+ .grid-item .explore-logo img {
+ width: 100%;
+ }
+ .grid-item .explore-subscribe {
+ margin-top: 16px;
+ max-width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+}
+</style>