Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Vue Rewrite] Share Article Component #2361

Merged
merged 6 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/Plugin/Client/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace OCA\News\Plugin\Client;

/**
* TODO: remove this? it seems old.. there is no global list of plugins anymore and seems like plugins register themselves
* TODO: remove this? it seems old.. there is no global list of plugins and seems plugins register themselves?
*
* We actually really want to avoid this global list of plugins. A way would be
* for News plugin apps to register themselves in a special database table
Expand Down
2 changes: 2 additions & 0 deletions lib/Service/ShareService.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ public function shareItemWithUser(string $userId, int $itemId, string $shareReci
$sharedItem->setStarred(false);
$sharedItem->setSharedBy($userId);

// TODO: should we set the pub date to the date of share?

// Get 'shared with me' dummy feed
$feedUrl = $this->urlGenerator->getBaseUrl() . '/news/sharedwithme';
$feed = $this->feedService->findByUrl($shareRecipientId, $feedUrl);
Expand Down
157 changes: 157 additions & 0 deletions src/components/ShareItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<template>
<NcModal @close="$emit('close')">
<div id="share-item">
<form name="feedform">
<fieldset>
<input v-model="userName"
type="text"
:placeholder="t('news', 'User Name')"
name="user"
pattern="[^\s]+"
required
autofocus
@keyup="debounceSearchUsers()">

<div class="user-bubble-container">
<NcLoadingIcon v-if="searching" />
<NcUserBubble v-for="user in users"
v-else-if="!searching"
:key="user.shareName"
:size="30"
:display-name="user.displayName"
:primary="selected.map((val) => { return val.shareName }).includes(user.shareName)"
:user="user.shareName"
@click="clickUser(user)" />
</div>

<NcButton :wide="true"
type="primary"
:disabled="selected.length === 0"
@click="share()">
<template v-if="selected.length === 0">
{{ t("news", "Share") }}
</template>
<template v-else-if="selected.length === 1">
{{ t("news", "Share with") + ' ' + selected[0].displayName }}
</template>
<template v-else-if="selected.length > 1">
{{ t("news", "Share with {num} users", { num: selected.length }) }}
</template>
</NcButton>
</fieldset>
</form>
</div>
</NcModal>
</template>

<script lang="ts">

import Vue from 'vue'
import _ from 'lodash'

import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'

import { ShareService } from '../dataservices/share.service'

type ShareUser = {
shareName: string;
displayName: string;
}

export default Vue.extend({
components: {
NcModal,
NcButton,
NcUserBubble,
NcLoadingIcon,
},
props: {
itemId: {
type: Number,
required: true,
},
},
data: () => {
return {
userName: '',
users: [],
selected: [],
searching: false,
} as {
userName: string;
users: ShareUser[];
selected: ShareUser[];
searching: boolean;
debounceSearchUsers?: () => void;
}
},
created() {
this.debounceSearchUsers = _.debounce(this.searchUsers, 800)
},
methods: {
/**
* Adds or removes user to selected list
*
* @param user {ShareUser} user that was clicked

Check warning on line 98 in src/components/ShareItem.vue

View workflow job for this annotation

GitHub Actions / eslint node

Missing JSDoc @param "user" type

Check warning on line 98 in src/components/ShareItem.vue

View workflow job for this annotation

GitHub Actions / eslint

Missing JSDoc @param "user" type
*/
clickUser(user: ShareUser) {
const selectedUsers = this.selected.map((val: ShareUser) => { return val.shareName })
if (selectedUsers.includes(user.shareName)) {
this.selected.splice(selectedUsers.indexOf(user.shareName), 1)
} else {
this.selected.push(user)
}
},

/**
* Searches for Users based on user input to display for sharing
*/
async searchUsers() {
this.users = []
this.searching = true
const response = await ShareService.fetchUsers(this.userName)
this.searching = false

for (const user of response.data.ocs.data.users) {
this.users.push({ displayName: user.label, shareName: user.value.shareWith })
}
},

/**
* Shares an item with another use in the same nextcloud instance
*/
async share() {
await ShareService.share(this.itemId, this.selected.map((val: ShareUser) => { return val.shareName }))

this.$emit('close')
},
},
})

</script>

<style>
#share-item .user-bubble__content * {
cursor: pointer;
}

