ResourceFolder.vue 22.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
<template>
  <div class="container section" v-if="resource">
    <nav class="breadcrumb" aria-label="breadcrumbs">
      <ul>
        <li>
          <router-link
            :to="{
              name: RouteName.GROUP,
              params: { preferredUsername: usernameWithDomain(resource.actor) },
            }"
Thomas Citharel's avatar
Thomas Citharel committed
11
            >{{ resource.actor.name }}</router-link
12
13
14
15
16
17
18
19
20
21
22
23
          >
        </li>
        <li>
          <router-link
            :to="{
              name: RouteName.RESOURCE_FOLDER_ROOT,
              params: { preferredUsername: usernameWithDomain(resource.actor) },
            }"
            >{{ $t("Resources") }}</router-link
          >
        </li>
        <li
Thomas Citharel's avatar
Thomas Citharel committed
24
25
26
27
          :class="{
            'is-active':
              index + 1 === ResourceMixin.resourcePathArray(resource).length,
          }"
Thomas Citharel's avatar
Thomas Citharel committed
28
          v-for="(pathFragment, index) in filteredPath"
29
30
31
32
33
34
          :key="pathFragment"
        >
          <router-link
            :to="{
              name: RouteName.RESOURCE_FOLDER,
              params: {
Thomas Citharel's avatar
Thomas Citharel committed
35
36
37
38
                path: ResourceMixin.resourcePathArray(resource).slice(
                  0,
                  index + 1
                ),
39
                preferredUsername: usernameWithDomain(resource.actor),
40
41
42
43
44
45
46
47
48
49
50
51
52
              },
            }"
            >{{ pathFragment }}</router-link
          >
        </li>
        <li>
          <b-dropdown aria-role="list">
            <b-button class="button is-primary" slot="trigger">+</b-button>

            <b-dropdown-item aria-role="listitem" @click="createFolderModal">
              <b-icon icon="folder" />
              {{ $t("New folder") }}
            </b-dropdown-item>
Thomas Citharel's avatar
Thomas Citharel committed
53
54
55
56
            <b-dropdown-item
              aria-role="listitem"
              @click="createLinkResourceModal = true"
            >
57
58
59
              <b-icon icon="link" />
              {{ $t("New link") }}
            </b-dropdown-item>
Thomas Citharel's avatar
Thomas Citharel committed
60
61
62
63
            <hr
              class="dropdown-divider"
              v-if="config.resourceProviders.length"
            />
64
65
66
67
68
69
70
71
72
73
74
75
76
77
            <b-dropdown-item
              aria-role="listitem"
              v-for="resourceProvider in config.resourceProviders"
              :key="resourceProvider.software"
              @click="createResourceFromProvider(resourceProvider)"
            >
              <b-icon :icon="mapServiceTypeToIcon[resourceProvider.software]" />
              {{ createSentenceForType(resourceProvider.software) }}
            </b-dropdown-item>
          </b-dropdown>
        </li>
      </ul>
    </nav>
    <section>
Thomas Citharel's avatar
Thomas Citharel committed
78
      <p v-if="resource.path === '/'" class="module-description">
Thomas Citharel's avatar
Thomas Citharel committed
79
80
81
        {{
          $t("A place to store links to documents or resources of any type.")
        }}
Thomas Citharel's avatar
Thomas Citharel committed
82
      </p>
83
84
      <div class="list-header">
        <div class="list-header-right">
85
          <b-checkbox v-model="checkedAll" v-if="resource.children.total > 0" />
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
          <div class="actions" v-if="validCheckedResources.length > 0">
            <small>
              {{
                $tc("No resources selected", validCheckedResources.length, {
                  count: validCheckedResources.length,
                })
              }}
            </small>
            <b-button
              type="is-danger"
              icon-right="delete"
              size="is-small"
              @click="deleteMultipleResources"
              >{{ $t("Delete") }}</b-button
            >
          </div>
        </div>
      </div>
104
105
106
107
108
109
      <draggable
        v-model="resource.children.elements"
        :sort="false"
        :group="groupObject"
        v-if="resource.children.total > 0"
      >
