Skip to content

Commit

Permalink
reuse component
Browse files Browse the repository at this point in the history
Signed-off-by: Swikriti Tripathi <swikriti808@gmail.com>

reuse

Signed-off-by: Swikriti Tripathi <swikriti808@gmail.com>

emit event

Signed-off-by: Swikriti Tripathi <swikriti808@gmail.com>

add op icon vue

Signed-off-by: Swikriti Tripathi <swikriti808@gmail.com>
  • Loading branch information
SwikritiT committed Aug 1, 2023
1 parent b10c873 commit 3906597
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 42 deletions.
27 changes: 27 additions & 0 deletions lib/Listener/OpenProjectReferenceListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,48 @@
namespace OCA\OpenProject\Listener;

use OCA\OpenProject\AppInfo\Application;
use OCA\OpenProject\Service\OpenProjectAPIService;
use OCP\AppFramework\Services\IInitialState;
use OCP\Collaboration\Reference\RenderReferenceEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\IConfig;
use OCP\Util;

/**
* @template-implements IEventListener<Event>
*/
class OpenProjectReferenceListener implements IEventListener {

/**
* @var IInitialState
*/
private $initialStateService;

/**
* @var IConfig
*/
private $config;

public function __construct(
IInitialState $initialStateService,
IConfig $config
) {
$this->initialStateService = $initialStateService;
$this->config = $config;
}
public function handle(Event $event): void {
// @phpstan-ignore-next-line - make phpstan not complain in nextcloud version other than 26
if (!$event instanceof RenderReferenceEvent) {
return;
}

Util::addScript(Application::APP_ID, Application::APP_ID . '-reference');
$this->initialStateService->provideInitialState('admin-config-status', OpenProjectAPIService::isAdminConfigOk($this->config));

$this->initialStateService->provideInitialState(
'openproject-url',
$this->config->getAppValue(Application::APP_ID, 'openproject_instance_url')
);
}
}
9 changes: 1 addition & 8 deletions lib/Reference/WorkPackageReferenceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
use OCP\IL10N;
use OCP\IURLGenerator;