#share-item fieldset {
padding: 16px;
}

#share-item input {
width: 90%;
}

#share-item .user-bubble-container {
margin: 10px;
}

#share-item .user-bubble__wrapper {
margin: 5px 10px;
}

</style>
2 changes: 1 addition & 1 deletion src/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
</NcCounterBubble>
</template>
<template #actions>
<SidebarFeedLinkActions v-if="topLevelItem.name === undefined" :feed-id="topLevelItem.id" />
<SidebarFeedLinkActions v-if="topLevelItem.name === undefined && !topLevelItem.url.includes('news/sharedwithme')" :feed-id="topLevelItem.id" />
<NcActionButton v-if="topLevelItem.name !== undefined" icon="icon-checkmark" @click="markFolderRead(topLevelItem)">
{{ t("news", "Mark read") }}
</NcActionButton>
Expand Down
21 changes: 13 additions & 8 deletions src/components/feed-display/FeedItemDisplay.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
<template>
<div class="feed-item-display">
<ShareItem v-if="showShareMenu" :item-id="item.id" @close="closeShareMenu()" />

<div class="action-bar">
<NcActions :force-menu="true">
<template #icon>
<ShareVariant />
</template>
<NcActionButton>
<template #default>
<!-- TODO: Share Menu --> TODO
</template>
<NcActions>
<NcActionButton :title="t('news', 'Share within Instance')" @click="showShareMenu = true">
{{ t('news', 'Share') }}
<template #icon>
<ShareVariant />
</template>
Expand Down Expand Up @@ -83,9 +80,9 @@
<a :href="item.enclosureLink"><img :src="item.mediaThumbnail" alt=""></a>
</div>

<div v-if="item.mediaDescription" class="enclosure description" v-html="item.mediaDescription" />

Check warning on line 83 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint node

'v-html' directive can lead to XSS attack

Check warning on line 83 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint

'v-html' directive can lead to XSS attack

<div class="body" :dir="item.rtl && 'rtl'" v-html="item.body" />

Check warning on line 85 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint node

'v-html' directive can lead to XSS attack

Check warning on line 85 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint

'v-html' directive can lead to XSS attack
</div>
</div>
</template>
Expand All @@ -101,6 +98,8 @@
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'

import ShareItem from '../ShareItem.vue'

