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

Add Activities skeletor #11564

Merged
merged 16 commits into from
Sep 25, 2024
9 changes: 7 additions & 2 deletions dev/docker/ocis.web.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@
"ocm",
"webfinger",
"epub-reader",
"app-store"
"app-store",
"activities"
],
"external_apps": [
{
"id": "preview",
"path": "web-app-preview",
"config": {
"mimeTypes": ["image/tiff", "image/bmp", "image/x-ms-bmp"]
"mimeTypes": [
"image/tiff",
"image/bmp",
"image/x-ms-bmp"
]
}
},
{
Expand Down
9 changes: 9 additions & 0 deletions packages/web-app-activities/l10n/.tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com

[o:owncloud-org:p:owncloud-web:r:activities]
file_filter = locale/<lang>/app.po
minimum_perc = 0
source_file = template.pot
source_lang = en
type = PO
1 change: 1 addition & 0 deletions packages/web-app-activities/l10n/translations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
19 changes: 19 additions & 0 deletions packages/web-app-activities/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "web-app-activities",
"version": "0.0.0",
"private": true,
"description": "ownCloud activities app",
"license": "AGPL-3.0",
"devDependencies": {
"web-test-helpers": "workspace:*"
},
"peerDependencies": {
"@ownclouders/web-client": "workspace:*",
"@ownclouders/web-pkg": "workspace:*",
"design-system": "workspace:@ownclouders/design-system@*",
"pinia": "^2.2.2",
"vue-concurrency": "^5.0.1",
"vue-router": "^4.2.5",
"vue3-gettext": "^2.4.0"
}
}
20 changes: 20 additions & 0 deletions packages/web-app-activities/src/LayoutContainer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<main id="activities">
<router-view />
</main>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
name: 'LayoutContainer'
})
</script>

<style lang="scss">
#activities {
overflow: auto;
padding: var(--oc-space-medium) !important;
}
</style>
1 change: 1 addition & 0 deletions packages/web-app-activities/src/appid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const APPID = 'activities'
102 changes: 102 additions & 0 deletions packages/web-app-activities/src/components/ActivityItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<template>
<div class="oc-flex oc-flex-middle activity-item">
<div class="oc-flex oc-flex-middle">
<oc-avatar :width="36" :user-name="activity.template.variables.user.displayName" />
<span class="oc-ml-s" v-text="activity.template.variables.user.displayName" />
</div>
<div>activity unknown</div>
kulmann marked this conversation as resolved.
Show resolved Hide resolved
<div class="oc-text-truncate">
<resource-list-item v-if="resource" :resource="resource" :is-resource-clickable="false" />
<div
v-if="resourceNotAccessible"
class="oc-text-muted oc-flex oc-flex-middle oc-p-xs"
v-oc-tooltip="$gettext('The resource is unavailable, it may have been deleted.')"
>
<oc-icon name="eye-off" />
<span class="oc-ml-s" v-text="activity.template.variables.resource.name" />
</div>
</div>
<div><span v-text="recordedDateTime" /></div>
</div>
</template>

<script lang="ts">
import { computed, defineComponent, onMounted, PropType, ref, unref } from 'vue'
import { Activity } from '@ownclouders/web-client/graph/generated'
import { DateTime } from 'luxon'
import {
formatDateFromDateTime,
formatRelativeDateFromDateTime,
ResourceListItem,
useClientService
} from '@ownclouders/web-pkg'
import { useGettext } from 'vue3-gettext'
import { Resource } from '@ownclouders/web-client'
import { DavProperty } from '@ownclouders/web-client/webdav'

export default defineComponent({
name: 'ActivityList',
components: { ResourceListItem },
props: {
activity: {
type: Object as PropType<Activity>,
required: true
}
},
setup(props) {
const clientService = useClientService()
const { current: currentLanguage } = useGettext()
const resource = ref<Resource>()
const resourceNotAccessible = ref(false)

const recordedDateTime = computed(() => {
const dateTime = DateTime.fromISO(props.activity.times.recordedTime)

const isWithinLastHour = dateTime > DateTime.now().minus({ hour: 1 })
if (isWithinLastHour) {
return formatRelativeDateFromDateTime(dateTime, currentLanguage)
}

return formatDateFromDateTime(dateTime, currentLanguage)
})

onMounted(async () => {
try {
resource.value = await clientService.webdav.getFileInfo(
AlexAndBear marked this conversation as resolved.
Show resolved Hide resolved
unref(props.activity.template.variables.space.id),
{ fileId: props.activity.template.variables.resource.id },
{ davProperties: DavProperty.name }
)
} catch (e) {
resourceNotAccessible.value = true
}
})

return {
recordedDateTime,
resource,
resourceNotAccessible
}
}
})
</script>