class WorkPackageReferenceProvider extends ADiscoverableReferenceProvider implements ISearchableReferenceProvider {
class WorkPackageReferenceProvider extends ADiscoverableReferenceProvider {
private const RICH_OBJECT_TYPE = Application::APP_ID . '_work_package';

// as we know we are on NC >= 26, we can use Php 8 syntax for class attributes
Expand Down Expand Up @@ -76,13 +76,6 @@ public function getIconUrl(): string {
);
}

/**
* @inheritDoc
*/
public function getSupportedSearchProviderIds(): array {
return ['openproject-search'];
}

/**
* Parse a link to find a work package ID
*
Expand Down
20 changes: 5 additions & 15 deletions lib/Search/OpenProjectSearchProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,24 +110,14 @@ public function search(IUser $user, ISearchQuery $query): SearchResult {
$offset = $offset ? intval($offset) : 0;
$openprojectUrl = OpenProjectAPIService::sanitizeUrl($this->config->getAppValue(Application::APP_ID, 'openproject_instance_url'));
$accessToken = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'token');

if ($accessToken === '') {
$searchEnabled = $this->config->getUserValue(
$user->getUID(),
Application::APP_ID, 'search_enabled',
$this->config->getAppValue(Application::APP_ID, 'default_enable_unified_search', '0')) === '1';
if ($accessToken === '' || !$searchEnabled) {
return SearchResult::paginated($this->getName(), [], 0);
}

$routeFrom = $query->getRoute();
$requestedFromSmartPicker = $routeFrom === '' || $routeFrom === 'smart-picker';

if (!$requestedFromSmartPicker) {
$searchEnabled = $this->config->getUserValue(
$user->getUID(),
Application::APP_ID, 'search_enabled',
$this->config->getAppValue(Application::APP_ID, 'default_enable_unified_search', '0')) === '1';
if (!$searchEnabled) {
return SearchResult::paginated($this->getName(), [], 0);
}
}

$searchResults = $this->service->searchWorkPackage($user->getUID(), $term, null, false);
$searchResults = array_slice($searchResults, $offset, $limit);

Expand Down
31 changes: 31 additions & 0 deletions src/components/icons/OpenProjectIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>
<span :aria-hidden="!title"
:aria-label="title"
class="material-design-icon openproject-icon"
role="img"
v-bind="$attrs"
@click="$emit('click', $event)">
<svg xmlns="http://www.w3.org/2000/svg"
width="150"
height="150">
<path :stroke="fillColor" :fill="fillColor" d="m 80,110 h 0.013 C 80.01,110 80,110.086 80,110.166 c 0,4.01 3.357,7.224 7.5,7.224 4.143,0 7.5,-3.194 7.5,-7.204 C 95,110.104 94.99,110 94.987,110 H 95 V 92 H 80 Z" />
<path :stroke="fillColor" :fill="fillColor" d="M 115,13 H 105 C 91.193,13 80,24.045 80,37.853 v 11 9 V 68 H 46 45 36 C 22.193,68 11,79.046 11,92.853 v 20 C 11,126.659 22.193,138 36,138 h 10 c 13.807,0 25,-11.341 25,-25.147 v -20 C 71,92.518 70.988,92 70.975,92 H 56 v 0.853 7 13 C 56,118.366 51.514,123 46,123 H 36 c -5.514,0 -10,-4.634 -10,-10.147 v -20 C 26,87.339 30.486,83 36,83 h 8 1 1 22.914 2.086 34 7 3 c 13.807,0 25,-11.341 25,-25.147 v -20 C 140,24.045 128.807,13 115,13 Z m 10,44.853 C 125,63.367 120.514,68 115,68 h -3 -3 -4 -10 v -10.147 -9 -1 -10 C 95,32.338 99.486,28 105,28 h 10 c 5.514,0 10,4.338 10,9.853 z" />
</svg>
</span>
</template>

<script>
export default {
name: 'OpenProjectIcon',
props: {
title: {
type: String,
default: '',
},
fillColor: {
type: String,
default: 'currentColor',
},
},
}
</script>
20 changes: 14 additions & 6 deletions src/components/tab/EmptyContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
<div class="empty-content--wrapper">
<div class="empty-content--icon">
<CheckIcon v-if="isStateOk && dashboard" :size="60" />
<LinkPlusIcon v-else-if="!!isAdminConfigOk && isStateOk && !dashboard" :size="60" />
<LinkPlusIcon v-else-if="!!isAdminConfigOk && isStateOk && !dashboard && !isSmartPicker" :size="60" />
<OpenProjectIcon v-else-if="!!isSmartPicker" class="empty-content--icon--openproject" />
<LinkOffIcon v-else :size="60" />
</div>
<div v-if="!!isAdminConfigOk" class="empty-content--message">
<div v-if="!!isAdminConfigOk && !isSmartPicker" class="empty-content--message">
<div class="empty-content--message--title">
{{ emptyContentTitleMessage }}
</div>
Expand All @@ -25,14 +26,15 @@
import LinkPlusIcon from 'vue-material-design-icons/LinkPlus.vue'
import LinkOffIcon from 'vue-material-design-icons/LinkOff.vue'
import CheckIcon from 'vue-material-design-icons/Check.vue'
import OpenProjectIcon from '../icons/OpenProjectIcon.vue'
import { generateUrl } from '@nextcloud/router'
import { translate as t } from '@nextcloud/l10n'
import OAuthConnectButton from '../OAuthConnectButton.vue'
import { STATE } from '../../utils.js'
export default {
name: 'EmptyContent',
components: { OAuthConnectButton, LinkPlusIcon, LinkOffIcon, CheckIcon },
components: { OAuthConnectButton, LinkPlusIcon, LinkOffIcon, CheckIcon, OpenProjectIcon },
props: {
state: {
type: String,
Expand All @@ -57,6 +59,10 @@ export default {
type: Boolean,
default: false,
},
isSmartPicker: {
type: Boolean,
default: false,
},
},
data() {
return {
Expand Down Expand Up @@ -109,9 +115,11 @@ export default {
display: flex;
align-items: center;
justify-content: center;
img {
height: 50px;
width: 50px;
&--openproject {
margin: 90px;
display: block;
align-items: center;
justify-content: center;
}
}
&--message {
Expand Down
58 changes: 46 additions & 12 deletions src/components/tab/SearchInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
</template>

<script>
import { loadState } from '@nextcloud/initial-state'
import debounce from 'lodash/debounce.js'
import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
Expand All @@ -50,18 +51,22 @@ export default {
props: {
fileInfo: {
type: Object,
required: true,
default: null,
},
linkedWorkPackages: {
type: Array,
required: true,
default: null,
},
isSmartPicker: {
type: Boolean,
default: false,
},
},
data: () => ({
state: STATE.OK,
searchResults: [],
noOptionsText: t('integration_openproject', 'Start typing to search'),
placeholder: t('integration_openproject', 'Search for a work package to create a relation'),
openprojectUrl: loadState('integration_openproject', 'openproject-url'),
}),
computed: {
isStateOk() {
Expand All @@ -78,8 +83,18 @@ export default {
}
return ''
},
placeholder() {
if (this.isSmartPicker) {
return t('integration_openproject', 'Search for work packages')
} else {
return t('integration_openproject', 'Search for a work package to create a relation')
}
},
filterSearchResultsByFileId() {
return this.searchResults.filter(wp => {
if (this.isSmartPicker) {
return wp.id
}
if (wp.fileId === undefined || wp.fileId === '') {
console.error('work-package data does not contain a fileId')
return false
Expand Down Expand Up @@ -117,13 +132,25 @@ export default {
},
async asyncFind(query) {
this.resetState()
await this.debounceMakeSearchRequest(query, this.fileInfo.id)
if (this.isSmartPicker) {
await this.debounceMakeSearchRequest(query)
} else {
await this.debounceMakeSearchRequest(query, this.fileInfo.id)
}
},
async getFileLink(selectedOption) {
return this.openprojectUrl + '/projects/' + selectedOption.projectId + '/work_packages/' + selectedOption.id
},
debounceMakeSearchRequest: debounce(function(...args) {
if (args[0].length < SEARCH_CHAR_LIMIT) return
return this.makeSearchRequest(...args)
}, DEBOUNCE_THRESHOLD),
async linkWorkPackageToFile(selectedOption) {
if (this.isSmartPicker) {
const link = await this.getFileLink(selectedOption)
this.$emit('submit', link)
return
}
const params = new URLSearchParams()
params.append('workpackageId', selectedOption.id)
params.append('fileId', this.fileInfo.id)
Expand All @@ -149,7 +176,7 @@ export default {
)
}
},
async makeSearchRequest(search, fileId) {
async makeSearchRequest(search, fileId = null) {
this.state = STATE.LOADING
const url = generateUrl('/apps/integration_openproject/work-packages')
const req = {}
Expand All @@ -170,13 +197,20 @@ export default {
for (let workPackage of workPackages) {
try {
if (this.isStateLoading) {
workPackage.fileId = fileId
workPackage = await workpackageHelper.getAdditionalMetaData(workPackage)
const alreadyLinked = this.linkedWorkPackages.some(el => el.id === workPackage.id)
const alreadyInSearchResults = this.searchResults.some(el => el.id === workPackage.id)
// check the state again, it might have changed in between
if (!alreadyInSearchResults && !alreadyLinked && this.isStateLoading) {
this.searchResults.push(workPackage)
if (this.isSmartPicker) {
workPackage = await workpackageHelper.getAdditionalMetaData(workPackage)
if (this.isStateLoading) {
this.searchResults.push(workPackage)
}
} else {
workPackage.fileId = fileId
workPackage = await workpackageHelper.getAdditionalMetaData(workPackage)
const alreadyLinked = this.linkedWorkPackages.some(el => el.id === workPackage.id)
const alreadyInSearchResults = this.searchResults.some(el => el.id === workPackage.id)
// check the state again, it might have changed in between
if (!alreadyInSearchResults && !alreadyLinked && this.isStateLoading) {
this.searchResults.push(workPackage)
}
}
}
} catch (e) {
Expand Down
19 changes: 18 additions & 1 deletion src/reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/

// this requires @nextcloud/vue >= 7.9.0
import { registerWidget } from '@nextcloud/vue/dist/Components/NcRichText.js'
import { registerWidget, registerCustomPickerElement, NcCustomPickerRenderResult } from '@nextcloud/vue/dist/Components/NcRichText.js'

// this is required for lazy loading
__webpack_nonce__ = btoa(OC.requestToken) // eslint-disable-line
Expand All @@ -41,3 +41,20 @@ registerWidget('integration_openproject_work_package', async (el, { richObjectTy
},
}).$mount(el)
})

registerCustomPickerElement('openproject-work-package-ref', async (el, { providerId, accessible }) => {
const { default: Vue } = await import(/* webpackChunkName: "reference-picker-lazy" */'vue')
const { default: WorkPackagePickerElement } = await import(/* webpackChunkName: "reference-picker-lazy" */'./views/WorkPackagePickerElement.vue')
Vue.mixin({ methods: { t, n } })

const Element = Vue.extend(WorkPackagePickerElement)
const vueElement = new Element({
propsData: {
providerId,
accessible,
},
}).$mount(el)
return new NcCustomPickerRenderResult(vueElement.$el, vueElement)
}, (el, renderResult) => {
renderResult.object.$destroy()
})
Loading

0 comments on commit 3906597

Please sign in to comment.