110
        <transition-group>
Thomas Citharel's avatar
Thomas Citharel committed
111
112
113
114
          <div
            v-for="localResource in resource.children.elements"
            :key="localResource.id"
          >
115
116
117
118
119
120
121
122
123
124
125
126
            <div class="resource-item">
              <div
                class="resource-checkbox"
                :class="{ checked: checkedResources[localResource.id] }"
              >
                <b-checkbox v-model="checkedResources[localResource.id]" />
              </div>
              <resource-item
                :resource="localResource"
                v-if="localResource.type !== 'folder'"
                @delete="deleteResource"
                @rename="handleRename"
127
                @move="handleMove"
128
129
130
131
132
              />
              <folder-item
                :resource="localResource"
                :group="resource.actor"
                @delete="deleteResource"
133
                @rename="handleRename"
134
                @move="handleMove"
135
136
137
138
139
140
                v-else
              />
            </div>
          </div>
        </transition-group>
      </draggable>
Thomas Citharel's avatar
Thomas Citharel committed
141
142
143
144
      <div
        class="content has-text-centered has-text-grey"
        v-if="resource.children.total === 0"
      >
145
146
        <p>{{ $t("No resources in this folder") }}</p>
      </div>
147
    </section>
Thomas Citharel's avatar
Thomas Citharel committed
148
149
150
151
152
153
154
155
156
157
158
    <b-pagination
      v-if="resource.children.total > RESOURCES_PER_PAGE"
      :total="resource.children.total"
      v-model="page"
      :per-page="RESOURCES_PER_PAGE"
      :aria-next-label="$t('Next page')"
      :aria-previous-label="$t('Previous page')"
      :aria-page-label="$t('Page')"
      :aria-current-label="$t('Current page')"
    >
    </b-pagination>
159
160
161
162
163
164
165
166
    <b-modal :active.sync="renameModal" has-modal-card>
      <div class="modal-card">
        <section class="modal-card-body">
          <form @submit.prevent="renameResource">
            <b-field :label="$t('Title')">
              <b-input aria-required="true" v-model="updatedResource.title" />
            </b-field>

Thomas Citharel's avatar
Thomas Citharel committed
167
168
169
            <b-button native-type="submit">{{
              $t("Rename resource")
            }}</b-button>
170
171
172
173
          </form>
        </section>
      </div>
    </b-modal>
174
175
176
177
178
    <b-modal :active.sync="moveModal" has-modal-card>
      <div class="modal-card">
        <section class="modal-card-body">
          <resource-selector
            :initialResource="updatedResource"
179
            :username="usernameWithDomain(resource.actor)"
Thomas Citharel's avatar
Thomas Citharel committed
180
181
            @update-resource="moveResource"
            @close-move-modal="moveModal = false"
182
183
184
185
          />
        </section>
      </div>
    </b-modal>
186
187
188
189
190
191
192
193
    <b-modal :active.sync="createResourceModal" has-modal-card>
      <div class="modal-card">
        <section class="modal-card-body">
          <form @submit.prevent="createResource">
            <b-field :label="$t('Title')">
              <b-input aria-required="true" v-model="newResource.title" />
            </b-field>

Thomas Citharel's avatar
Thomas Citharel committed
194
195
196
            <b-button native-type="submit">{{
              createResourceButtonLabel
            }}</b-button>
197
198
199
200
          </form>
        </section>
      </div>
    </b-modal>
201
202
203
204
205
    <b-modal
      :active.sync="createLinkResourceModal"
      has-modal-card
      class="link-resource-modal"
    >
206
207
      <div class="modal-card">
        <section class="modal-card-body">
208
209
210
          <b-message type="is-danger" v-if="modalError">
            {{ modalError }}
          </b-message>
211
212
213
214
215
216
217
218
219
220
221
          <form @submit.prevent="createResource">
            <b-field :label="$t('URL')">
              <b-input
                type="url"
                required
                v-model="newResource.resourceUrl"
                @blur="previewResource"
              />
            </b-field>

            <div class="new-resource-preview" v-if="newResource.title">
