From 6db796e574393126d3f6fbc8fcc37085b6de168a Mon Sep 17 00:00:00 2001 From: Steven Vandevelde Date: Thu, 9 Feb 2023 18:46:27 +0100 Subject: [PATCH 1/3] WIP --- package-lock.json | 14 +-- package.json | 2 +- src/Applications/Brain/User/Hypaethral.elm | 25 ++++++ src/Applications/Brain/User/State.elm | 57 ++++++++----- src/Applications/UI.elm | 3 + src/Applications/UI/Playlists/State.elm | 37 +++++--- src/Applications/UI/Playlists/View.elm | 3 +- src/Applications/UI/Types.elm | 1 + src/Javascript/Brain/common.ts | 6 +- src/Javascript/Brain/user.ts | 99 +++++++++++++++++++--- src/Javascript/common.js | 5 +- src/Library/User/Layer.elm | 4 +- 12 files changed, 196 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index d31a472f..92dec2b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "subworkers": "^1.0.1", "timer.js": "^1.0.4", "tocca": "^2.0.9", - "webnative": "^0.35.0" + "webnative": "^0.36.2" }, "devDependencies": { "assert": "^2.0.0", @@ -5046,9 +5046,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webnative": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/webnative/-/webnative-0.35.2.tgz", - "integrity": "sha512-2TIK8Amt2M0STz3DFpxSgbujzhHjrwqaDgyTvIghzg6qGW9eHS73KwAC4ErjshFNlOPT7w6z1mVoIB9x3AUSJA==", + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/webnative/-/webnative-0.36.2.tgz", + "integrity": "sha512-WrFT/BEEro+QC21gZuegBI3bOR1x0j4AxZTWHa8fC7nhMi1rdc/KuAuPV0yKB7T4xlB8j00N44B+pdfa/h4Rww==", "dependencies": { "@ipld/dag-cbor": "^8.0.0", "@ipld/dag-pb": "^3.0.1", @@ -8773,9 +8773,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webnative": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/webnative/-/webnative-0.35.2.tgz", - "integrity": "sha512-2TIK8Amt2M0STz3DFpxSgbujzhHjrwqaDgyTvIghzg6qGW9eHS73KwAC4ErjshFNlOPT7w6z1mVoIB9x3AUSJA==", + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/webnative/-/webnative-0.36.2.tgz", + "integrity": "sha512-WrFT/BEEro+QC21gZuegBI3bOR1x0j4AxZTWHa8fC7nhMi1rdc/KuAuPV0yKB7T4xlB8j00N44B+pdfa/h4Rww==", "requires": { "@ipld/dag-cbor": "^8.0.0", "@ipld/dag-pb": "^3.0.1", diff --git a/package.json b/package.json index 0968a865..07fc61a0 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,6 @@ "subworkers": "^1.0.1", "timer.js": "^1.0.4", "tocca": "^2.0.9", - "webnative": "^0.35.0" + "webnative": "^0.36.2" } } diff --git a/src/Applications/Brain/User/Hypaethral.elm b/src/Applications/Brain/User/Hypaethral.elm index 290d2d13..1f81cb60 100644 --- a/src/Applications/Brain/User/Hypaethral.elm +++ b/src/Applications/Brain/User/Hypaethral.elm @@ -29,7 +29,12 @@ retrieveDropbox accessToken bit = retrieveFission : HypaethralBit -> Task String (Maybe Json.Decode.Value) retrieveFission bit = + let + includePublicData = + bit == Playlists + in [ ( "fileName", fileName bit ) + , ( "includePublicData", Json.Encode.bool includePublicData ) ] |> TaskPort.call { function = "fromFission" @@ -95,8 +100,16 @@ saveDropbox accessToken bit data = saveFission : HypaethralBit -> Json.Decode.Value -> Task String () saveFission bit data = + let + _ = + Debug.log "saveFission" bit + + savePublicData = + bit == Playlists + in [ ( "fileName", fileName bit ) , ( "data", data ) + , ( "savePublicData", Json.Encode.bool savePublicData ) ] |> TaskPort.call { function = "toFission" @@ -137,10 +150,22 @@ saveRemoteStorage { token, userAddress } bit data = saveLocal : HypaethralBit -> Json.Decode.Value -> Task String () saveLocal bit data = + let + _ = + Debug.log "saveLocal" bit + in data |> Brain.Task.Ports.toCacheWithSuffix Alien.SyncLocal (hypaethralBitFileName bit) + |> Task.andThen + (\_ -> + let + _ = + Debug.log "saveLocal succeeded" () + in + Task.succeed () + ) |> Task.mapError TaskPort.errorToStringCustom diff --git a/src/Applications/Brain/User/State.elm b/src/Applications/Brain/User/State.elm index 9d7df0dd..c54d5570 100644 --- a/src/Applications/Brain/User/State.elm +++ b/src/Applications/Brain/User/State.elm @@ -418,19 +418,27 @@ saveAllHypaethralDataTask userData method = saveHypaethralDataBitsTask : List HypaethralBit -> HypaethralData -> (HypaethralBit -> Json.Value -> Task String ()) -> Task String () saveHypaethralDataBitsTask bits userData saveFn = - bits - |> List.map - (\bit -> - let - value = - encodeHypaethralBit bit userData - in - Task.andThen - (\_ -> saveFn bit value) - (Hypaethral.saveLocal bit value) - ) - |> Task.sequence - |> Task.map (always ()) + [ -------- + -- LOCAL + -------- + List.map + (\bit -> + Hypaethral.saveLocal bit (encodeHypaethralBit bit userData) + ) + bits + , --------- + -- REMOTE + --------- + List.map + (\bit -> + saveFn bit (encodeHypaethralBit bit userData) + ) + bits + ] + |> List.concat + |> List.foldl + (\nextTask -> Task.andThen (\_ -> nextTask)) + (Task.succeed ()) {-| Save different parts of hypaethral data, @@ -452,8 +460,14 @@ saveHypaethralDataBits bitsWithoutModifiedAt model = { model | hypaethralUserData = updatedUserData } save saveFn = - saveFn - |> saveHypaethralDataBitsTask bits updatedUserData + Time.now + |> Task.andThen + (\currentTime -> + saveHypaethralDataBitsTask + bits + { updatedUserData | modifiedAt = Just currentTime } + saveFn + ) |> Common.attemptTask (always Brain.Bypass) |> return updatedModel in @@ -470,7 +484,7 @@ saveHypaethralDataBits bitsWithoutModifiedAt model = model.currentTime (SaveHypaethralDataBits bits) (Task.succeed ()) - |> return updatedModel + |> return model else save (Hypaethral.saveDropbox accessToken) @@ -507,6 +521,7 @@ saveHypaethralDataSlowly debouncerMsg model = |> Maybe.withDefault [] |> EverySet.fromList |> EverySet.toList + |> Debug.log "Save bits" in c |> Cmd.map (SaveHypaethralDataSlowly >> UserMsg) @@ -649,15 +664,16 @@ removeEncryptionKey model = Alien.SecretKey |> Brain.Task.Ports.removeCache |> Task.mapError TaskPort.errorToStringCustom + |> Task.andThen (\_ -> Time.now) |> Task.andThen - (\_ -> + (\currentTime -> case model.userSyncMethod of Just method -> let data = model.hypaethralUserData in - saveAllHypaethralDataTask { data | modifiedAt = Just model.currentTime } method + saveAllHypaethralDataTask { data | modifiedAt = Just currentTime } method Nothing -> Task.succeed () @@ -673,15 +689,16 @@ updateEncryptionKey json model = passphrase |> Brain.Task.Ports.fabricateSecretKey |> Task.mapError TaskPort.errorToStringCustom + |> Task.andThen (\_ -> Time.now) |> Task.andThen - (\_ -> + (\currentTime -> case model.userSyncMethod of Just method -> let data = model.hypaethralUserData in - saveAllHypaethralDataTask { data | modifiedAt = Just model.currentTime } method + saveAllHypaethralDataTask { data | modifiedAt = Just currentTime } method Nothing -> Task.succeed () diff --git a/src/Applications/UI.elm b/src/Applications/UI.elm index 42fb036c..560bf95f 100644 --- a/src/Applications/UI.elm +++ b/src/Applications/UI.elm @@ -433,6 +433,9 @@ update msg = ShowPlaylistListMenu a b -> Playlists.showListMenu a b + TogglePlaylistVisibility a -> + Playlists.toggleVisibility a + ----------------------------------------- -- Routing ----------------------------------------- diff --git a/src/Applications/UI/Playlists/State.elm b/src/Applications/UI/Playlists/State.elm index f5665fa8..54ff30c3 100644 --- a/src/Applications/UI/Playlists/State.elm +++ b/src/Applications/UI/Playlists/State.elm @@ -79,7 +79,7 @@ addTracksToPlaylist { playlistName, tracks } model = |> (\s -> s ++ " to the __" ++ properPlaylistName ++ "__ playlist") |> Notifications.success |> Common.showNotificationWithModel newModel - |> andThen save + |> andThen User.savePlaylists assistWithAddingTracksToPlaylist : List IdentifiedTrack -> Manager @@ -125,7 +125,7 @@ create model = , newPlaylistContext = Nothing , playlists = playlist :: model.playlists } - |> save + |> User.savePlaylists |> andThen redirectToPlaylistIndexPage Nothing -> @@ -180,7 +180,7 @@ delete { playlistName } model = else Return.singleton ) - |> andThen save + |> andThen User.savePlaylists modify : Manager @@ -219,7 +219,7 @@ modify model = , lastModifiedPlaylist = Just properName , playlists = newCollection ++ autoGenerated } - |> save + |> User.savePlaylists |> andThen redirectToPlaylistIndexPage else @@ -284,16 +284,6 @@ removeTracks playlist tracks model = |> andThen User.savePlaylists -save : Manager -save model = - model.playlists - |> List.filterNot .autoGenerated - |> Json.Encode.list Playlists.encode - |> Alien.broadcast Alien.SavePlaylists - |> Ports.toBrain - |> return model - - select : Playlist -> Manager select playlist model = { model | page = Page.Index, selectedPlaylist = Just playlist } @@ -333,6 +323,25 @@ showListMenu playlist mouseEvent model = Return.singleton { model | contextMenu = Just contextMenu } +toggleVisibility : Playlist -> Manager +toggleVisibility playlist model = + let + updatedPlaylist = + { playlist | public = not playlist.public } + in + model.playlists + |> List.map + (\p -> + if p.name == playlist.name then + updatedPlaylist + + else + p + ) + |> (\c -> { model | playlists = c }) + |> User.savePlaylists + + -- ㊙️ diff --git a/src/Applications/UI/Playlists/View.elm b/src/Applications/UI/Playlists/View.elm index 89be6c5d..e86c61f4 100644 --- a/src/Applications/UI/Playlists/View.elm +++ b/src/Applications/UI/Playlists/View.elm @@ -79,7 +79,8 @@ index playlists selectedPlaylist bgColor authMethodSupportsPublicData = else Icons.public_off - , msg = Nothing + , msg = + Just (\_ -> TogglePlaylistVisibility playlist) , title = if playlist.public then "Make private" diff --git a/src/Applications/UI/Types.elm b/src/Applications/UI/Types.elm index 74c4723a..9d3af297 100644 --- a/src/Applications/UI/Types.elm +++ b/src/Applications/UI/Types.elm @@ -261,6 +261,7 @@ type Msg | SetPlaylistCreationContext String | SetPlaylistModificationContext String String | ShowPlaylistListMenu Playlist Mouse.Event + | TogglePlaylistVisibility Playlist ----------------------------------------- -- Routing ----------------------------------------- diff --git a/src/Javascript/Brain/common.ts b/src/Javascript/Brain/common.ts index 37c563ae..a39d3928 100644 --- a/src/Javascript/Brain/common.ts +++ b/src/Javascript/Brain/common.ts @@ -68,7 +68,11 @@ export function fromCache(key) { export function toCache(key, data) { - return db.setInIndex({ key: key, data: data }) + console.log("toCache", key) + return db.setInIndex({ key: key, data: data }).then(result => { + console.log("toCache succeeded", key) + return result + }) } diff --git a/src/Javascript/Brain/user.ts b/src/Javascript/Brain/user.ts index ed01f4d8..b35e1905 100644 --- a/src/Javascript/Brain/user.ts +++ b/src/Javascript/Brain/user.ts @@ -12,7 +12,7 @@ import { APP_INFO, WEBNATIVE_CONFIG } from "../common" import * as crypto from "../crypto" import { decryptIfNeeded, encryptIfPossible, SECRET_KEY_LOCATION } from "./common" -import { parseJsonIfNeeded, toCache } from "./common" +import { parseJsonIfNeeded, removeCache, toCache } from "./common" const ports: Record = {} @@ -74,29 +74,99 @@ taskPorts.toDropbox = async ({ fileName, data, token }) => { let session, wn -taskPorts.fromFission = async ({ fileName }) => { +taskPorts.fromFission = async ({ fileName, includePublicData }) => { await constructFission() - const path = wn.path.appData(APP_INFO, wn.path.file(fileName)) - - return await session.fs.exists(path) - ? session.fs.read(path) + // Private data + const privatePath = wn.path.appData(APP_INFO, wn.path.file(fileName)) + const privateData = await session.fs.exists(privatePath) + ? session.fs.read(privatePath) .then(bytes => new TextDecoder().decode(bytes)) .then(parseJsonIfNeeded) : null + + // If public data and working with arrays + if (includePublicData && Array.isArray(privateData)) { + const publicPath = { + file: privatePath.file.map((a: string, idx: number) => { + return idx === 0 + ? "public" + : a + }) + } + + const publicData = await session.fs.exists(publicPath) + ? session.fs.read(publicPath) + .then(bytes => new TextDecoder().decode(bytes)) + .then(parseJsonIfNeeded) + : null + + return publicData + ? [ ...privateData, ...publicData ] + : privateData + + // Otherwise + } else { + return privateData + + } } -taskPorts.toFission = async ({ data, fileName }) => { +taskPorts.toFission = async ({ data, fileName, savePublicData }) => { + console.log("toFission", fileName, data) await constructFission() - const json = JSON.stringify(data) + // Data identifying + const privatePath = wn.path.appData(APP_INFO, wn.path.file(fileName)) + const isDataObject = typeof data === "object" && !!data.data + + if (!isDataObject) { + await session.fs.write( + privatePath, + new TextEncoder().encode(JSON.stringify(data)) + ) + await session.fs.publish() + + return + } + + // Group data + const [ privateData, publicData ] = Array.isArray(data.data) && savePublicData + ? data.data.reduce( + ([ priv, pub ], item) => { + return item.public + ? [ priv, [ ...pub, item ] ] + : [ [ ...priv, item ], pub ] + }, + [ [], [] ] + ) + : [ data.data, null ] + + // Private data await session.fs.write( - wn.path.appData(APP_INFO, wn.path.file(fileName)), - new TextEncoder().encode(json) + privatePath, + new TextEncoder().encode(JSON.stringify({ ...data, data: privateData })) ) + // Public data + if (publicData) { + const publicPath = { + file: privatePath.file.map((a: string, idx: number) => { + return idx === 0 + ? "public" + : a + }) + } + + await session.fs.write( + publicPath, + new TextEncoder().encode(JSON.stringify({ ...data, data: publicData })) + ) + } + + // Publish await session.fs.publish() } @@ -114,9 +184,14 @@ async function constructFission() { }) session = program.session - session.fs = await program.loadFileSystem(session.username) - if (!session) throw new Error("Failed to load Webnative session") + if (!session) { + await removeCache("SYNC_METHOD") + location.reload() + throw new Error("Failed to load Webnative session") + } + + session.fs = await program.fileSystem.load(session.username) if (!session.fs) throw new Error("Did not load Webnative file system") } diff --git a/src/Javascript/common.js b/src/Javascript/common.js index fe8b94da..8be91aa3 100644 --- a/src/Javascript/common.js +++ b/src/Javascript/common.js @@ -11,7 +11,10 @@ export const APP_INFO = { export const WEBNATIVE_CONFIG = { namespace: APP_INFO, - permissions: { app: APP_INFO }, + permissions: { + app: APP_INFO, + fs: { public: [ { directory: [ "Apps", APP_INFO.creator, APP_INFO.name ] } ] } + }, debug: true, } diff --git a/src/Library/User/Layer.elm b/src/Library/User/Layer.elm index 3bbbb8fc..3cb973ed 100644 --- a/src/Library/User/Layer.elm +++ b/src/Library/User/Layer.elm @@ -227,9 +227,7 @@ methodSupportsPublicData method = False Fission _ -> - -- NOTE: Temporarily disabled, - -- since we don't actually support public playlists yet. - False + True Ipfs _ -> False -- 2.51.2 From aab0fcd671298d270f42d584b8e8111c66eba963 Mon Sep 17 00:00:00 2001 From: Steven Vandevelde Date: Tue, 14 Feb 2023 13:25:07 +0100 Subject: [PATCH 2/3] WIP --- src/Applications/Brain/User/Hypaethral.elm | 15 --------------- src/Applications/Brain/User/State.elm | 1 - 2 files changed, 16 deletions(-) diff --git a/src/Applications/Brain/User/Hypaethral.elm b/src/Applications/Brain/User/Hypaethral.elm index 1f81cb60..178dc057 100644 --- a/src/Applications/Brain/User/Hypaethral.elm +++ b/src/Applications/Brain/User/Hypaethral.elm @@ -101,9 +101,6 @@ saveDropbox accessToken bit data = saveFission : HypaethralBit -> Json.Decode.Value -> Task String () saveFission bit data = let - _ = - Debug.log "saveFission" bit - savePublicData = bit == Playlists in @@ -150,22 +147,10 @@ saveRemoteStorage { token, userAddress } bit data = saveLocal : HypaethralBit -> Json.Decode.Value -> Task String () saveLocal bit data = - let - _ = - Debug.log "saveLocal" bit - in data |> Brain.Task.Ports.toCacheWithSuffix Alien.SyncLocal (hypaethralBitFileName bit) - |> Task.andThen - (\_ -> - let - _ = - Debug.log "saveLocal succeeded" () - in - Task.succeed () - ) |> Task.mapError TaskPort.errorToStringCustom diff --git a/src/Applications/Brain/User/State.elm b/src/Applications/Brain/User/State.elm index c54d5570..26e7aa4b 100644 --- a/src/Applications/Brain/User/State.elm +++ b/src/Applications/Brain/User/State.elm @@ -521,7 +521,6 @@ saveHypaethralDataSlowly debouncerMsg model = |> Maybe.withDefault [] |> EverySet.fromList |> EverySet.toList - |> Debug.log "Save bits" in c |> Cmd.map (SaveHypaethralDataSlowly >> UserMsg) -- 2.51.2 From 03cf72352576dc07f00325055460b7110b16fffa Mon Sep 17 00:00:00 2001 From: Steven Vandevelde Date: Tue, 14 Feb 2023 13:26:20 +0100 Subject: [PATCH 3/3] WIP --- src/Javascript/Brain/common.ts | 2 -- src/Javascript/Brain/user.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/src/Javascript/Brain/common.ts b/src/Javascript/Brain/common.ts index a39d3928..23854c7e 100644 --- a/src/Javascript/Brain/common.ts +++ b/src/Javascript/Brain/common.ts @@ -68,9 +68,7 @@ export function fromCache(key) { export function toCache(key, data) { - console.log("toCache", key) return db.setInIndex({ key: key, data: data }).then(result => { - console.log("toCache succeeded", key) return result }) } diff --git a/src/Javascript/Brain/user.ts b/src/Javascript/Brain/user.ts index b35e1905..38e30466 100644 --- a/src/Javascript/Brain/user.ts +++ b/src/Javascript/Brain/user.ts @@ -114,7 +114,6 @@ taskPorts.fromFission = async ({ fileName, includePublicData }) => { taskPorts.toFission = async ({ data, fileName, savePublicData }) => { - console.log("toFission", fileName, data) await constructFission() // Data identifying -- 2.51.2