<style lang="scss">
.activity-item {
.oc-resource-name {
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
overflow: hidden;
}
}

.activity-item > * {
flex: 1;
text-align: left;
}

.activity-item > *:last-child {
text-align: right !important;
}
</style>
67 changes: 67 additions & 0 deletions packages/web-app-activities/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import translations from '../l10n/translations.json'
import { useGettext } from 'vue3-gettext'
import { computed } from 'vue'
import { AppMenuItemExtension, defineWebApplication, Extension } from '@ownclouders/web-pkg'
import { urlJoin } from '@ownclouders/web-client'
import { RouteRecordRaw } from 'vue-router'
import { APPID } from './appid'

export default defineWebApplication({
setup() {
const { $gettext } = useGettext()

const appInfo = {
name: $gettext('Activities'),
id: APPID,
icon: 'pulse',
color: '#887ef1'
}

const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'root',
component: () => import('./LayoutContainer.vue'),
redirect: urlJoin(appInfo.id, 'list'),
meta: {
authContext: 'user'
},
children: [
{
path: 'list',
name: 'list',
component: () => import('./views/App.vue'),
meta: {
authContext: 'user',
title: $gettext('Activities')
}
}
]
}
]

const menuItemExtension: AppMenuItemExtension = {
id: `app.${appInfo.id}.menuItem`,
type: 'appMenuItem',
label: () => appInfo.name,
color: appInfo.color,
icon: appInfo.icon,
priority: 30,
path: urlJoin(appInfo.id)
}
const extensions = computed(() => {
const result: Extension[] = []

result.push(menuItemExtension)

return result
})

return {
appInfo,
routes,
translations,
extensions
}
}
})
73 changes: 73 additions & 0 deletions packages/web-app-activities/src/views/ActivityList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<template>
<oc-list class="activity-list">
<li v-for="(activities, date) in activitiesDateCategorized" :key="date" class="oc-mb-l">
<h2 class="oc-text-bold oc-text-muted activity-list-date oc-text-medium" v-text="getDateHeadline(date)" />
<ul class="oc-ml-s oc-mt-s timeline">
AlexAndBear marked this conversation as resolved.
Show resolved Hide resolved
<li v-for="activityItem in activities" :key="activityItem">
<ActivityItem :activity="activityItem" />
</li>
</ul>
</li>
</oc-list>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { Activity } from '@ownclouders/web-client/graph/generated'
import { DateTime } from 'luxon'
import ActivityItem from '../components/ActivityItem.vue'
import { formatDateFromDateTime } from '@ownclouders/web-pkg'
import { useGettext } from 'vue3-gettext'

export default defineComponent({
name: 'ActivityList',
components: { ActivityItem },
props: {
activities: {
type: Array as PropType<Activity[]>,
required: true
}
},
setup(props) {
const { current: currentLanguage } = useGettext()

const activitiesDateCategorized = computed(() => {
return props.activities.reduce((acc, activity) => {
const date = DateTime.fromISO(activity.times.recordedTime).toISODate()

if (!acc[date]) {
acc[date] = []
}
acc[date].push(activity)

return acc
}, {})
})

const getDateHeadline = (dateISO: string) => {
const dateTime = DateTime.fromISO(dateISO)

if (
dateTime.hasSame(DateTime.now(), 'day') ||
dateTime.hasSame(DateTime.now().minus({ day: 1 }), 'day')
) {
return dateTime.toRelativeCalendar({ locale: currentLanguage })
}

return formatDateFromDateTime(dateTime, currentLanguage, DateTime.DATE_MED_WITH_WEEKDAY)
}

return { activitiesDateCategorized, getDateHeadline }
}
})
</script>

<style lang="scss">
.activity-list {
max-width: 1000px;

&-date {
text-transform: capitalize;
}
}
</style>
Loading