222
              <resource-item :resource="newResource" :preview="true" />
223
224
225
226
227
228
229
230
231
232
            </div>

            <b-field :label="$t('Title')">
              <b-input aria-required="true" v-model="newResource.title" />
            </b-field>

            <b-field :label="$t('Text')">
              <b-input type="textarea" v-model="newResource.summary" />
            </b-field>

Thomas Citharel's avatar
Thomas Citharel committed
233
234
235
            <b-button native-type="submit">{{
              $t("Create resource")
            }}</b-button>
236
237
238
239
240
241
242
243
244
245
246
247
248
249
          </form>
        </section>
      </div>
    </b-modal>
  </div>
</template>
<script lang="ts">
import { Component, Mixins, Prop, Watch } from "vue-property-decorator";
import ResourceItem from "@/components/Resource/ResourceItem.vue";
import FolderItem from "@/components/Resource/FolderItem.vue";
import Draggable from "vuedraggable";
import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
import { IActor, usernameWithDomain } from "../../types/actor";
import RouteName from "../../router/name";
Thomas Citharel's avatar
Thomas Citharel committed
250
251
252
253
254
import {
  IResource,
  mapServiceTypeToIcon,
  IProvider,
} from "../../types/resource";
255
256
257
258
259
260
261
262
263
264
import {
  CREATE_RESOURCE,
  DELETE_RESOURCE,
  PREVIEW_RESOURCE_LINK,
  GET_RESOURCE,
  UPDATE_RESOURCE,
} from "../../graphql/resources";
import { CONFIG } from "../../graphql/config";
import { IConfig } from "../../types/config.model";
import ResourceMixin from "../../mixins/resource";
265
import ResourceSelector from "../../components/Resource/ResourceSelector.vue";
Thomas Citharel's avatar
Thomas Citharel committed
266
267
268
269
270
import {
  ApolloCache,
  FetchResult,
  InternalRefetchQueriesInclude,
} from "@apollo/client/core";
Thomas Citharel's avatar
Thomas Citharel committed
271
272
import VueRouter from "vue-router";
const { isNavigationFailure, NavigationFailureType } = VueRouter;
273
274

@Component({
275
  components: { FolderItem, ResourceItem, Draggable, ResourceSelector },
276
277
278
  apollo: {
    resource: {
      query: GET_RESOURCE,
Thomas Citharel's avatar
Thomas Citharel committed
279
      fetchPolicy: "cache-and-network",
280
281
282
283
284
285
286
287
      variables() {
        let path = Array.isArray(this.$route.params.path)
          ? this.$route.params.path.join("/")
          : this.$route.params.path || this.path;
        path = path[0] !== "/" ? `/${path}` : path;
        return {
          path,
          username: this.$route.params.preferredUsername,
Thomas Citharel's avatar
Thomas Citharel committed
288
289
          page: this.page,
          limit: this.RESOURCES_PER_PAGE,
290
291
        };
      },
292
293
294
      error({ graphQLErrors }) {
        this.handleErrors(graphQLErrors);
      },
295
296
297
298
    },
    config: CONFIG,
    currentActor: CURRENT_ACTOR_CLIENT,
  },
299
300
301
302
303
304
305
306
307
308
309
310
311
  metaInfo() {
    return {
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      title: this.isRoot
        ? (this.$t("Resources") as string)
        : (this.$t("{folder} - Resources", {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            folder: this.lastFragment,
          }) as string),
    };
  },
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
})
export default class Resources extends Mixins(ResourceMixin) {
  @Prop({ required: true }) path!: string;

  resource!: IResource;

  config!: IConfig;

  currentActor!: IActor;

  RouteName = RouteName;

  ResourceMixin = ResourceMixin;

  usernameWithDomain = usernameWithDomain;

Thomas Citharel's avatar
Thomas Citharel committed
328
329
  RESOURCES_PER_PAGE = 10;

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  newResource: IResource = {
    title: "",
    summary: "",
    resourceUrl: "",
    children: { elements: [], total: 0 },
    metadata: {},
    type: "link",
  };

