Skip to content

Commit a39bf35

Browse files
feat: profile on mobile view
Signed-off-by: Kristian Zendato <kristian.zendato@nextcloud.com>
1 parent 9f5d6ce commit a39bf35

358 files changed

Lines changed: 435 additions & 339 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

REUSE.toml

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

apps/profile/src/views/ProfileApp.vue

Lines changed: 125 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { subscribe, unsubscribe } from '@nextcloud/event-bus'
1212
import { loadState } from '@nextcloud/initial-state'
1313
import { translate as t } from '@nextcloud/l10n'
1414
import { generateUrl } from '@nextcloud/router'
15+
import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile'
1516
import { computed, onBeforeMount, onBeforeUnmount, onMounted, ref } from 'vue'
1617
import NcActionLink from '@nextcloud/vue/components/NcActionLink'
1718
import NcActions from '@nextcloud/vue/components/NcActions'
@@ -22,7 +23,8 @@ import NcContent from '@nextcloud/vue/components/NcContent'
2223
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
2324
import NcRichText from '@nextcloud/vue/components/NcRichText'
2425
import AccountIcon from 'vue-material-design-icons/AccountOutline.vue'
25-
import MapMarkerIcon from 'vue-material-design-icons/MapMarker.vue'
26+
import BriefcaseIcon from 'vue-material-design-icons/BriefcaseOutline.vue'
27+
import MapMarkerIcon from 'vue-material-design-icons/MapMarkerOutline.vue'
2628
import PencilIcon from 'vue-material-design-icons/PencilOutline.vue'
2729
import ProfileSection from '../components/ProfileSection.vue'
2830
@@ -60,9 +62,13 @@ onBeforeMount(() => {
6062
})
6163
6264
const isCurrentUser = getCurrentUser()?.uid === profileParameters.userId
65+
const isMobile = useIsMobile()
6366
6467
const primaryAction = profileParameters.actions[0]
65-
const otherActions = profileParameters.actions.slice(1)
68+
const showEditAsPrimaryAction = computed(() => isMobile.value && isCurrentUser)
69+
const otherActions = computed(() => showEditAsPrimaryAction.value
70+
? profileParameters.actions
71+
: profileParameters.actions.slice(1))
6672
6773
const settingsUrl = generateUrl('/settings/user')
6874
const emptyProfileMessage = isCurrentUser
@@ -113,48 +119,69 @@ function openStatusModal() {
113119
<NcAppContent>
114120
<div class="profile__header">
115121
<div class="profile__header__container">
122+
<NcAvatar
123+
v-if="isMobile"
124+
class="avatar profile__header__container__avatar"
125+
:class="{ interactive: isCurrentUser }"
126+
:user="profileParameters.userId"
127+
:size="120"
128+
disableMenu
129+
disableTooltip
130+
:isNoUser="!profileParameters.isUserAvatarVisible"
131+
@click.prevent.stop="openStatusModal" />
116132
<div class="profile__header__container__placeholder" />
117133
<div class="profile__header__container__displayname">
118134
<h2>{{ profileParameters.displayname || profileParameters.userId }}</h2>
119-
<span v-if="profileParameters.pronouns">·</span>
135+
<span v-if="profileParameters.pronouns" class="profile__header__container__pronoun-separator">·</span>
120136
<span v-if="profileParameters.pronouns" class="profile__header__container__pronouns">{{ profileParameters.pronouns }}</span>
137+
</div>
138+
<div class="profile__header__container__controls">
139+
<NcButton
140+
v-if="userStatus.icon || userStatus.message"
141+
class="profile__header__container__status"
142+
:disabled="!isCurrentUser"
143+
:variant="isCurrentUser ? 'tertiary' : 'tertiary-no-background'"
144+
@click="openStatusModal">
145+
<span class="profile__header__container__status-content">
146+
<span>{{ userStatus.icon }} {{ userStatus.message }}</span>
147+
<PencilIcon v-if="isCurrentUser" :size="20" />
148+
</span>
149+
</NcButton>
121150
<NcButton
122-
v-if="isCurrentUser"
151+
v-if="isCurrentUser && !isMobile"
123152
variant="primary"
124153
:href="settingsUrl">
125-
<template #icon>
126-
<PencilIcon :size="20" />
127-
</template>
128-
{{ t('profile', 'Edit Profile') }}
154+
{{ t('profile', 'Edit profile') }}
129155
</NcButton>
130156
</div>
131-
<NcButton
132-
v-if="userStatus.icon || userStatus.message"
133-
:disabled="!isCurrentUser"
134-
:variant="isCurrentUser ? 'tertiary' : 'tertiary-no-background'"
135-
@click="openStatusModal">
136-
{{ userStatus.icon }} {{ userStatus.message }}
137-
</NcButton>
138157
</div>
139158
</div>
140159

141160
<div class="profile__wrapper">
142161
<div class="profile__content">
143162
<div class="profile__sidebar">
144163
<NcAvatar
164+
v-if="!isMobile"
145165
class="avatar"
146166
:class="{ interactive: isCurrentUser }"
147167
:user="profileParameters.userId"
148168
:size="180"
149-
:disableMenu="true"
150-
:disableTooltip="true"
169+
disableMenu
170+
disableTooltip
151171
:isNoUser="!profileParameters.isUserAvatarVisible"
152172
@click.prevent.stop="openStatusModal" />
153173

154174
<div class="user-actions">
175+
<NcButton
176+
v-if="showEditAsPrimaryAction"
177+
variant="primary"
178+
class="user-actions__primary"
179+
:href="settingsUrl">
180+
{{ t('profile', 'Edit profile') }}
181+
</NcButton>
155182
<!-- When a tel: URL is opened with target="_blank", a blank new tab is opened which is inconsistent with the handling of other URLs so we set target="_self" for the phone action -->
156183
<NcButton
157-
v-if="primaryAction"
184+
v-if="primaryAction && !showEditAsPrimaryAction"
158185
variant="primary"
159186
class="user-actions__primary"
160187
:href="primaryAction.target"
@@ -167,7 +194,10 @@ function openStatusModal() {
167194
</template>
168195
{{ primaryAction.title }}
169196
</NcButton>
170-
<NcActions class="user-actions__other" :inline="4">
197+
<NcActions
198+
v-if="otherActions.length > 0"
199+
class="user-actions__other"
200+
:inline="isMobile ? 5 : 4">
171201
<NcActionLink
172202
v-for="action in otherActions"
173203
:key="action.id"
@@ -188,12 +218,17 @@ function openStatusModal() {
188218
<div class="profile__blocks">
189219
<div v-if="profileParameters.organisation || profileParameters.role || profileParameters.address" class="profile__blocks-details">
190220
<div v-if="profileParameters.organisation || profileParameters.role" class="detail">
191-
<p>{{ profileParameters.organisation }} <span v-if="profileParameters.organisation && profileParameters.role">•</span> {{ profileParameters.role }}</p>
221+
<p>
222+
<BriefcaseIcon
223+
class="detail-icon"
224+
:size="16" />
225+
{{ profileParameters.organisation }} <span v-if="profileParameters.organisation && profileParameters.role">•</span> {{ profileParameters.role }}
226+
</p>
192227
</div>
193228
<div v-if="profileParameters.address" class="detail">
194229
<p>
195230
<MapMarkerIcon
196-
class="map-icon"
231+
class="detail-icon"
197232
:size="16" />
198233
{{ profileParameters.address }}
199234
</p>
@@ -234,6 +269,7 @@ $content-max-width: 640px;
234269
235270
:deep(#app-content-vue) {
236271
background-color: unset;
272+
min-width: 320px;
237273
}
238274
239275
.profile {
@@ -282,6 +318,18 @@ $content-max-width: 640px;
282318
font-size: 20px;
283319
}
284320
}
321+
322+
&__controls {
323+
display: flex;
324+
align-items: center;
325+
gap: 8px;
326+
}
327+
328+
&__status-content {
329+
display: flex;
330+
align-items: center;
331+
gap: 8px;
332+
}
285333
}
286334
}
287335
@@ -293,7 +341,10 @@ $content-max-width: 640px;
293341
min-width: 220px;
294342
margin-block: -150px 0;
295343
margin-inline: 0 20px;
344+
}
296345
346+
&__header,
347+
&__sidebar {
297348
// Specificity hack is needed to override Avatar component styles
298349
:deep(.avatar.avatardiv) {
299350
text-align: center;
@@ -336,7 +387,7 @@ $content-max-width: 640px;
336387
}
337388
338389
&__blocks {
339-
margin: 18px 0 80px 0;
390+
margin: 18px 0 80px 16px;
340391
display: grid;
341392
gap: 16px 0;
342393
width: $content-max-width;
@@ -355,7 +406,7 @@ $content-max-width: 640px;
355406
display: inline-block;
356407
color: var(--color-text-maxcontrast);
357408
358-
p .map-icon {
409+
p .detail-icon {
359410
display: inline-block;
360411
vertical-align: middle;
361412
}
@@ -380,7 +431,7 @@ $content-max-width: 640px;
380431
381432
&__primary {
382433
margin: 0 auto;
383-
max-width: 100%;
434+
width: 180px !important;
384435
385436
&__icon {
386437
filter: var(--primary-invert-if-dark);
@@ -406,39 +457,58 @@ $content-max-width: 640px;
406457
@media only screen and (max-width: 1024px) {
407458
.profile {
408459
&__header {
409-
height: 250px;
460+
height: 190px;
410461
position: unset;
411462
412463
&__container {
413-
grid-template-columns: unset;
414-
margin-bottom: 110px;
464+
align-self: center;
465+
grid-template-columns: 136px minmax(0, 1fr);
466+
grid-template-rows: max-content max-content;
467+
max-width: 600px;
468+
margin: 0 auto;
469+
padding-inline: 16px;
470+
row-gap: 0;
471+
472+
&__avatar {
473+
grid-column: 1;
474+
grid-row: 1 / 3;
475+
justify-self: center;
476+
}
477+
478+
&__placeholder {
479+
display: none;
480+
}
415481
416482
&__displayname {
417-
margin: 80px 20px 0px 0px!important;
483+
grid-column: 2;
484+
grid-row: 1;
485+
margin: 0;
418486
width: unset;
419-
text-align: center;
420-
padding-inline: 12px;
487+
height: unset;
488+
flex-direction: column;
489+
align-items: flex-start;
490+
gap: 2px;
491+
492+
h2 {
493+
font-size: 24px;
494+
overflow-wrap: anywhere;
495+
}
421496
}
422497
423-
&__edit-button {
424-
width: fit-content;
425-
display: block;
426-
margin: 60px auto;
498+
&__pronoun-separator {
499+
display: none;
427500
}
428501
429-
&__status-text {
430-
margin: 4px auto;
502+
&__status {
503+
grid-column: 2;
504+
grid-row: 2;
505+
justify-self: start;
431506
}
432507
}
433508
}
434509
435510
&__content {
436511
display: block;
437-
438-
.avatar {
439-
// Overlap avatar to top header
440-
margin-top: -110px !important;
441-
}
442512
}
443513
444514
&__blocks {
@@ -452,13 +522,27 @@ $content-max-width: 640px;
452522
margin: unset;
453523
position: unset;
454524
}
525+
526+
&__header,
527+
&__sidebar {
528+
:deep(.avatar.avatardiv) {
529+
.avatardiv__user-status {
530+
inset-inline-end: 0;
531+
bottom: 0;
532+
}
533+
}
534+
}
455535
}
456536
457537
.user-actions {
458538
width: unset;
459539
max-width: 600px;
460540
margin: 0 auto;
461541
padding: 20px 50px 0px 50px;
542+
543+
&__primary {
544+
width: 220px !important;
545+
}
462546
}
463547
}
464548
</style>

core/img/actions/mail-outline.svg

Lines changed: 1 addition & 0 deletions
Loading

core/img/actions/phone-outline.svg

Lines changed: 1 addition & 0 deletions
Loading

core/img/actions/timezone.svg

Lines changed: 1 addition & 1 deletion
Loading

dist/ActivityCommentAction-BGREODgE.chunk.mjs

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import{a as t}from"./index-6vw6WPN4.chunk.mjs";import{t as m}from"./translation-DoG5ZELJ-hYauSqYO.chunk.mjs";import{C as e,a as p}from"./CommentView-D2KR4_N4.chunk.mjs";import{l as i}from"./activity-CAMqbip4.chunk.mjs";import{b as a,r as s,o as n,c,m as u}from"./Web-C6SCsxGW.chunk.mjs";import{_ as l}from"./public-C1mLBHT3.chunk.mjs";import"./mdi-DUejZYLr.chunk.mjs";import"./NcModal-DiDvgcU8-Bkdv9ZLr.chunk.mjs";import"./logger-D3RVzcfQ-C5odKnCF.chunk.mjs";import"./createElementId-DhjFt1I9-B6NfXHiG.chunk.mjs";import"./index-kP1pdrFW.chunk.mjs";import"./Check-CPfb-TC6.chunk.mjs";import"./TrashCanOutline-PqQ-0bHn.chunk.mjs";import"./PencilOutline-DxKH-9EP.chunk.mjs";import"./NcActionSeparator-B9pNQaji-qiliI1Bl.chunk.mjs";import"./NcAvatar-1KxMUN7V-BeBC6ybQ.chunk.mjs";import"./index-D9pEuVUV.chunk.mjs";import"./util-C9Hc1fff.chunk.mjs";import"./ArrowRight-KF6TRuCS.chunk.mjs";import"./colors-Cv9F-jWS-CKv124bx.chunk.mjs";import"./NcUserStatusIcon-BF5OEQFU-CfpesNPs.chunk.mjs";import"./NcActionLink-BFiaYt9A-DhRpxriE.chunk.mjs";import"./NcDateTime.vue_vue_type_script_setup_true_lang-BJuPH7S7-CCycrJH_.chunk.mjs";import"./NcActionText-CQ9qwJ0p-DhTj8Eo7.chunk.mjs";import"./NcUserBubble-BXR7vT0V-BP1yaUQ3.chunk.mjs";import"./GetComments-cZxNi7-L.chunk.mjs";import"./index-CZScwDbg.chunk.mjs";import"./dav-CchuWPTa.chunk.mjs";import"./externalStorageUtils-C3askeL2.chunk.mjs";const d=a({components:{CommentEntry:p},mixins:[e],props:{reloadCallback:{type:Function,required:!0}},methods:{onNewComment(){try{this.reloadCallback()}catch(o){t(m("comments","Could not reload comments")),i.error("Could not reload comments",{error:o})}}}});function C(o,f,y,b,w,N){const r=s("CommentEntry");return n(),c(r,u(o.editorData,{autoComplete:o.autoComplete,resourceType:o.resourceType,editor:!0,userData:o.userData,resourceId:o.resourceId,class:"comments-action",onNew:o.onNewComment}),null,16,["autoComplete","resourceType","userData","resourceId","onNew"])}const W=l(d,[["render",C],["__scopeId","data-v-099b6b12"]]);export{W as default};
2+
//# sourceMappingURL=ActivityCommentAction-HElFzY9V.chunk.mjs.map
File renamed without changes.

0 commit comments

Comments
 (0)