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

Update bookmarks #343

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
189 changes: 95 additions & 94 deletions frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,99 +7,100 @@ export {}

declare module 'vue' {
export interface GlobalComponents {
Activity: (typeof import('./src/components/Activity.vue'))['default']
AddMemberDialog: (typeof import('./src/components/AddMemberDialog.vue'))['default']
AddTeamDialog: (typeof import('./src/components/AddTeamDialog.vue'))['default']
AppSidebar: (typeof import('./src/components/AppSidebar.vue'))['default']
ArchivedTeams: (typeof import('./src/components/Settings/ArchivedTeams.vue'))['default']
AssignUser: (typeof import('./src/components/AssignUser.vue'))['default']
Bookmarks: (typeof import('./src/components/Settings/Bookmarks.vue'))['default']
ChevronTriangle: (typeof import('./src/components/icons/ChevronTriangle.vue'))['default']
ColorPicker: (typeof import('./src/components/ColorPicker.vue'))['default']
CommandPalette: (typeof import('./src/components/CommandPalette/CommandPalette.vue'))['default']
Comment: (typeof import('./src/components/Comment.vue'))['default']
CommentEditor: (typeof import('./src/components/CommentEditor.vue'))['default']
CommentsArea: (typeof import('./src/components/CommentsArea.vue'))['default']
CommentsList: (typeof import('./src/components/CommentsList.vue'))['default']
Component: (typeof import('./src/components/TextEditorTaskExtension/Component.vue'))['default']
CoverImage: (typeof import('./src/components/CoverImage.vue'))['default']
DesktopLayout: (typeof import('./src/components/DesktopLayout.vue'))['default']
DiscussionBreadcrumbs: (typeof import('./src/components/DiscussionBreadcrumbs.vue'))['default']
DiscussionList: (typeof import('./src/components/DiscussionList.vue'))['default']
DiscussionMeta: (typeof import('./src/components/DiscussionMeta.vue'))['default']
DiscussionView: (typeof import('./src/components/DiscussionView.vue'))['default']
DragHandleIcon: (typeof import('./src/components/DragHandleIcon.vue'))['default']
GameplanLogo: (typeof import('./src/components/GameplanLogo.vue'))['default']
GameplanLogoType: (typeof import('./src/components/GameplanLogoType.vue'))['default']
IconPicker: (typeof import('./src/components/IconPicker.vue'))['default']
ImagePreview: (typeof import('./src/components/ImagePreview.vue'))['default']
InputWithPills: (typeof import('./src/components/InputWithPills.vue'))['default']
InviteGuestDialog: (typeof import('./src/components/InviteGuestDialog.vue'))['default']
InvitePeople: (typeof import('./src/components/Settings/InvitePeople.vue'))['default']
Item: (typeof import('./src/components/CommandPalette/Item.vue'))['default']
ItemProject: (typeof import('./src/components/CommandPalette/ItemProject.vue'))['default']
ItemTeam: (typeof import('./src/components/CommandPalette/ItemTeam.vue'))['default']
Link: (typeof import('./src/components/Link.vue'))['default']
Links: (typeof import('./src/components/Links.vue'))['default']
LucideArchive: (typeof import('~icons/lucide/archive'))['default']
LucideArrowDownLeft: (typeof import('~icons/lucide/arrow-down-left'))['default']
LucideArrowDownUp: (typeof import('~icons/lucide/arrow-down-up'))['default']
LucideArrowUpLeft: (typeof import('~icons/lucide/arrow-up-left'))['default']
LucideBarChart2: (typeof import('~icons/lucide/bar-chart2'))['default']
LucideBell: (typeof import('~icons/lucide/bell'))['default']
LucideBellPlus: (typeof import('~icons/lucide/bell-plus'))['default']
LucideCalendar: (typeof import('~icons/lucide/calendar'))['default']
LucideCheck: (typeof import('~icons/lucide/check'))['default']
LucideChevronDown: (typeof import('~icons/lucide/chevron-down'))['default']
LucideChevronRight: (typeof import('~icons/lucide/chevron-right'))['default']
LucideCoffee: (typeof import('~icons/lucide/coffee'))['default']
LucideEdit: (typeof import('~icons/lucide/edit'))['default']
LucideEdit2: (typeof import('~icons/lucide/edit2'))['default']
LucideEdit3: (typeof import('~icons/lucide/edit3'))['default']
LucideHeart: (typeof import('~icons/lucide/heart'))['default']
LucideHome: (typeof import('~icons/lucide/home'))['default']
LucideLock: (typeof import('~icons/lucide/lock'))['default']
LucideMinusCircle: (typeof import('~icons/lucide/minus-circle'))['default']
LucideMoreHorizontal: (typeof import('~icons/lucide/more-horizontal'))['default']
LucidePin: (typeof import('~icons/lucide/pin'))['default']
LucidePlus: (typeof import('~icons/lucide/plus'))['default']
LucideRefreshCw: (typeof import('~icons/lucide/refresh-cw'))['default']
LucideRotateCcw: (typeof import('~icons/lucide/rotate-ccw'))['default']
LucideSave: (typeof import('~icons/lucide/save'))['default']
LucideSearch: (typeof import('~icons/lucide/search'))['default']
LucideUnfoldVertical: (typeof import('~icons/lucide/unfold-vertical'))['default']
LucideUnlock: (typeof import('~icons/lucide/unlock'))['default']
LucideUserPlus: (typeof import('~icons/lucide/user-plus'))['default']
LucideUserPlus2: (typeof import('~icons/lucide/user-plus2'))['default']
LucideX: (typeof import('~icons/lucide/x'))['default']
Members: (typeof import('./src/components/Settings/Members.vue'))['default']
MobileLayout: (typeof import('./src/components/MobileLayout.vue'))['default']
NewTaskDialog: (typeof import('./src/components/NewTaskDialog.vue'))['default']
PageList: (typeof import('./src/components/PageList.vue'))['default']
Pie: (typeof import('./src/components/Pie.vue'))['default']
Poll: (typeof import('./src/components/Poll.vue'))['default']
PollEditor: (typeof import('./src/components/PollEditor.vue'))['default']
ProfileImageEditor: (typeof import('./src/components/ProfileImageEditor.vue'))['default']
ReactionFaceIcon: (typeof import('./src/components/ReactionFaceIcon.vue'))['default']
Reactions: (typeof import('./src/components/Reactions.vue'))['default']
ReadmeEditor: (typeof import('./src/components/ReadmeEditor.vue'))['default']
RevisionsDialog: (typeof import('./src/components/RevisionsDialog.vue'))['default']
RouterLink: (typeof import('vue-router'))['RouterLink']
RouterView: (typeof import('vue-router'))['RouterView']
SettingsDialog: (typeof import('./src/components/Settings/SettingsDialog.vue'))['default']
SettingsTab: (typeof import('./src/components/Settings/SettingsTab.vue'))['default']
Tabs: (typeof import('./src/components/Tabs.vue'))['default']
TaskDetail: (typeof import('./src/components/TaskDetail.vue'))['default']
TaskList: (typeof import('./src/components/TaskList.vue'))['default']
TaskPriorityIcon: (typeof import('./src/components/icons/TaskPriorityIcon.vue'))['default']
TaskStatusIcon: (typeof import('./src/components/icons/TaskStatusIcon.vue'))['default']
TeamMembers: (typeof import('./src/components/TeamMembers.vue'))['default']
TextEditor: (typeof import('./src/components/TextEditor.vue'))['default']
UnsplashImageBrowser: (typeof import('./src/components/UnsplashImageBrowser.vue'))['default']
UserAvatar: (typeof import('./src/components/UserAvatar.vue'))['default']
UserDropdown: (typeof import('./src/components/UserDropdown.vue'))['default']
UserImage: (typeof import('./src/components/UserImage.vue'))['default']
UserInfo: (typeof import('./src/components/UserInfo.vue'))['default']
UserProfileLink: (typeof import('./src/components/UserProfileLink.vue'))['default']
Activity: typeof import('./src/components/Activity.vue')['default']
AddMemberDialog: typeof import('./src/components/AddMemberDialog.vue')['default']
AddTeamDialog: typeof import('./src/components/AddTeamDialog.vue')['default']
AppSidebar: typeof import('./src/components/AppSidebar.vue')['default']
ArchivedTeams: typeof import('./src/components/Settings/ArchivedTeams.vue')['default']
AssignUser: typeof import('./src/components/AssignUser.vue')['default']
Bookmarks: typeof import('./src/components/Settings/Bookmarks.vue')['default']
ChevronTriangle: typeof import('./src/components/icons/ChevronTriangle.vue')['default']
ColorPicker: typeof import('./src/components/ColorPicker.vue')['default']
CommandPalette: typeof import('./src/components/CommandPalette/CommandPalette.vue')['default']
Comment: typeof import('./src/components/Comment.vue')['default']
CommentEditor: typeof import('./src/components/CommentEditor.vue')['default']
CommentsArea: typeof import('./src/components/CommentsArea.vue')['default']
CommentsList: typeof import('./src/components/CommentsList.vue')['default']
Component: typeof import('./src/components/TextEditorTaskExtension/Component.vue')['default']
CoverImage: typeof import('./src/components/CoverImage.vue')['default']
DesktopLayout: typeof import('./src/components/DesktopLayout.vue')['default']
DiscussionBreadcrumbs: typeof import('./src/components/DiscussionBreadcrumbs.vue')['default']
DiscussionList: typeof import('./src/components/DiscussionList.vue')['default']
DiscussionListByData: typeof import('./src/components/DiscussionListByData.vue')['default']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single line change shouldn't affect other lines in a commit. This messes up git blame. If you want to format the other lines, do it in another commit.

DiscussionMeta: typeof import('./src/components/DiscussionMeta.vue')['default']
DiscussionView: typeof import('./src/components/DiscussionView.vue')['default']
DragHandleIcon: typeof import('./src/components/DragHandleIcon.vue')['default']
GameplanLogo: typeof import('./src/components/GameplanLogo.vue')['default']
GameplanLogoType: typeof import('./src/components/GameplanLogoType.vue')['default']
IconPicker: typeof import('./src/components/IconPicker.vue')['default']
ImagePreview: typeof import('./src/components/ImagePreview.vue')['default']
InputWithPills: typeof import('./src/components/InputWithPills.vue')['default']
InviteGuestDialog: typeof import('./src/components/InviteGuestDialog.vue')['default']
InvitePeople: typeof import('./src/components/Settings/InvitePeople.vue')['default']
Item: typeof import('./src/components/CommandPalette/Item.vue')['default']
ItemProject: typeof import('./src/components/CommandPalette/ItemProject.vue')['default']
ItemTeam: typeof import('./src/components/CommandPalette/ItemTeam.vue')['default']
Link: typeof import('./src/components/Link.vue')['default']
Links: typeof import('./src/components/Links.vue')['default']
LucideArchive: typeof import('~icons/lucide/archive')['default']
LucideArrowDownLeft: typeof import('~icons/lucide/arrow-down-left')['default']
LucideArrowDownUp: typeof import('~icons/lucide/arrow-down-up')['default']
LucideArrowUpLeft: typeof import('~icons/lucide/arrow-up-left')['default']
LucideBarChart2: typeof import('~icons/lucide/bar-chart2')['default']
LucideBell: typeof import('~icons/lucide/bell')['default']
LucideBellPlus: typeof import('~icons/lucide/bell-plus')['default']
LucideCalendar: typeof import('~icons/lucide/calendar')['default']
LucideCheck: typeof import('~icons/lucide/check')['default']
LucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
LucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
LucideCoffee: typeof import('~icons/lucide/coffee')['default']
LucideEdit: typeof import('~icons/lucide/edit')['default']
LucideEdit2: typeof import('~icons/lucide/edit2')['default']
LucideEdit3: typeof import('~icons/lucide/edit3')['default']
LucideHeart: typeof import('~icons/lucide/heart')['default']
LucideHome: typeof import('~icons/lucide/home')['default']
LucideLock: typeof import('~icons/lucide/lock')['default']
LucideMinusCircle: typeof import('~icons/lucide/minus-circle')['default']
LucideMoreHorizontal: typeof import('~icons/lucide/more-horizontal')['default']
LucidePin: typeof import('~icons/lucide/pin')['default']
LucidePlus: typeof import('~icons/lucide/plus')['default']
LucideRefreshCw: typeof import('~icons/lucide/refresh-cw')['default']
LucideRotateCcw: typeof import('~icons/lucide/rotate-ccw')['default']
LucideSave: typeof import('~icons/lucide/save')['default']
LucideSearch: typeof import('~icons/lucide/search')['default']
LucideUnfoldVertical: typeof import('~icons/lucide/unfold-vertical')['default']
LucideUnlock: typeof import('~icons/lucide/unlock')['default']
LucideUserPlus: typeof import('~icons/lucide/user-plus')['default']
LucideUserPlus2: typeof import('~icons/lucide/user-plus2')['default']
LucideX: typeof import('~icons/lucide/x')['default']
Members: typeof import('./src/components/Settings/Members.vue')['default']
MobileLayout: typeof import('./src/components/MobileLayout.vue')['default']
NewTaskDialog: typeof import('./src/components/NewTaskDialog.vue')['default']
PageList: typeof import('./src/components/PageList.vue')['default']
Pie: typeof import('./src/components/Pie.vue')['default']
Poll: typeof import('./src/components/Poll.vue')['default']
PollEditor: typeof import('./src/components/PollEditor.vue')['default']
ProfileImageEditor: typeof import('./src/components/ProfileImageEditor.vue')['default']
ReactionFaceIcon: typeof import('./src/components/ReactionFaceIcon.vue')['default']
Reactions: typeof import('./src/components/Reactions.vue')['default']
ReadmeEditor: typeof import('./src/components/ReadmeEditor.vue')['default']
RevisionsDialog: typeof import('./src/components/RevisionsDialog.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SettingsDialog: typeof import('./src/components/Settings/SettingsDialog.vue')['default']
SettingsTab: typeof import('./src/components/Settings/SettingsTab.vue')['default']
Tabs: typeof import('./src/components/Tabs.vue')['default']
TaskDetail: typeof import('./src/components/TaskDetail.vue')['default']
TaskList: typeof import('./src/components/TaskList.vue')['default']
TaskPriorityIcon: typeof import('./src/components/icons/TaskPriorityIcon.vue')['default']
TaskStatusIcon: typeof import('./src/components/icons/TaskStatusIcon.vue')['default']
TeamMembers: typeof import('./src/components/TeamMembers.vue')['default']
TextEditor: typeof import('./src/components/TextEditor.vue')['default']
UnsplashImageBrowser: typeof import('./src/components/UnsplashImageBrowser.vue')['default']
UserAvatar: typeof import('./src/components/UserAvatar.vue')['default']
UserDropdown: typeof import('./src/components/UserDropdown.vue')['default']
UserImage: typeof import('./src/components/UserImage.vue')['default']
UserInfo: typeof import('./src/components/UserInfo.vue')['default']
UserProfileLink: typeof import('./src/components/UserProfileLink.vue')['default']
}
}
114 changes: 114 additions & 0 deletions frontend/src/components/DiscussionListByData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<template>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a duplicate of DiscussionList without the data fetching. The correct way for this refactor is to make DiscussionList use DiscussionListByData.

Also, DiscussionList is a better name for a component that only renders UI and data fetching can be done separately.

<div class="h-full">
<router-link
v-for="(d, index) in discussions"
:key="d.name"
:to="{
name: 'ProjectDiscussion',
params: {
teamId: d.team,
projectId: d.project,
postId: d.name,
slug: d.slug,
},
}"
class="group relative block h-15 rounded-[10px] transition hover:bg-gray-100"
>
<div class="flex h-full items-center space-x-4 overflow-hidden px-3 py-2">
<UserInfo :email="d.last_post_by || d.owner">
<template v-slot="{ user }">
<div class="flex items-center space-x-3">
<div class="relative flex">
<UserAvatar :user="d.closed_by || user.name" size="2xl" />
</div>
</div>
<div class="min-w-0 flex-1">
<div class="flex min-w-0 items-center">
<div
class="overflow-hidden text-ellipsis whitespace-nowrap leading-none text-gray-900'"
>
<span
class="overflow-hidden text-ellipsis whitespace-nowrap text-base font-medium"
>
{{ d.title }}
</span>
</div>
</div>
<div class="mt-1.5 flex min-w-0 items-center justify-between">
<div
class="overflow-hidden text-ellipsis whitespace-nowrap text-base text-gray-600"
>
<span>
{{ user.full_name }}
</span>
<template v-if="d.project">
<span> in </span>
<span>
{{ d.team }}
<span class="text-gray-500"> &mdash; </span>
{{ d.project }}
</span>
</template>
</div>
</div>
</div>
<div class="ml-auto">
<div
class="shrink-0 whitespace-nowrap text-sm text-gray-600"
:title="discussionTimestampDescription(d)"
>
{{ discussionTimestamp(d) }}
</div>
<div class="mt-1.5 flex items-center justify-end space-x-3">
<Badge>{{ d.comments_count + 1 }}</Badge>
</div>
</div>
</template>
</UserInfo>
</div>
<div
class="mx-3 h-px border-t border-gray-200"
v-if="index < discussions?.length - 1"
></div>
</router-link>
<div class="px-2 sm:px-0">
<div
v-if="discussions?.length === 0"
class="flex flex-col items-center rounded-lg border-2 border-dashed py-8 text-base text-gray-600"
>
<LucideCoffee class="h-7 w-7 text-gray-500" />
No discussions
</div>
</div>
</div>
</template>