  updatedResource: IResource = {
    title: "",
    resourceUrl: "",
    metadata: {},
    children: { elements: [], total: 0 },
    path: undefined,
  };

  checkedResources: { [key: string]: boolean } = {};

  validCheckedResources: string[] = [];

  checkedAll = false;

  createResourceModal = false;

  createLinkResourceModal = false;

357
358
  moveModal = false;

359
360
  renameModal = false;

361
362
  modalError = "";

Thomas Citharel's avatar
Thomas Citharel committed
363
  groupObject: Record<string, unknown> = {
364
365
366
367
368
369
370
    name: "resources",
    pull: "clone",
    put: true,
  };

  mapServiceTypeToIcon = mapServiceTypeToIcon;

Thomas Citharel's avatar
Thomas Citharel committed
371
372
373
374
375
376
377
378
379
380
  get page(): number {
    return parseInt((this.$route.query.page as string) || "1", 10);
  }

  set page(page: number) {
    this.pushRouter({
      page: page.toString(),
    });
  }

381
382
383
384
385
386
387
  get actualPath(): string {
    const path = Array.isArray(this.$route.params.path)
      ? this.$route.params.path.join("/")
      : this.$route.params.path || this.path;
    return path[0] !== "/" ? `/${path}` : path;
  }

Thomas Citharel's avatar
Thomas Citharel committed
388
389
390
391
392
393
394
  get filteredPath(): string[] {
    if (this.resource && this.resource.path !== "/") {
      return ResourceMixin.resourcePathArray(this.resource);
    }
    return [];
  }

395
396
397
398
399
400
401
402
  get isRoot(): boolean {
    return this.actualPath === "/";
  }

  get lastFragment(): string | undefined {
    return this.filteredPath.slice(-1)[0];
  }

Thomas Citharel's avatar
Thomas Citharel committed
403
  async createResource(): Promise<void> {
404
    if (!this.resource.actor) return;
405
    this.modalError = "";
406
    try {
Thomas Citharel's avatar
Thomas Citharel committed
407
      await this.$apollo.mutate({
408
409
410
411
412
413
414
        mutation: CREATE_RESOURCE,
        variables: {
          title: this.newResource.title,
          summary: this.newResource.summary,
          actorId: this.resource.actor.id,
          resourceUrl: this.newResource.resourceUrl,
          parentId:
Thomas Citharel's avatar
Thomas Citharel committed
415
416
417
            this.resource.id && this.resource.id.startsWith("root_")
              ? null
              : this.resource.id,
418
419
          type: this.newResource.type,
        },
420
        refetchQueries: () => this.postRefreshQueries(),
421
422
423
424
425
426
427
428
      });
      this.createLinkResourceModal = false;
      this.createResourceModal = false;
      this.newResource.title = "";
      this.newResource.summary = "";
      this.newResource.resourceUrl = "";
    } catch (err) {
      console.error(err);
429
      this.modalError = err.graphQLErrors[0].message;
430
431
432
    }
  }

Thomas Citharel's avatar
Thomas Citharel committed
433
  async previewResource(): Promise<void> {
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
    this.modalError = "";
    try {
      if (this.newResource.resourceUrl === "") return;
      const { data } = await this.$apollo.mutate({
        mutation: PREVIEW_RESOURCE_LINK,
        variables: {
          resourceUrl: this.newResource.resourceUrl,
        },
      });
      this.newResource.title = data.previewResourceLink.title;
      this.newResource.summary = data.previewResourceLink.description;
      this.newResource.metadata = data.previewResourceLink;
      this.newResource.type = "link";
    } catch (err) {
      console.error(err);
      this.modalError = err.graphQLErrors[0].message;
    }
451
452
  }

Thomas Citharel's avatar
Thomas Citharel committed
453
  createSentenceForType(type: string): string {
454
    switch (type) {
455
      case "folder":
Thomas Citharel's avatar
Thomas Citharel committed
456
        return this.$t("Create a folder") as string;
457
      case "pad":
Thomas Citharel's avatar
Thomas Citharel committed
458
        return this.$t("Create a pad") as string;
459
      case "calc":
Thomas Citharel's avatar
Thomas Citharel committed
460
        return this.$t("Create a calc") as string;
461
      case "visio":
Marcin Mikolajczak's avatar
Marcin Mikolajczak committed
462
        return this.$t("Create a videoconference") as string;
Thomas Citharel's avatar
Thomas Citharel committed
463
464
      default:
        return "";
465
466
467
    }
  }

Thomas Citharel's avatar
Thomas Citharel committed
468
  createFolderModal(): void {
469
470
471
472
    this.newResource.type = "folder";
    this.createResourceModal = true;
  }

Thomas Citharel's avatar
Thomas Citharel committed
473
474
  createResourceFromProvider(provider: IProvider): void {
    this.newResource.resourceUrl = Resources.generateFullResourceUrl(provider);
475
476
477
478
    this.newResource.type = provider.software;
    this.createResourceModal = true;
  }

Thomas Citharel's avatar
Thomas Citharel committed
479
  static generateFullResourceUrl(provider: IProvider): string {
480
481
482
    const randomString = [...Array(10)]
      .map(() => Math.random().toString(36)[3])
      .join("")
Thomas Citharel's avatar
Thomas Citharel committed
483
484
485
      .replace(/(.|$)/g, (c) =>
        c[!Math.round(Math.random()) ? "toString" : "toLowerCase"]()
      );
486
487
488
489
490
491
492
493
494
    switch (provider.type) {
      case "ethercalc":
      case "etherpad":
      case "jitsi":
      default:
        return `${provider.endpoint}${randomString}`;
    }
  }

Thomas Citharel's avatar
Thomas Citharel committed
495
496
  get createResourceButtonLabel(): string {
    if (!this.newResource.type) return "";
497
498
499
500
    return this.createSentenceForType(this.newResource.type);
  }

