Skip to content

Commit a8ff2b0

Browse files
authored
Merge pull request #751 from Adamant-im/hotfix/cannot-upload-mp4-to-ipfs
File attachments issues
2 parents 2a329be + 4ec5b88 commit a8ff2b0

File tree

9 files changed

+110
-16
lines changed

9 files changed

+110
-16
lines changed

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ android {
77
applicationId "im.adamant.adamantmessengerpwa"
88
minSdkVersion rootProject.ext.minSdkVersion
99
targetSdkVersion rootProject.ext.targetSdkVersion
10-
versionCode 490
11-
versionName "4.9.0"
10+
versionCode 491
11+
versionName "4.9.1"
1212
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1313
aaptOptions {
1414
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

android/app/capacitor.build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ android {
99

1010
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
1111
dependencies {
12-
12+
implementation project(':capacitor-community-file-opener')
13+
implementation project(':capacitor-filesystem')
1314

1415
}
1516

android/capacitor.settings.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
22
include ':capacitor-android'
33
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
4+
5+
include ':capacitor-community-file-opener'
6+
project(':capacitor-community-file-opener').projectDir = new File('../node_modules/@capacitor-community/file-opener/android')
7+
8+
include ':capacitor-filesystem'
9+
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')

package-lock.json

Lines changed: 26 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "adamant-im",
3-
"version": "4.9.0",
3+
"version": "4.9.1",
44
"author": "ADAMANT Foundation <[email protected]>",
55
"license": "GPLv3",
66
"description": "Decentralized Messenger",
@@ -35,8 +35,10 @@
3535
"postinstall": "electron-builder install-app-deps && npm run schema:generate"
3636
},
3737
"dependencies": {
38+
"@capacitor-community/file-opener": "^6.0.1",
3839
"@capacitor/android": "^6.1.2",
3940
"@capacitor/core": "^6.1.2",
41+
"@capacitor/filesystem": "^6.0.3",
4042
"@emoji-mart/data": "^1.2.1",
4143
"@klayr/codec": "^0.5.1",
4244
"@klayr/cryptography": "^4.1.1",

src/components/AChat/AChatAttachment/AChatImageModal.vue

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ import { ref, computed, onMounted, PropType } from 'vue'
5858
import { useI18n } from 'vue-i18n'
5959
import { useStore } from 'vuex'
6060
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
61+
import { Capacitor } from '@capacitor/core'
62+
import { Directory, Filesystem, WriteFileResult } from '@capacitor/filesystem'
63+
import { FileOpener } from '@capacitor-community/file-opener'
6164
6265
import AChatImageModalItem from './AChatImageModalItem.vue'
6366
import AChatModalFile from './AChatModalFile.vue'
@@ -69,7 +72,36 @@ function delay(ms: number) {
6972
return new Promise((resolve) => setTimeout(resolve, ms))
7073
}
7174
72-
function downloadFileByUrl(url: string, filename = 'unnamed') {
75+
async function downloadFileNatively(blobUrl: string, filename: string): Promise<WriteFileResult> {
76+
const response = await fetch(blobUrl)
77+
const blob = await response.blob()
78+
79+
return new Promise((resolve, reject) => {
80+
const reader = new FileReader()
81+
reader.readAsDataURL(blob)
82+
83+
reader.onloadend = async () => {
84+
const base64Data = reader.result?.toString().split(',')[1]
85+
86+
Filesystem.writeFile({
87+
path: filename,
88+
data: base64Data!,
89+
directory: Directory.Documents
90+
})
91+
.then(resolve)
92+
.catch(reject)
93+
}
94+
})
95+
}
96+
97+
async function downloadFileByUrl(url: string, filename = 'unnamed') {
98+
if (Capacitor.isNativePlatform()) {
99+
const fileResult = await downloadFileNatively(url, filename)
100+
await FileOpener.open({ filePath: fileResult.uri })
101+
102+
return
103+
}
104+
73105
const anchor = document.createElement('a')
74106
anchor.href = url
75107
anchor.download = filename
@@ -202,7 +234,7 @@ export default {
202234
const fileName = file.name
203235
? `${file.name}${file.extension ? '.' + file.extension : ''}`
204236
: undefined
205-
downloadFileByUrl(imageUrl, fileName)
237+
await downloadFileByUrl(imageUrl, fileName)
206238
} catch {
207239
void store.dispatch('snackbar/show', {
208240
message: t('chats.file_not_found')

src/lib/adamant-api/asset.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,29 +200,35 @@ export interface AttachmentAsset {
200200
/**
201201
* AIP-18: https://github.com/Adamant-im/AIPs/pull/54/files
202202
* @param {Array<FileData>} files
203-
* @param {Array<[string, string]>} [nonces] First element is the nonce of original file, second is nonce of preview
204-
* @param {Array<[string, string]>} [ids] List of files IDs after uploading to IPFS. First element is the ID of original file, second is ID of preview.
205-
* @param {string} [comment]
203+
* @param {string} [comment] Optional comment associated with the transaction
204+
* @param {Array<[string, string]>} [cids] List of files IDs after uploading to IPFS. First element is the ID of original file, second is ID of preview.
206205
*/
207-
export function attachmentAsset(files: FileData[], comment?: string): AttachmentAsset {
206+
export function attachmentAsset(
207+
files: FileData[],
208+
comment?: string,
209+
cids?: [string, string]
210+
): AttachmentAsset {
208211
return {
209212
files: files.map(({ file, width, height, cid, preview, encoded }) => {
210213
const name = extractFileName(file.name)
211214
const extension = extractFileExtension(file.name)!
212215
const resolution: FileAsset['resolution'] = width && height ? [width, height] : undefined
213216

217+
const fileCid = cids?.[0] || cid
218+
const previewCid = cids?.[1] || preview?.cid
219+
214220
return {
215221
mimeType: file.type,
216222
name,
217223
extension,
218224
resolution,
219225
duration: undefined, // @todo check if is a video or audio file
220226
size: file.size,
221-
id: cid,
227+
id: fileCid,
222228
nonce: encoded.nonce,
223229
preview: preview
224230
? {
225-
id: preview.cid,
231+
id: previewCid!,
226232
nonce: preview.encoded.nonce,
227233
extension
228234
}

src/lib/chat/helpers/createAttachment.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { attachmentAsset } from '@/lib/adamant-api/asset'
88
type Params = {
99
recipientId: string
1010
senderId: string
11+
recipientPublicKey: string
12+
senderPublicKey: string
1113
files: FileData[]
1214
nonces?: [string, string]
1315
ids?: [string, string]
@@ -25,6 +27,8 @@ export function createAttachment({
2527
files,
2628
message,
2729
replyToId,
30+
recipientPublicKey,
31+
senderPublicKey,
2832
status = TS.PENDING
2933
}: Params) {
3034
const timestamp = Date.now()
@@ -35,6 +39,8 @@ export function createAttachment({
3539
hash: id,
3640
recipientId,
3741
senderId,
42+
recipientPublicKey,
43+
senderPublicKey,
3844
message: message || '',
3945
status,
4046
timestamp: Date.now(),

src/store/modules/chat/index.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getPublicKey } from '@/lib/adamant-api'
12
import validateAddress from '@/lib/validateAddress'
23
import * as admApi from '@/lib/adamant-api'
34
import {
@@ -613,7 +614,6 @@ const actions = {
613614
const normalizedMessages = messages.map(normalizeMessage)
614615
dispatch('botCommands/reInitCommands', normalizedMessages, { root: true })
615616
normalizedMessages.forEach((message) => {
616-
617617
const { recipientId, senderId } = message
618618

619619
if (recipientId === rootState.address || senderId === rootState.address) {
@@ -774,10 +774,15 @@ const actions = {
774774
* @returns {Promise}
775775
*/
776776
async sendAttachment({ commit, rootState }, { files, message, recipientId, replyToId }) {
777+
const recipientPublicKey = await getPublicKey(recipientId)
778+
const senderPublicKey = await getPublicKey(rootState.address)
779+
777780
let messageObject = createAttachment({
778781
message,
779782
recipientId,
780783
senderId: rootState.address,
784+
recipientPublicKey,
785+
senderPublicKey,
781786
files,
782787
replyToId
783788
})
@@ -789,7 +794,7 @@ const actions = {
789794
})
790795

791796
const cids = files.map((file) => [file.cid, file.preview?.cid]).filter((cid) => !!cid)
792-
const newAsset = replyToId
797+
let newAsset = replyToId
793798
? { replyto_id: replyToId, reply_message: attachmentAsset(files, message) }
794799
: attachmentAsset(files, message)
795800
commit('updateMessage', {
@@ -806,6 +811,18 @@ const actions = {
806811
}
807812
})
808813
console.debug('Files uploaded', uploadData)
814+
815+
// Heisenbug: After uploading an MP4 file, the CID returned by the IPFS node differs from the locally computed one.
816+
// So we update the CIDs one more time, just to be sure.
817+
newAsset = replyToId
818+
? { replyto_id: replyToId, reply_message: attachmentAsset(files, message, uploadData.cids) }
819+
: attachmentAsset(files, message, uploadData.cids)
820+
commit('updateMessage', {
821+
id: messageObject.id,
822+
partnerId: recipientId,
823+
asset: newAsset
824+
})
825+
console.debug('Updated CIDs after upload', newAsset)
809826
} catch (err) {
810827
commit('updateMessage', {
811828
id: messageObject.id,

0 commit comments

Comments
 (0)