import { Feed } from '../../types/Feed'
import { FeedItem } from '../../types/FeedItem'
import { ACTIONS, MUTATIONS } from '../../store'
Expand All @@ -113,6 +112,7 @@
ShareVariant,
NcActions,
NcActionButton,
ShareItem,
},
props: {
item: {
Expand All @@ -123,6 +123,7 @@
data: () => {
return {
keepUnread: false,
showShareMenu: false,
}
},
computed: {
Expand Down Expand Up @@ -171,11 +172,15 @@
this.$store.dispatch(item.starred ? ACTIONS.UNSTAR_ITEM : ACTIONS.STAR_ITEM, { item })
},

closeShareMenu() {
this.showShareMenu = false
},

getMediaType(mime: string): 'audio' | 'video' | false {

Check warning on line 179 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint node

'mime' is defined but never used

Check warning on line 179 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint

'mime' is defined but never used
// TODO: figure out how to check media type
return false
},
play(item: FeedItem) {

Check warning on line 183 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint node

'item' is defined but never used

Check warning on line 183 in src/components/feed-display/FeedItemDisplay.vue

View workflow job for this annotation

GitHub Actions / eslint

'item' is defined but never used
// TODO: implement play audio/video
},
},
Expand Down
32 changes: 18 additions & 14 deletions src/components/feed-display/FeedItemRow.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<div class="feed-item-row" @click="select()">
<ShareItem v-if="showShareMenu" :item-id="shareItem" @close="closeShareMenu()" />
<div class="link-container">
<a class="external"
target="_blank"
Expand All @@ -15,7 +16,7 @@
<div class="title-container" :class="{ 'unread': item.unread }">
<span style="white-space: nowrap" :dir="item.rtl && 'rtl'">
{{ item.title }}
<span class="intro" v-html="item.intro" />

Check warning on line 19 in src/components/feed-display/FeedItemRow.vue

View workflow job for this annotation

GitHub Actions / eslint node

'v-html' directive can lead to XSS attack

Check warning on line 19 in src/components/feed-display/FeedItemRow.vue

View workflow job for this annotation

GitHub Actions / eslint

'v-html' directive can lead to XSS attack
</span>
</div>
<div class="date-container">
Expand All @@ -28,14 +29,9 @@
<EyeIcon v-if="item.unread && !keepUnread" @click="toggleKeepUnread(item)" />
<EyeCheckIcon v-if="!item.unread && !keepUnread" @click="toggleKeepUnread(item)" />
<EyeLockIcon v-if="keepUnread" class="keep-unread" @click="toggleKeepUnread(item)" />
<NcActions :force-menu="true">
<template #icon>
<ShareVariant />
</template>
<NcActionButton>
<template #default>
<!-- TODO: Share Menu --> TODO
</template>
<NcActions>
<NcActionButton :title="t('news', 'Share within Instance')" @click="shareItem = item.id; showShareMenu = true">
{{ t('news', 'Share') }}
<template #icon>
<ShareVariant />
</template>
Expand All @@ -60,6 +56,8 @@
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'

import ShareItem from '../ShareItem.vue'

import { Feed } from '../../types/Feed'
import { FeedItem } from '../../types/FeedItem'
import { ACTIONS, MUTATIONS } from '../../store'
Expand All @@ -76,6 +74,7 @@
RssIcon,
NcActions,
NcActionButton,
ShareItem,
},
props: {
item: {
Expand All @@ -86,6 +85,8 @@
data: () => {
return {
keepUnread: false,
showShareMenu: false,
shareItem: undefined,
}
},
computed: {
Expand Down Expand Up @@ -114,17 +115,17 @@
const elapsed = current - previous

if (elapsed < msPerMinute) {
return Math.round(elapsed / 1000) + ' ' + t('news', 'seconds')
return t('news', '{num} seconds', { num: Math.round(elapsed / 1000) })
} else if (elapsed < msPerHour) {
return Math.round(elapsed / msPerMinute) + ' ' + t('news', 'minutes ago')
return t('news', '{num} minutes ago', { num: Math.round(elapsed / msPerMinute) })
} else if (elapsed < msPerDay) {
return Math.round(elapsed / msPerHour) + ' ' + t('news', 'hours ago')
return t('news', '{num} hours ago', { num: Math.round(elapsed / msPerHour) })
} else if (elapsed < msPerMonth) {
return Math.round(elapsed / msPerDay) + ' ' + t('news', 'days ago')
return t('news', '{num} days ago', { num: Math.round(elapsed / msPerDay) })
} else if (elapsed < msPerYear) {
return Math.round(elapsed / msPerMonth) + ' ' + t('news', 'months ago')
return t('news', '{num} months ago', { num: Math.round(elapsed / msPerMonth) })
} else {
return Math.round(elapsed / msPerYear) + ' ' + t('news', 'years ago')
return t('news', '{num} years ago', { num: Math.round(elapsed / msPerYear) })
}
},
getFeed(id: number): Feed {
Expand All @@ -142,6 +143,9 @@
toggleStarred(item: FeedItem): void {
this.$store.dispatch(item.starred ? ACTIONS.UNSTAR_ITEM : ACTIONS.STAR_ITEM, { item })
},
closeShareMenu() {
this.showShareMenu = false
},
},
})

Expand Down
28 changes: 28 additions & 0 deletions src/dataservices/share.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AxiosResponse } from 'axios'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'

export class ShareService {

/**
* Retrieves all of users matching the search term
*
* @param query {String} search string
* @return {AxiosResponse} Folders contained in data.folders property
*/
static fetchUsers(query: string): Promise<AxiosResponse> {
return axios.get(generateOcsUrl(`apps/files_sharing/api/v1/sharees?search=${query}&itemType=news_item&perPage=5/`))
}

static async share(id: number, users: string[]): Promise<boolean> {
const promises = []
for (const shareName of users) {
promises.push(axios.post(`items/${id}/share/${shareName}`))
}

await Promise.all(promises)

return true
}

}
2 changes: 1 addition & 1 deletion src/shims-vue.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ declare module '@nextcloud/vue/dist/Components/*' {
};

// Necessary to access translate in typescript components
function t(app, key): string { }
function t(app, key, params?): string { }
Loading
Loading