  @Watch("checkedAll")
Thomas Citharel's avatar
Thomas Citharel committed
501
  watchCheckedAll(): void {
502
503
504
505
506
507
508
509
510
    this.resource.children.elements.forEach(({ id }) => {
      if (!id) return;
      this.checkedResources[id] = this.checkedAll;
    });
  }

  @Watch("checkedResources", { deep: true })
  watchValidCheckedResources(): string[] {
    const validCheckedResources: string[] = [];
Thomas Citharel's avatar
Thomas Citharel committed
511
    Object.entries(this.checkedResources).forEach(([key, value]) => {
512
513
514
      if (value) {
        validCheckedResources.push(key);
      }
Thomas Citharel's avatar
Thomas Citharel committed
515
516
517
    });
    this.validCheckedResources = validCheckedResources;
    return this.validCheckedResources;
518
519
  }

Thomas Citharel's avatar
Thomas Citharel committed
520
521
  async deleteMultipleResources(): Promise<void> {
    this.validCheckedResources.forEach(async (resourceID) => {
522
      await this.deleteResource(resourceID);
Thomas Citharel's avatar
Thomas Citharel committed
523
    });
524
525
  }

526
  // eslint-disable-next-line class-methods-use-this
Thomas Citharel's avatar
Thomas Citharel committed
527
  private postRefreshQueries(): InternalRefetchQueriesInclude {
528
529
530
531
532
533
    return [
      {
        query: GET_RESOURCE,
        variables: {
          path: this.actualPath,
          username: this.$route.params.preferredUsername,
534
535
          page: this.page,
          limit: this.RESOURCES_PER_PAGE,
536
537
538
539
540
        },
      },
    ];
  }

Thomas Citharel's avatar
Thomas Citharel committed
541
  async deleteResource(resourceID: string): Promise<void> {
542
543
544
545
546
547
    try {
      await this.$apollo.mutate({
        mutation: DELETE_RESOURCE,
        variables: {
          id: resourceID,
        },
548
        refetchQueries: () => this.postRefreshQueries(),
549
      });
Thomas Citharel's avatar
Thomas Citharel committed
550
551
552
      this.validCheckedResources = this.validCheckedResources.filter(
        (id) => id !== resourceID
      );
553
554
555
556
557
558
      delete this.checkedResources[resourceID];
    } catch (e) {
      console.error(e);
    }
  }

Thomas Citharel's avatar
Thomas Citharel committed
559
  handleRename(resource: IResource): void {
560
    console.log("handleRename");
561
    this.renameModal = true;
Thomas Citharel's avatar
Thomas Citharel committed
562
    this.updatedResource = { ...resource };
563
564
  }

Thomas Citharel's avatar
Thomas Citharel committed
565
  handleMove(resource: IResource): void {
566
    this.moveModal = true;
Thomas Citharel's avatar
Thomas Citharel committed
567
    this.updatedResource = { ...resource };
568
569
  }

Thomas Citharel's avatar
Thomas Citharel committed
570
571
572
573
574
575
  async moveResource(
    resource: IResource,
    oldParent: IResource | undefined
  ): Promise<void> {
    const parentPath =
      oldParent && oldParent.path ? oldParent.path || "/" : "/";
576
577
    await this.updateResource(resource, parentPath);
    this.moveModal = false;
578
579
  }

Thomas Citharel's avatar
Thomas Citharel committed
580
  async renameResource(): Promise<void> {
581
    await this.updateResource(this.updatedResource);
582
    this.renameModal = false;
583
584
  }

Thomas Citharel's avatar
Thomas Citharel committed
585
586
587
588
  async updateResource(
    resource: IResource,
    parentPath: string | null = null
  ): Promise<void> {
589
590
591
592
593
594
    try {
      await this.$apollo.mutate<{ updateResource: IResource }>({
        mutation: UPDATE_RESOURCE,
        variables: {
          id: resource.id,
          title: resource.title,
595
          parentId: resource.parent ? resource.parent.id : null,
596
597
          path: resource.path,
        },
598
        refetchQueries: () => this.postRefreshQueries(),
599
600
601
602
        update: (
          store: ApolloCache<{ updateResource: IResource }>,
          { data }: FetchResult
        ) => {
Thomas Citharel's avatar
Thomas Citharel committed
603
604
          if (!data || data.updateResource == null || parentPath == null)
            return;
605
606
607
608
609
610
611
612
613
614
615
616
617
          if (!this.resource.actor) return;

          console.log("Removing ressource from old parent");
          const oldParentCachedData = store.readQuery<{ resource: IResource }>({
            query: GET_RESOURCE,
            variables: {
              path: parentPath,
              username: this.resource.actor.preferredUsername,
            },
          });
          if (oldParentCachedData == null) return;
          const { resource: oldParentCachedResource } = oldParentCachedData;
          if (oldParentCachedResource == null) {
Thomas Citharel's avatar
Thomas Citharel committed
618
619
620
            console.error(
              "Cannot update resource cache, because of null value."
            );
621
622
            return;
          }
Thomas Citharel's avatar
Thomas Citharel committed
623
          const updatedResource: IResource = data.updateResource;
624

Thomas Citharel's avatar
Thomas Citharel committed
625
          const updatedElementList =
Thomas Citharel's avatar
Thomas Citharel committed
626
627
628
            oldParentCachedResource.children.elements.filter(
              (cachedResource) => cachedResource.id !== updatedResource.id
            );
629
630
631
632
633
634
635

          store.writeQuery({
            query: GET_RESOURCE,
            variables: {
              path: parentPath,
              username: this.resource.actor.preferredUsername,
            },
Thomas Citharel's avatar
Thomas Citharel committed
636
637
638
639
640
641
642
643
644
            data: {
              resource: {
                ...oldParentCachedResource,
                children: {
                  ...oldParentCachedResource.children,
                  elements: [...updatedElementList],
                },
              },
            },
645
646
647
648
          });
          console.log("Finished removing ressource from old parent");

          console.log("Adding resource to new parent");
Thomas Citharel's avatar
Thomas Citharel committed
649
          if (!updatedResource.parent || !updatedResource.parent.path) {
650
651
652
653
654
655
            console.log("No cache found for new parent");
            return;
          }
          const newParentCachedData = store.readQuery<{ resource: IResource }>({
            query: GET_RESOURCE,
            variables: {
Thomas Citharel's avatar
Thomas Citharel committed
656
              path: updatedResource.parent.path,
657
658
659
660
661
662
              username: this.resource.actor.preferredUsername,
            },
          });
          if (newParentCachedData == null) return;
          const { resource: newParentCachedResource } = newParentCachedData;
          if (newParentCachedResource == null) {
Thomas Citharel's avatar
Thomas Citharel committed
663
664
665
            console.error(
              "Cannot update resource cache, because of null value."
            );
666
667
668
669
670
671
            return;
          }

          store.writeQuery({
            query: GET_RESOURCE,
            variables: {
Thomas Citharel's avatar
Thomas Citharel committed
672
              path: updatedResource.parent.path,
673
674
              username: this.resource.actor.preferredUsername,
            },
Thomas Citharel's avatar
Thomas Citharel committed
675
676
677
678
679
680
681
682
683
684
685
686
            data: {
              resource: {
                ...newParentCachedResource,
                children: {
                  ...newParentCachedResource.children,
                  elements: [
                    ...newParentCachedResource.children.elements,
                    resource,
                  ],
                },
              },
            },
687
688
689
          });
          console.log("Finished adding resource to new parent");
        },
690
691
692
693
694
      });
    } catch (e) {
      console.error(e);
    }
  }
695

Thomas Citharel's avatar
Thomas Citharel committed
696
697
698
699
700
701
702
703
704
705
706
  @Watch("page")
  loadMoreResources(): void {
    this.$apollo.queries.resource.fetchMore({
      // New variables
      variables: {
        page: this.page,
        limit: this.RESOURCES_PER_PAGE,
      },
    });
  }

707
708
709
710
711
  handleErrors(errors: any[]): void {
    if (errors.some((error) => error.status_code === 404)) {
      this.$router.replace({ name: RouteName.PAGE_NOT_FOUND });
    }
  }
Thomas Citharel's avatar
Thomas Citharel committed
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731

