Skip to content

Commit

Permalink
Person to person sharing (#1799)
Browse files Browse the repository at this point in the history
* Initial work to add person-to-person sharing

* A few sharing item UI refinements

* Initial new sharing form with multiple upload

* Add a grey box...

* Remove redundant check

* Rename sharing to offers

* Add a mock chat to offer detail UI

* Create mobile version of offer detail UI

* Tidy up multiple image editing

* A bit more polish on the offer UI

Some translations, icon, breadcrumb, etc...

* Snapshot update

* Add create offer breadcrumb title

* Rename a few offer related things for consistency

* Offer image carousel, multi upload, various bits

* Refine image upload/remove/re-position

* Add websocket listeners for offer changes

* Wireup offer conversation

* Basic features to allow to accept/archive offers

* Remove uneeded demo data

* Move selected offer data/logic to own store module

* Preserve offer filter in more places

The create/save filter preservation does not work actually
as we clear the store contents on route change.

* Remove status from offer form

Status change is now done via explicit actions/buttons

* Use address autocomplete (#1878)

* Use address autocomplete

As it used to be.
Also defaults place edit map to be group centre if available.

* Remove uneeded extra wrapped div

* Update setup for storyshots testing

"sync: false" seems to be the new way it goes and will be the default
in the future, see vuejs/vue-test-utils#1137

The transition group stubs seem to be there by default now too, I think..
Anyway it works where it did no tbefore :)

Also see vuejs/vue-test-utils#1163

* Add @types/jest - for editor support

See https://blog.jetbrains.com/webstorm/2018/10/testing-with-jest-in-webstorm/

* Increase test wait

Not really sure why this changed, but don't want to think about
it for now...

* Add text to translation file

* Use the spelling of the enemy

* Simplify template logic

* Ffix multicroppa

* Further refine offer editing, esp MultiCroppa

* Use gift as offers menu item

* Remove offer upload progress logging

* Nicer create offer button

* Clear offerDetail on route leave

* Add offer list loading indicator

* Add offer body loading indicator

* Add loading spinner when fetching offer for edit

* Correctly wire up offer form reset events

* Only show offer accept/archive buttons if active

* Handle updating offers after save/websocket better

* Cleaner mobile form view

* Only show carousel if >1 image

* Fix offer header when name is very long

* Fix offer detail image display when only 1

* Fix overflowing text on group offers page

* White background offer description

* Snapshot update

* Don't try and be fancy with updating users

* Fix style style

* Hide offers if group does not have the feature

* Check route features in after route hook

* Actually filter offers by current group

* Add offers covert function to convert createdAt

* Display offer information in latest messages

I did it a slightly different way, by adding related offers
into the latestMessages state...

* Keep related offers up to date via websocket

It's a bit overkeen actually as most users won't be in the
conversations for the offers, so we don't need to update the
related item... should really check we need it first

* Import default offer status from store

* Use current route query instead of getter

* Use + for new offer button

* Allow setting new_offer notification types

* Show offer status if not active

Although it's a bit ugly

* Change notification type spinner

* Add group.features to test mocks

* Wrap settings forms in FormContainer

* Rename to KFormContainer and move to base

* Add confirm step to accept/archive offer buttons

* Use photo icon for offer image placeholder

* Add offer as a "fake" empty result

* Filter offers by status and sort by createdAt desc

... entries can get added via websockets that might not fit
the filter, and might need reordering

* Refine group offer cards

- use QImg instead of lots of CSS
- use QItem inside for nicer layout

* Correct RouterLink casing

* Update test snapshots

* Please the pedantic linter

* Remove offer description helper

* Extract mapErrors to statusMixin

* Add accept/archive text into i18n

* Remove unneeded code

* Add offer filter status text to i18n

* Correct size for new offer button

* Remove one layer of div soup

* Put the group offer cards in proper links

* Remove commented out code

* Convert offer in websocket update

* Prefix functional getter with get

For getNotificationTypeStatus

* Preserve route query when clicking on offer conv

* Use icon service for latest messages offer item

* Move withoutKeys to utils

* Use invisible class instead of custom css

* Don't put const value in data

* Save disk space by compacting template

* Remove unneeded v-model

Was only used when I tried out quasar :rules

* Document MultiCroppa value prop a bit more

* Document the data that latestMesages/related has
  • Loading branch information
nicksellen authored and tiltec committed Nov 26, 2019
1 parent 4339ba7 commit 0a4a890
Show file tree
Hide file tree
Showing 39 changed files with 1,550 additions and 48 deletions.
54 changes: 39 additions & 15 deletions src/__snapshots__/storyshots.spec.js.snap
Expand Up @@ -338,7 +338,7 @@ exports[`Storyshots ChangePhoto with photo 1`] = `
`;

exports[`Storyshots ChatConversation closed 1`] = `
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding" style="display:none;"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -511,7 +511,7 @@ exports[`Storyshots ChatConversation closed 1`] = `
`;

exports[`Storyshots ChatConversation default 1`] = `
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding" style="display:none;"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -673,7 +673,7 @@ exports[`Storyshots ChatConversation default 1`] = `
`;

exports[`Storyshots ChatConversation fetching past 1`] = `
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -835,7 +835,7 @@ exports[`Storyshots ChatConversation fetching past 1`] = `
`;

exports[`Storyshots ChatConversation message groups 1`] = `
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding" style="display:none;"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -1159,7 +1159,7 @@ exports[`Storyshots ChatConversation message groups 1`] = `
`;

exports[`Storyshots ChatConversation not participant 1`] = `
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding" style="display:none;"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -1321,7 +1321,7 @@ exports[`Storyshots ChatConversation not participant 1`] = `
`;

exports[`Storyshots ChatConversation thread 1`] = `
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding" style="display:none;"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -2058,7 +2058,7 @@ exports[`Storyshots Detail application 1`] = `
<animate attributeName="fill-opacity" from="1" to="1" begin="0s" dur="0.8s" values="1;.5;1" calcMode="linear" repeatCount="indefinite"></animate>
</circle>
</svg></div>
<div>
<div class="bg-white">
<div class="q-expansion-item q-item-type bg-grey-2 q-expansion-item--expanded q-expansion-item--standard">
<div class="q-expansion-item__container relative-position">
<div tabindex="0" class="q-item q-item-type row no-wrap q-item--clickable q-link cursor-pointer q-focusable q-hoverable text-bold">
Expand Down Expand Up @@ -2314,7 +2314,7 @@ exports[`Storyshots Detail pickup 1`] = `
<animate attributeName="fill-opacity" from="1" to="1" begin="0s" dur="0.8s" values="1;.5;1" calcMode="linear" repeatCount="indefinite"></animate>
</circle>
</svg></div>
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding" style="display:none;"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -2543,7 +2543,7 @@ exports[`Storyshots Detail private 1`] = `
<animate attributeName="fill-opacity" from="1" to="1" begin="0s" dur="0.8s" values="1;.5;1" calcMode="linear" repeatCount="indefinite"></animate>
</circle>
</svg></div>
<div>
<div class="bg-white">
<div class="full-width text-center generic-padding" style="display:none;"><svg fill="currentColor" width="40" height="40" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" class="q-spinner">
<circle cx="15" cy="15" r="15">
<animate attributeName="r" from="15" to="15" begin="0s" dur="0.8s" values="15;9;15" calcMode="linear" repeatCount="indefinite"></animate>
Expand Down Expand Up @@ -4658,7 +4658,7 @@ exports[`Storyshots GroupPreviewUI without public description 1`] = `
`;

exports[`Storyshots GroupSettings show 1`] = `
<div id="notifications" class="q-card no-shadow grey-border">
<div id="notifications" class="q-card bg-white q-mb-lg no-shadow grey-border">
<div class="q-card__section">
<div class="text-h6">
Notifications by group
Expand Down Expand Up @@ -4702,6 +4702,9 @@ exports[`Storyshots GroupSettings show 1`] = `
Keep up to date with what's been happening in the group.
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label><label tabindex="0" class="q-item q-item-type row no-wrap q-item--clickable q-link cursor-pointer q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
<div class="q-item__section column q-item__section--side justify-center">
Expand All @@ -4723,6 +4726,9 @@ exports[`Storyshots GroupSettings show 1`] = `
Reminds you of your pickups, and tells you about empty or not-full pickups of your favorite places in the next day
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label><label tabindex="0" class="q-item q-item-type row no-wrap q-item--clickable q-link cursor-pointer q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
<div class="q-item__section column q-item__section--side justify-center">
Expand All @@ -4744,6 +4750,9 @@ exports[`Storyshots GroupSettings show 1`] = `
Tells you when somebody wants to join your group
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label><label tabindex="0" class="q-item q-item-type row no-wrap q-item--clickable q-link cursor-pointer q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
<div class="q-item__section column q-item__section--side justify-center">
Expand All @@ -4765,6 +4774,9 @@ exports[`Storyshots GroupSettings show 1`] = `
Tells you when a new conflict resolution process has been started in your group
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label>
<div class="q-pt-md q-pl-md"><button tabindex="0" type="button" class="q-btn inline q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
Expand Down Expand Up @@ -9948,7 +9960,7 @@ exports[`Storyshots ProfilePicture without user 1`] = `

exports[`Storyshots Settings Page Default 1`] = `
<div>
<div class="q-card no-shadow grey-border">
<div class="q-card bg-white q-mb-lg no-shadow grey-border">
<div class="q-card__section">
<div class="text-h6">
Profile
Expand Down Expand Up @@ -10076,7 +10088,7 @@ exports[`Storyshots Settings Page Default 1`] = `
</div>
</div>
</div>
<div class="q-card no-shadow grey-border">
<div class="q-card bg-white q-mb-lg no-shadow grey-border">
<div class="q-card__section">
<div class="text-h6">
Switch language
Expand All @@ -10092,7 +10104,7 @@ exports[`Storyshots Settings Page Default 1`] = `
</button></div>
</div>
</div>
<div class="q-card no-shadow grey-border">
<div class="q-card bg-white q-mb-lg no-shadow grey-border">
<div class="q-card__section">
<div class="text-h6">
Account
Expand Down Expand Up @@ -10182,7 +10194,7 @@ exports[`Storyshots Settings Page Default 1`] = `
</div>
</div>
</div>
<div id="notifications" class="q-card no-shadow grey-border">
<div id="notifications" class="q-card bg-white q-mb-lg no-shadow grey-border">
<div class="q-card__section">
<div class="text-h6">
Notifications by group
Expand Down Expand Up @@ -10226,6 +10238,9 @@ exports[`Storyshots Settings Page Default 1`] = `
Keep up to date with what's been happening in the group.
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label><label tabindex="0" class="q-item q-item-type row no-wrap q-item--clickable q-link cursor-pointer q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
<div class="q-item__section column q-item__section--side justify-center">
Expand All @@ -10247,6 +10262,9 @@ exports[`Storyshots Settings Page Default 1`] = `
Reminds you of your pickups, and tells you about empty or not-full pickups of your favorite places in the next day
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label><label tabindex="0" class="q-item q-item-type row no-wrap q-item--clickable q-link cursor-pointer q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
<div class="q-item__section column q-item__section--side justify-center">
Expand All @@ -10268,6 +10286,9 @@ exports[`Storyshots Settings Page Default 1`] = `
Tells you when somebody wants to join your group
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label><label tabindex="0" class="q-item q-item-type row no-wrap q-item--clickable q-link cursor-pointer q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
<div class="q-item__section column q-item__section--side justify-center">
Expand All @@ -10289,6 +10310,9 @@ exports[`Storyshots Settings Page Default 1`] = `
Tells you when a new conflict resolution process has been started in your group
</div>
</div>
<div class="q-item__section column q-item__section--side justify-center"><svg width="18px" height="18px" viewBox="25 25 50 50" class="q-spinner q-spinner-mat invisible">
<circle cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10" class="path"></circle>
</svg></div>
</label>
<div class="q-pt-md q-pl-md"><button tabindex="0" type="button" class="q-btn inline q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
<div tabindex="-1" class="q-focus-helper"></div>
Expand All @@ -10305,7 +10329,7 @@ exports[`Storyshots Settings Page Default 1`] = `
</div>
</div>
</div>
<div class="q-card no-shadow grey-border">
<div class="q-card bg-white q-mb-lg no-shadow grey-border">
<div class="q-card__section">
<div class="text-h6">
Push notifications
Expand Down
1 change: 1 addition & 0 deletions src/authuser/pages/Settings.story.js
Expand Up @@ -35,6 +35,7 @@ const datastore = createDatastore({
currentGroup: {
getters: {
value: () => groupsMock[0],
getNotificationTypeStatus: () => () => ({}),
},
},
groups: {
Expand Down
21 changes: 10 additions & 11 deletions src/authuser/pages/Settings.vue
@@ -1,6 +1,6 @@
<template>
<div v-if="user">
<QCard class="no-shadow grey-border">
<KFormContainer>
<QCardSection>
<div class="text-h6">
{{ $t('USERDATA.PROFILE_TITLE') }}
Expand All @@ -22,8 +22,8 @@
@save="saveUser"
/>
</QCardSection>
</QCard>
<QCard class="no-shadow grey-border">
</KFormContainer>
<KFormContainer>
<QCardSection>
<div class="text-h6">
{{ $t('LANGUAGECHOOSER.SWITCH') }}
Expand All @@ -34,8 +34,8 @@
<LocaleSelect />
</div>
</QCardSection>
</QCard>
<QCard class="no-shadow grey-border">
</KFormContainer>
<KFormContainer>
<QCardSection>
<div class="text-h6">
{{ $t('USERDATA.ACCOUNT') }}
Expand All @@ -59,11 +59,10 @@
/>
</QCardActions>
</QCardSection>
</QCard>
</KFormContainer>
<GroupSettings />
<QCard
<KFormContainer
v-if="!$q.platform.is.cordova"
class="no-shadow grey-border"
>
<QCardSection>
<div class="text-h6">
Expand All @@ -78,13 +77,12 @@
@disable="disablePush"
/>
</QCardSection>
</QCard>
</KFormContainer>
</div>
</template>

<script>
import {
QCard,
QCardSection,
QSeparator,
QCardActions,
Expand All @@ -100,11 +98,11 @@ import RequestDeleteAccount from '@/authuser/components/Settings/RequestDeleteAc
import Push from '@/authuser/components/Settings/Push'
import LocaleSelect from '@/utils/components/LocaleSelect'
import GroupSettings from '@/group/pages/Settings'
import KFormContainer from '@/base/components/KFormContainer'
export default {
name: 'Settings',
components: {
QCard,
QCardSection,
QSeparator,
QCardActions,
Expand All @@ -116,6 +114,7 @@ export default {
Push,
LocaleSelect,
GroupSettings,
KFormContainer,
},
computed: {
...mapGetters({
Expand Down
13 changes: 13 additions & 0 deletions src/base/components/KFormContainer.vue
@@ -0,0 +1,13 @@
<script>
import { Platform, QCard } from 'quasar'
const Component = Platform.is.mobile ? 'div' : QCard
export default {
functional: true,
render (h, { data, children }) {
if (data.class) {
throw new Error('KFormContainer component does not support class attribute')
}
return h(Component, { ...data, class: ['bg-white', 'q-mb-lg', 'no-shadow', 'grey-border'] }, children)
},
}
</script>
2 changes: 2 additions & 0 deletions src/base/datastore.js
Expand Up @@ -20,6 +20,7 @@ import pickups from '@/pickups/datastore'
import sidenav from '@/sidenav/datastore'
import places from '@/places/datastore'
import subscriptions from '@/subscriptions/datastore'
import offers from '@/offers/datastore'
import topbar from '@/topbar/datastore'
import unsubscribe from '@/unsubscribe/datastore'
import users from '@/users/datastore'
Expand All @@ -45,6 +46,7 @@ const appModules = [
sidenav,
places,
subscriptions,
offers,
topbar,
unsubscribe,
users,
Expand Down
16 changes: 15 additions & 1 deletion src/base/datastore/routerPlugin.js
Expand Up @@ -4,6 +4,7 @@ import * as Sentry from '@sentry/browser'
export default datastore => {
const isLoggedIn = () => datastore.getters['auth/isLoggedIn']
const getUserGroupId = () => isLoggedIn() && datastore.getters['auth/user'].currentGroup
const getFeatures = () => datastore.getters['currentGroup/features']

router.beforeEach(async (to, from, nextFn) => {
datastore.commit('routeMeta/setNext', to)
Expand All @@ -25,7 +26,6 @@ export default datastore => {
// redirect homescreen correctly
else if (to.path === '/') {
const groupId = getUserGroupId()

if (groupId) {
next = { name: 'group', params: { groupId } }
}
Expand Down Expand Up @@ -73,6 +73,20 @@ export default datastore => {
catch (err) {
Sentry.captureException(err)
}

// Check if our route requires any features we don't have
// It would be nice to do this _before_ we visit the route, but we don't have the features
// available at that point
const features = getFeatures()
if (to.matched.some(m => m.meta.requireFeature && !features.includes(m.meta.requireFeature))) {
const groupId = datastore.getters['currentGroup/id']
if (groupId) {
await router.push({ name: 'group', params: { groupId } })
}
else {
await router.push({ path: '/' })
}
}
})

datastore.watch((state, getters) => [
Expand Down
4 changes: 3 additions & 1 deletion src/base/icons/icons.json
Expand Up @@ -3,5 +3,7 @@
"pickup_fw": "fas fa-fw fa-shopping-basket",
"feedback": "fas fa-balance-scale",
"place": "fas fa-shopping-cart",
"place_fw": "fas fa-fw fa-shopping-cart"
"place_fw": "fas fa-fw fa-shopping-cart",
"offer": "fas fa-gift",
"offer_fw": "fas fa-fw fa-gift"
}

0 comments on commit 0a4a890

Please sign in to comment.