Skip to content

Commit 6557d9c

Browse files
nbitonfranck-boullier
authored andcommitted
Unit floor plan upload (#871)
* feat: upgrade to Meteor 1.8.1 * feat: refactored cloudinary transformation name and added a new one * feat: upload unit floor plan UI and logic * chore: clean up * feat: disable unit floor plan (store old ones) * feat: allowing all users change floor plan; outside of edit mode * feat: unit membership check for floor plan change * feat: revised design for "save" and "cancel" for editing unit details * feat: changed "unit"->"property" in various labels * fix: lint
1 parent a835513 commit 6557d9c

22 files changed

+465
-82
lines changed

.meteor/packages

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@
66

77
meteor-base@1.4.0 # Packages every Meteor app needs to have
88
mobile-experience@1.0.5 # Packages for a great mobile UX
9-
mongo@1.6.0 # The database Meteor supports right now
9+
mongo@1.6.2 # The database Meteor supports right now
1010
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
1111
reactive-var@1.0.11 # Reactive variable for tracker
1212
tracker@1.2.0 # Meteor's client-side reactive programming library
1313

14-
standard-minifier-js@2.4.0 # JS minifier run for production mode
14+
standard-minifier-js@2.4.1 # JS minifier run for production mode
1515
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers
16-
ecmascript@0.12.3 # Enable ECMAScript2015+ syntax in app code
16+
ecmascript@0.12.4 # Enable ECMAScript2015+ syntax in app code
1717
shell-server@0.4.0 # Server-side component of the `meteor shell` command
1818

1919
react-meteor-data
2020
accounts-password@1.5.1
21-
http@1.4.1
21+
http@1.4.2
2222
juliancwirko:postcss
2323
nathantreid:css-modules@=3.1.4
2424
dburles:factory
@@ -31,4 +31,4 @@ dburles:collection-helpers
3131
percolate:migrations
3232
underscore@1.0.10
3333
email@1.2.3
34-
accounts-base
34+
accounts-base@1.4.3

.meteor/release

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
METEOR@1.8.0.1
1+
METEOR@1.8.1

.meteor/versions

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
accounts-base@1.4.3
22
accounts-password@1.5.1
33
allow-deny@1.1.0
4-
autoupdate@1.5.0
5-
babel-compiler@7.2.3
4+
autoupdate@1.6.0
5+
babel-compiler@7.3.4
66
babel-runtime@1.3.0
77
base64@1.0.11
88
binary-heap@1.0.11
@@ -22,18 +22,18 @@ ddp@1.4.0
2222
ddp-client@2.3.3
2323
ddp-common@1.4.0
2424
ddp-rate-limiter@1.0.7
25-
ddp-server@2.2.0
25+
ddp-server@2.3.0
2626
deps@1.0.12
2727
diff-sequence@1.1.1
2828
dynamic-import@0.5.1
29-
ecmascript@0.12.3
29+
ecmascript@0.12.4
3030
ecmascript-runtime@0.7.0
3131
ecmascript-runtime-client@0.8.0
3232
ecmascript-runtime-server@0.7.1
3333
ejson@1.1.0
3434
email@1.2.3
3535
es5-shim@4.8.0
36-
fetch@0.1.0
36+
fetch@0.1.1
3737
geojson-utils@1.0.10
3838
hasse:tachyons@1.0.0
3939
hot-code-push@1.0.4
@@ -48,36 +48,36 @@ launch-screen@1.1.1
4848
livedata@1.0.18
4949
localstorage@1.2.0
5050
logging@1.1.20
51-
meteor@1.9.2
51+
meteor@1.9.3
5252
meteor-base@1.4.0
53-
minifier-css@1.4.1
54-
minifier-js@2.4.0
53+
minifier-css@1.4.2
54+
minifier-js@2.4.1
5555
minimongo@1.4.5
5656
mobile-experience@1.0.5
5757
mobile-status-bar@1.0.14
58-
modern-browsers@0.1.3
58+
modern-browsers@0.1.4
5959
modules@0.13.0
6060
modules-runtime@0.10.3
61-
mongo@1.6.0
62-
mongo-decimal@0.1.0
61+
mongo@1.6.2
62+
mongo-decimal@0.1.1
6363
mongo-dev-server@1.1.0
6464
mongo-id@1.0.7
6565
nathantreid:css-modules@3.1.4
6666
npm-bcrypt@0.9.3
67-
npm-mongo@3.1.1
67+
npm-mongo@3.1.2
6868
observe-sequence@1.0.16
6969
omega:dirty-chai@0.0.2
7070
ordered-dict@1.1.0
7171
pauldowman:dotenv@1.0.1
7272
percolate:migrations@1.0.2
7373
practicalmeteor:chai@2.1.0_1
7474
practicalmeteor:sinon@1.14.1_2
75-
promise@0.11.1
75+
promise@0.11.2
7676
random@1.1.0
7777
rate-limit@1.0.9
7878
react-meteor-data@0.2.16
7979
reactive-var@1.0.11
80-
reload@1.2.0
80+
reload@1.3.0
8181
retry@1.1.0
8282
routepolicy@1.1.0
8383
service-configuration@1.0.11
@@ -88,7 +88,7 @@ socket-stream-client@0.2.2
8888
spacebars@1.0.15
8989
spacebars-compiler@1.1.3
9090
srp@1.0.12
91-
standard-minifier-js@2.4.0
91+
standard-minifier-js@2.4.1
9292
templating@1.3.2
9393
templating-compiler@1.3.3
9494
templating-runtime@1.3.2
@@ -98,5 +98,5 @@ tracker@1.2.0
9898
ui@1.0.13
9999
underscore@1.0.10
100100
url@1.2.0
101-
webapp@1.7.2
101+
webapp@1.7.3
102102
webapp-hashing@1.0.9

imports/api/unit-meta-data.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Mongo } from 'meteor/mongo'
22
import { Meteor } from 'meteor/meteor'
3+
import randToken from 'rand-token'
4+
import UnitRolesData from './unit-roles-data'
35