  async pushRouter(args: Record<string, string>): Promise<void> {
    try {
      const path = this.filteredPath.toString();
      const routeName =
        path === ""
          ? RouteName.RESOURCE_FOLDER_ROOT
          : RouteName.RESOURCE_FOLDER;

      await this.$router.push({
        name: routeName,
        params: { path },
        query: { ...this.$route.query, ...args },
      });
    } catch (e) {
      if (isNavigationFailure(e, NavigationFailureType.redirected)) {
        throw Error(e.toString());
      }
    }
  }
732
733
734
}
</script>
<style lang="scss" scoped>
735
736
.container.section {
  background: $white;
Thomas Citharel's avatar
Thomas Citharel committed
737
738
739
740

  & > nav.pagination {
    margin-top: 1rem;
  }
741
742
}

743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
nav.breadcrumb ul {
  align-items: center;

  li:last-child .dropdown {
    margin-left: 5px;

    a {
      justify-content: left;
      color: inherit;
      padding: 0.375rem 1rem;
    }
  }
}

.list-header {
  display: flex;
  justify-content: space-between;

  .list-header-right {
    display: flex;
    align-items: center;

765
766
767
768
    ::v-deep .b-checkbox.checkbox {
      margin-left: 10px;
    }

769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
    .actions {
      margin-right: 5px;

      & > * {
        margin-left: 5px;
      }
    }
  }
}

.resource-item,
.new-resource-preview {
  display: flex;
  font-size: 14px;
  border: 1px solid #c0cdd9;
  border-radius: 4px;
  color: #444b5d;
  margin-top: 14px;
787
  margin-bottom: 14px;
788
789
790

  .resource-checkbox {
    align-self: center;
791
    padding-left: 10px;
792
    opacity: 0.3;
793
794
795
796

    ::v-deep .b-checkbox.checkbox {
      margin-right: 0.25rem;
    }
797
798
799
800
801
802
803
804
  }

  &:hover .resource-checkbox,
  .resource-checkbox.checked {
    opacity: 1;
  }
}
</style>