<script>
export default {
name: 'DiscussionListByData',
props: {
discussions: {
type: Array,
required: true,
},
},
methods: {
discussionTimestamp(discussion) {
let timestamp = discussion.last_post_at || discussion.creation
if (this.$dayjs().diff(timestamp, 'day') < 25) {
return this.$dayjs(timestamp).fromNow()
}
if (this.$dayjs().diff(timestamp, 'year') < 1) {
return this.$dayjs(timestamp).format('D MMM')
}
return this.$dayjs(timestamp).format('D MMM YYYY')
},
discussionTimestampDescription(discussion) {
return [
`First Post: ${this.$dayjs(discussion.creation)}`,
`Latest Post: ${this.$dayjs(discussion.last_post_at)}`,
].join('\n')
},
},
}
</script>
21 changes: 11 additions & 10 deletions frontend/src/components/DiscussionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,17 @@ export default {
},
}
},
bookmark() {
bookmarkStatus() {
return {
type: 'resource',
url: 'gameplan.api.check_bookmark',
params: {
discussionId: this.postId,
type: 'list',
doctype: 'GP Bookmark',
filters: {
discussion: this.postId,
user: this.$user().name,
},
auto: true,
onSuccess(data) {
this.bookmarkStatus = data
this.bookmarkStatus = data.length ? true : false
},
}
},
Expand All @@ -302,13 +303,13 @@ export default {
}
},
methods: {
bookMarkDiscussion() {
toggleDiscussionBookmark() {
let data = {
discussion: this.discussion.name,
remove_bookmark: this.bookmarkStatus,
}
call('gameplan.api.bookmark_discussion', { data }).then((res) => {
this.$resources.bookmark.submit()
call('gameplan.api.toggle_discussion_bookmark', { data }).then((res) => {
this.$resources.bookmarkStatus.fetch()
})
},
copyLink() {
Expand Down Expand Up @@ -462,7 +463,7 @@ export default {
{
label: `${this.bookmarkStatus ? 'Remove Bookmark' : 'Add Bookmark'}`,
icon: 'bookmark',
onClick: this.bookMarkDiscussion,
onClick: this.toggleDiscussionBookmark,
},
{
label: 'Move to...',
Expand Down
Loading
Loading