From b0c7cb88035b5ca723de2a5a08cd2840214d9b97 Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Mon, 5 Mar 2018 08:44:37 +0900 Subject: [PATCH] wip --- src/api/endpoints/posts/create.ts | 13 ++++++ src/api/models/post.ts | 9 ++++ .../views/components/post-form-window.vue | 13 +++++- .../desktop/views/components/post-form.vue | 21 ++++++++- .../app/mobile/views/components/post-form.vue | 24 +++++++++-- src/web/docs/api/entities/post.yaml | 43 +++++++++++++++++++ 6 files changed, 117 insertions(+), 6 deletions(-) diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index a9d52fd128..15cbc4845c 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -39,6 +39,18 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [tags = [], tagsErr] = $(params.tags).optional.array('string').unique().eachQ(t => t.range(1, 32)).$; if (tagsErr) return rej('invalid tags'); + // Get 'geo' parameter + const [geo, geoErr] = $(params.geo).optional.nullable.strict.object() + .have('latitude', $().number().range(-180, 180)) + .have('longitude', $().number().range(-90, 90)) + .have('altitude', $().nullable.number()) + .have('accuracy', $().nullable.number()) + .have('altitudeAccuracy', $().nullable.number()) + .have('heading', $().nullable.number().range(0, 360)) + .have('speed', $().nullable.number()) + .$; + if (geoErr) return rej('invalid geo'); + // Get 'media_ids' parameter const [mediaIds, mediaIdsErr] = $(params.media_ids).optional.array('id').unique().range(1, 4).$; if (mediaIdsErr) return rej('invalid media_ids'); @@ -244,6 +256,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { user_id: user._id, app_id: app ? app._id : null, via_mobile: viaMobile, + geo, // 以下非正規化データ _reply: reply ? { user_id: reply.user_id } : undefined, diff --git a/src/api/models/post.ts b/src/api/models/post.ts index edb69e0c15..c37c8371c0 100644 --- a/src/api/models/post.ts +++ b/src/api/models/post.ts @@ -32,6 +32,15 @@ export type IPost = { category: string; is_category_verified: boolean; via_mobile: boolean; + geo: { + latitude: number; + longitude: number; + altitude: number; + accuracy: number; + altitudeAccuracy: number; + heading: number; + speed: number; + }; }; /** diff --git a/src/web/app/desktop/views/components/post-form-window.vue b/src/web/app/desktop/views/components/post-form-window.vue index 4427f59829..31a07a890e 100644 --- a/src/web/app/desktop/views/components/post-form-window.vue +++ b/src/web/app/desktop/views/components/post-form-window.vue @@ -1,6 +1,7 @@ <template> <mk-window ref="window" is-modal @closed="$destroy"> <span slot="header"> + <span :class="$style.icon" v-if="geo">%fa:map-marker-alt%</span> <span v-if="!reply">%i18n:desktop.tags.mk-post-form-window.post%</span> <span v-if="reply">%i18n:desktop.tags.mk-post-form-window.reply%</span> <span :class="$style.count" v-if="media.length != 0">{{ '%i18n:desktop.tags.mk-post-form-window.attaches%'.replace('{}', media.length) }}</span> @@ -12,7 +13,8 @@ :reply="reply" @posted="onPosted" @change-uploadings="onChangeUploadings" - @change-attached-media="onChangeMedia"/> + @change-attached-media="onChangeMedia" + @geo-attached="onGeoAttached"/> </mk-window> </template> @@ -24,7 +26,8 @@ export default Vue.extend({ data() { return { uploadings: [], - media: [] + media: [], + geo: null }; }, mounted() { @@ -39,6 +42,9 @@ export default Vue.extend({ onChangeMedia(media) { this.media = media; }, + onGeoAttached(geo) { + this.geo = geo; + }, onPosted() { (this.$refs.window as any).close(); } @@ -47,6 +53,9 @@ export default Vue.extend({ </script> <style lang="stylus" module> +.icon + margin-right 8px + .count margin-left 8px opacity 0.8 diff --git a/src/web/app/desktop/views/components/post-form.vue b/src/web/app/desktop/views/components/post-form.vue index 5cf5cffc6a..6e334e7ca8 100644 --- a/src/web/app/desktop/views/components/post-form.vue +++ b/src/web/app/desktop/views/components/post-form.vue @@ -27,6 +27,7 @@ <button class="drive" title="%i18n:desktop.tags.mk-post-form.attach-media-from-drive%" @click="chooseFileFromDrive">%fa:cloud%</button> <button class="kao" title="%i18n:desktop.tags.mk-post-form.insert-a-kao%" @click="kao">%fa:R smile%</button> <button class="poll" title="%i18n:desktop.tags.mk-post-form.create-poll%" @click="poll = true">%fa:chart-pie%</button> + <button class="geo" title="位置情報を添付する" @click="setGeo">%fa:map-marker-alt%</button> <p class="text-count" :class="{ over: text.length > 1000 }">{{ '%i18n:desktop.tags.mk-post-form.text-remain%'.replace('{}', 1000 - text.length) }}</p> <button :class="{ posting }" class="submit" :disabled="!canPost" @click="post"> {{ posting ? '%i18n:desktop.tags.mk-post-form.posting%' : submitText }}<mk-ellipsis v-if="posting"/> @@ -53,6 +54,7 @@ export default Vue.extend({ files: [], uploadings: [], poll: false, + geo: null, autocomplete: null, draghover: false }; @@ -193,6 +195,21 @@ export default Vue.extend({ } //#endregion }, + setGeo() { + if (navigator.geolocation == null) { + alert('お使いの端末は位置情報に対応していません'); + return; + } + + navigator.geolocation.getCurrentPosition(pos => { + this.geo = pos.coords; + this.$emit('geo-attached', this.geo); + }, err => { + alert('エラー: ' + err.message); + }, { + enableHighAccuracy: true + }); + }, post() { this.posting = true; @@ -201,7 +218,8 @@ export default Vue.extend({ media_ids: this.files.length > 0 ? this.files.map(f => f.id) : undefined, reply_id: this.reply ? this.reply.id : undefined, repost_id: this.repost ? this.repost.id : undefined, - poll: this.poll ? (this.$refs.poll as any).get() : undefined + poll: this.poll ? (this.$refs.poll as any).get() : undefined, + geo: this.geo, }).then(data => { this.clear(); this.deleteDraft(); @@ -459,6 +477,7 @@ export default Vue.extend({ > .drive > .kao > .poll + > .geo display inline-block cursor pointer padding 0 diff --git a/src/web/app/mobile/views/components/post-form.vue b/src/web/app/mobile/views/components/post-form.vue index d16d5d358b..559a6c1c4b 100644 --- a/src/web/app/mobile/views/components/post-form.vue +++ b/src/web/app/mobile/views/components/post-form.vue @@ -23,6 +23,7 @@ <button class="drive" @click="chooseFileFromDrive">%fa:cloud%</button> <button class="kao" @click="kao">%fa:R smile%</button> <button class="poll" @click="poll = true">%fa:chart-pie%</button> + <button class="geo" @click="setGeo">%fa:map-marker-alt%</button> <input ref="file" class="file" type="file" accept="image/*" multiple="multiple" @change="onChangeFile"/> </div> </div> @@ -44,7 +45,8 @@ export default Vue.extend({ text: '', uploadings: [], files: [], - poll: false + poll: false, + geo: null }; }, mounted() { @@ -83,6 +85,20 @@ export default Vue.extend({ onChangeUploadings(uploads) { this.$emit('change-uploadings', uploads); }, + setGeo() { + if (navigator.geolocation == null) { + alert('お使いの端末は位置情報に対応していません'); + return; + } + + navigator.geolocation.getCurrentPosition(pos => { + this.geo = pos.coords; + }, err => { + alert('エラー: ' + err.message); + }, { + enableHighAccuracy: true + }); + }, clear() { this.text = ''; this.files = []; @@ -97,6 +113,7 @@ export default Vue.extend({ media_ids: this.files.length > 0 ? this.files.map(f => f.id) : undefined, reply_id: this.reply ? this.reply.id : undefined, poll: this.poll ? (this.$refs.poll as any).get() : undefined, + geo: this.geo, via_mobile: viaMobile }).then(data => { this.$emit('post'); @@ -223,8 +240,9 @@ export default Vue.extend({ > .upload > .drive - .kao - .poll + > .kao + > .poll + > .geo display inline-block padding 0 margin 0 diff --git a/src/web/docs/api/entities/post.yaml b/src/web/docs/api/entities/post.yaml index e4359ffd0c..451d2579d9 100644 --- a/src/web/docs/api/entities/post.yaml +++ b/src/web/docs/api/entities/post.yaml @@ -128,3 +128,46 @@ props: desc: ja: "この選択肢に投票された数" en: "The number voted for this choice" + - name: "geo" + type: "object" + optional: true + desc: + ja: "位置情報" + en: "Geo location" + defName: "geo" + def: + - name: "latitude" + type: "number" + optional: false + desc: + ja: "緯度。-180〜180で表す。" + - name: "longitude" + type: "number" + optional: false + desc: + ja: "経度。-90〜90で表す。" + - name: "altitude" + type: "number" + optional: false + desc: + ja: "高度。メートル単位で表す。" + - name: "accuracy" + type: "number" + optional: false + desc: + ja: "緯度、経度の精度。メートル単位で表す。" + - name: "altitudeAccuracy" + type: "number" + optional: false + desc: + ja: "高度の精度。メートル単位で表す。" + - name: "heading" + type: "number" + optional: false + desc: + ja: "方角。0〜360の角度で表す。0が北、90が東、180が南、270が西。" + - name: "speed" + type: "number" + optional: false + desc: + ja: "速度。メートル / 秒数で表す。"