46
export const collectionName = 'unitMetaData'
57
export const unitTypes = Object.freeze([
@@ -102,6 +104,51 @@ Meteor.methods({
102104
}
103105

104106
UnitMetaData.update({ _id: id }, { $set: unitFields })
107+
},
108+
[`${collectionName}.updateFloorPlan`] (id, floorPlanUrl) {
109+
const metaData = UnitMetaData.findOne({ _id: id })
110+
if (!metaData) {
111+
throw new Meteor.Error(`No unit found for id ${id}`)
112+
}
113+
114+
const unitRole = UnitRolesData.findOne({
115+
unitId: id,
116+
'members.id': Meteor.userId()
117+
})
118+
if (!unitRole) {
119+
throw new Meteor.Error('You are not one of the members of this unit, so you are not allowed to update the floor plan')
120+
}
121+
122+
UnitMetaData.update({ _id: id }, {
123+
$push: {
124+
floorPlanUrls: {
125+
url: floorPlanUrl,
126+
addedBy: Meteor.userId(),
127+
addedAt: new Date(),
128+
id: randToken.generate(17)
129+
}
130+
}
131+
})
132+
},
133+
[`${collectionName}.disableFloorPlan`] (id) {
134+
const metaData = UnitMetaData.findOne({ _id: id })
135+
if (!metaData) {
136+
throw new Meteor.Error(`No unit found for id ${id}`)
137+
}
138+
139+
const unitRole = UnitRolesData.findOne({
140+
unitId: id,
141+
'members.id': Meteor.userId()
142+
})
143+
if (!unitRole) {
144+
throw new Meteor.Error('You are not one of the members of this unit, so you are not allowed to update the floor plan')
145+
}
146+
147+
UnitMetaData.update({ _id: id }, {
148+
$set: {
149+
[`floorPlanUrls.${metaData.floorPlanUrls.length - 1}.disabled`]: true
150+
}
151+
})
105152
}
106153
})
107154

imports/api/units.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ export const addUserToRole = (
312312

313313
const rolesProjByOwnership = (userId, unitItem) => {
314314
return {
315+
unitId: 1,
315316
unitBzId: 1,
316317
roleType: 1,
317318
members: 1,
@@ -425,7 +426,8 @@ if (Meteor.isServer) {
425426
state: 1,
426427
streetAddress: 1,
427428
zipCode: 1,
428-
city: 1
429+
city: 1,
430+
floorPlanUrls: 1
429431
}),
430432
withRolesData(rolesProjByOwnership, rolesSelectionByOwnership)
431433
],
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// @flow
2+
/* global File */
3+
export const UPLOAD_FLOOR_PLAN = 'upload_unit_floor_plan'
4+
export const UPLOAD_FLOOR_PLAN_STARTED = 'upload_unit_floor_plan_started'
5+
export const UPLOAD_FLOOR_PLAN_PROGRESS = 'upload_unit_floor_plan_progress'
6+
export const UPLOAD_FLOOR_PLAN_ERROR = 'upload_unit_floor_plan_error'
7+
export const UPLOAD_FLOOR_PLAN_COMPLETED = 'upload_unit_floor_plan_completed'
8+
export const CHANGE_FLOOR_PLAN_URL = 'change_unit_floor_plan_url'
9+
export const DISABLE_FLOOR_PLAN = 'disable_unit_floor_plan'
10+
11+
export type UnitFloorPlanInitAction = {
12+
type: string,
13+
preview: string,
14+
file: File,
15+
unitMongoId: string
16+
}
17+
18+
export type UnitFloorPlanProcessAction = {
19+
type: string,
20+
unitMongoId: string,
21+
file?: File,
22+
preview?: string,
23+
percent?: number,
24+
error?: {}
25+
}
26+
27+
export type UnitFloorPlanCompleteAction = {
28+
type: string,
29+
unitMongoId: string,
30+
url: string
31+
}
32+
33+
export type UnitFloorPlanDisableAction = {
34+
type: string,
35+
unitMongoId: string
36+
}
37+
38+
export function uploadFloorPlan (unitMongoId: string, preview: string, file: File): UnitFloorPlanInitAction {
39+
return {
40+
type: UPLOAD_FLOOR_PLAN,
41+
unitMongoId,
42+
preview,
43+
file
44+
}
45+
}
46+
47+
export function uploadFloorPlanStarted (unitMongoId: string, preview: string, file: File): UnitFloorPlanProcessAction {
48+
return {
49+
type: UPLOAD_FLOOR_PLAN_STARTED,
50+
unitMongoId,
51+
preview,
52+
file
53+
}
54+
}
55+
56+
export function uploadFloorPlanProgress (unitMongoId: string, percent: number): UnitFloorPlanProcessAction {
57+
return {
58+
type: UPLOAD_FLOOR_PLAN_PROGRESS,
59+
unitMongoId,
60+
percent
61+
}
62+
}
63+
64+
export function uploadFloorPlanError (unitMongoId: string, error: {}): UnitFloorPlanProcessAction {
65+
return {
66+
type: UPLOAD_FLOOR_PLAN_ERROR,
67+
unitMongoId,
68+
error
69+
}
70+
}
71+
72+
export function uploadFloorPlanCompleted (unitMongoId: string): UnitFloorPlanProcessAction {
73+
return {
74+
type: UPLOAD_FLOOR_PLAN_COMPLETED,
75+
unitMongoId
76+
}
77+
}
78+
79+
export function changeFloorPlanUrl (unitMongoId: string, url: string): UnitFloorPlanCompleteAction {
80+
return {
81+
type: CHANGE_FLOOR_PLAN_URL,
82+
unitMongoId,
83+
url
84+
}
85+
}
86+
87+
export function disableFloorPlan (unitMongoId: string): UnitFloorPlanDisableAction {
88+
return {
89+
type: DISABLE_FLOOR_PLAN,
90+
unitMongoId
91+
}
92+
}

imports/state/epics/base/file-upload-processor.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,26 @@ import 'rxjs/add/operator/bufferTime'
1717
import 'rxjs/add/operator/take'
1818

1919
type Action = { type: string }
20-
export type InputAction = { ...Action, file: File }
21-
type ActionGenerators = {
22-
init: (origAction: InputAction) => Action,
23-
progress: (origAction: InputAction, percent: number) => Action,
24-
error: (origAction: InputAction, error: {}) => Action,
25-
complete: (origAction: InputAction, uploadedUrl: string) => Array<Action> | Action
20+
export type InputAction = Action & { file: File }
21+
type ActionGenerators<T> = {
22+
init: (origAction: T) => Action,
23+
progress: (origAction: T, percent: number) => Action,
24+
error: (origAction: T, error: {}) => Action,
25+
complete: (origAction: T, uploadedUrl: string) => Array<Action> | Action
2626
}
27-
export const fileUploadProcessor = (actionType: string, actionGenerators: ActionGenerators) =>
27+
export const fileUploadProcessor = <T: InputAction>(actionType: string, actionGenerators: ActionGenerators<T>) =>
2828
(action$: Observable, store: {}, deps: { ajax: (opts: {}) => Observable }) => {
2929
const { CLOUDINARY_URL, CLOUDINARY_PRESET } = Meteor.settings.public
3030

3131
// Creating a stream to publish upload progress actions
32-
const buildProgressStream = (action: InputAction) => {
32+
const buildProgressStream = (action: T) => {
3333
return (new Subject())
3434
.filter(evt => evt.lengthComputable)
3535
.map(evt => actionGenerators.progress(action, Math.round(evt.loaded / evt.total * 100)))
3636
}
3737

3838
// Creating a stream to execute the upload and chaining to publish completion, error and "CREATE_COMMENT"
39-
const buildAjaxStream = (action: InputAction, progress$: Observable) => {
39+
const buildAjaxStream = (action: T, progress$: Observable) => {
4040
// Creating the upload payload
4141
const formData = new FormData()
4242
formData.append('file', action.file)
@@ -62,7 +62,7 @@ export const fileUploadProcessor = (actionType: string, actionGenerators: Action
6262
return action$
6363
.ofType(actionType)
6464
.filter(() => !!Meteor.userId()) // fail safe, but shouldn't happen
65-
.mergeMap((action: InputAction) => { // For any matching action
65+
.mergeMap((action: T) => { // For any matching action
6666
const progress$ = buildProgressStream(action)
6767
const ajax$ = buildAjaxStream(action, progress$)
6868

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @flow
2+
import fallibleMethodCaller from './base/fallible-method-caller'
3+
import { CHANGE_FLOOR_PLAN_URL } from '../actions/unit-floor-plan.actions'
4+
import { collectionName } from '../../api/unit-meta-data'
5+
import { genericErrorOccurred } from '../../ui/general-actions'
6+
7+
export const changeUnitFloorPlanUrl = fallibleMethodCaller({
8+
actionType: CHANGE_FLOOR_PLAN_URL,
9+
methodName: `${collectionName}.updateFloorPlan`,
10+
argTranslator: ({ unitMongoId, url }) => [unitMongoId, url],
11+
actionGenerators: {
12+
errorGen: (error, action) => genericErrorOccurred(
13+
`Error occurred while trying to update the floor plan URL for unit ${action.unitMongoId}: ${error.message}`
14+
)
15+
}
16+
})

0 commit comments

Comments
 (0)