diff --git a/CHANGELOG.md b/CHANGELOG.md
index 16391c5c5..9b035f7de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,75 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
This project *loosely* adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). More specifically:
+## [0.1.6] - 24/06/2021
+
+### Added
+
+### Changed
+
+### Deprecated
+
+### Removed
+
+### Fixed
+ - Holochain connection in ad4m being started in async meaning connection would not always occur before trying to call conductor in subsequent init languages calls
+
+### Security
+
+---
+
+## [0.1.5] - 24/06/2021
+
+### Added
+- Push notifications on message received if app not in view!
+- Black theme
+- Border around modals on 90s theme
+
+### Changed
+- Optimised all polling code to use web workers to avoid polluting the main render thread
+
+### Deprecated
+
+### Removed
+
+### Fixed
+ - Various fixes on channel notification icons
+ - Update to new ad4m version to fix concurrent language installation in new holochain version
+
+### Security
+
+---
+
+## [0.1.4] - 22/06/2021
+
+### Added
+- Script to fetch languages on build vs building from src
+- Cyberpunk theme!
+- Global error popup if ad4m or holochain cannot start
+- channel notification icon when live message received during sessions
+- use vue virtual scroll again for our scrolling
+- New messages arriving in channel will prompt UI to give notification to scroll to bottom of view
+- Holochain now needs to be built manually for each dev install with nix vs built in repo binaries
+- 5 channel views are now kept alive at once to allow for fast rendering/switching between them
+
+### Changed
+- Holochain now at latest version @ commit: 8600350687dd80bbc7a5620e8fe71ad55c97eed2
+- Global error now has proper styling
+- Use key/value store vs array for channels/communities/messages in vuex store
+
+### Deprecated
+
+### Removed
+
+### Fixed
+- Bug where old loading/error dialogues would remain in state after app restart
+- Pressing enter would make a new line in the editor
+- Sending a message will now always correctly move to bottom of channel
+
+### Security
+
+---
+
## [0.1.3] - 19/06/2021
### Added
diff --git a/package.json b/package.json
index 38a41fc5a..5c97141d1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "junto",
- "version": "0.1.6",
+ "version": "0.1.7",
"private": true,
"description": "Junto ad4m chat application leveraging distributed & decentralized technologies",
"author": "josh@junto.foundation",
@@ -27,7 +27,7 @@
"@apollo/client": "^3.3.14",
"@junto-foundation/junto-elements": "0.0.13",
"@perspect3vism/ad4m": "0.0.6",
- "@perspect3vism/ad4m-executor": "0.0.16",
+ "@perspect3vism/ad4m-executor": "0.0.17",
"@types/jest": "^26.0.20",
"@types/mocha": "^8.2.1",
"@vue/apollo-composable": "^4.0.0-alpha.12",
diff --git a/scripts/clean-state.command b/scripts/clean-state.command
new file mode 100755
index 000000000..ceda93a47
--- /dev/null
+++ b/scripts/clean-state.command
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+if [[ "$OSTYPE" == "linux-gnu"* ]]; then
+ path="$HOME/.config/junto"
+elif [[ "$OSTYPE" == "darwin"* ]]; then
+ path="$HOME/Library/Application Support/junto"
+fi
+
+echo "Will delete path: $path"
+# read -p "IS THIS CORRECT? (y/n) " answer
+
+# if [[ $answer =~ ^[Yy]$ ]] ;
+# then
+# echo "Accepted, deleting directory"
+rm -rf $path
+# else
+# echo "Not deleting"
+# fi
diff --git a/src/containers/EditProfile.vue b/src/containers/EditProfile.vue
index b82663cfc..cfa81d3e1 100644
--- a/src/containers/EditProfile.vue
+++ b/src/containers/EditProfile.vue
@@ -8,13 +8,13 @@
(username = e.target.value)"
>
Cancel
-
+
Save
@@ -56,10 +56,10 @@ export default defineComponent({
},
},
methods: {
- updateUser() {
+ updateProfile() {
this.isUpdatingProfile = true;
this.$store
- .dispatch("updateUser", {
+ .dispatch("updateProfile", {
username: this.username,
profilePicture: this.profilePicture,
})
diff --git a/src/core/graphql_queries.ts b/src/core/graphql_queries.ts
index a85f587e2..488bf522e 100644
--- a/src/core/graphql_queries.ts
+++ b/src/core/graphql_queries.ts
@@ -96,6 +96,16 @@ export const LANGUAGES = gql`
languages(filter: $filter) {
name
address
+ constructorIcon {
+ code
+ }
+ iconFor {
+ code
+ }
+ settings
+ settingsIcon {
+ code
+ }
}
}
`;
diff --git a/src/core/methods/getTypedExpressionLangs.ts b/src/core/methods/getTypedExpressionLangs.ts
index ccc0dd704..8cb0e78a8 100644
--- a/src/core/methods/getTypedExpressionLangs.ts
+++ b/src/core/methods/getTypedExpressionLangs.ts
@@ -24,6 +24,7 @@ export async function getTypedExpressionLanguages(
languageAddress: lang!,
createIcon: languageRes.constructorIcon!.code!,
viewIcon: languageRes.iconFor!.code!,
+ name: languageRes.name!,
};
store!.commit({
type: "addExpressionUI",
diff --git a/src/core/queries/getLanguages.ts b/src/core/queries/getLanguages.ts
new file mode 100644
index 000000000..0dc429380
--- /dev/null
+++ b/src/core/queries/getLanguages.ts
@@ -0,0 +1,18 @@
+import { apolloClient } from "@/main";
+import { LANGUAGES } from "../graphql_queries";
+import ad4m from "@perspect3vism/ad4m-executor";
+
+export async function getLanguages(): Promise {
+ return new Promise((resolve, reject) => {
+ apolloClient
+ .query<{ languages: ad4m.Language[] }>({
+ query: LANGUAGES,
+ })
+ .then((result) => {
+ resolve(result.data!.languages);
+ })
+ .catch((error) => {
+ reject(error);
+ });
+ });
+}
diff --git a/src/store/actions/createCommunity.ts b/src/store/actions/createCommunity.ts
index e86df7e5a..28793b421 100644
--- a/src/store/actions/createCommunity.ts
+++ b/src/store/actions/createCommunity.ts
@@ -182,6 +182,7 @@ export default async (
languageAddress: lang,
createIcon: languageRes.constructorIcon!.code!,
viewIcon: languageRes.iconFor!.code!,
+ name: languageRes.name!,
};
commit("addExpressionUI", uiData);
await sleep(40);
diff --git a/src/store/actions/getCommunityMembers.ts b/src/store/actions/getCommunityMembers.ts
index 4db0ab9be..19fbe3e77 100644
--- a/src/store/actions/getCommunityMembers.ts
+++ b/src/store/actions/getCommunityMembers.ts
@@ -3,6 +3,7 @@ import { getLinks } from "@/core/queries/getLinks";
import { Commit } from "vuex";
import { ExpressionTypes, State } from "..";
import type Expression from "@perspect3vism/ad4m/Expression";
+import { TimeoutCache } from "../../utils/timeoutCache";
export interface Context {
commit: Commit;
@@ -18,6 +19,7 @@ export default async function (
{ communityId }: Payload
): Promise {
const profiles: { [x: string]: Expression } = {};
+ const cache = new TimeoutCache(1000 * 60 * 5);
try {
const communities = state.communities;
@@ -39,6 +41,7 @@ export default async function (
const did = `${profileLang.languageAddress}://${profileLink.author!
.did!}`;
+ //TODO: we should store the whole profile in the store but just the did and then resolve the profile via cache/network
const profile = await getProfile(
profileLang.languageAddress,
profileLink.author!.did!
@@ -46,6 +49,7 @@ export default async function (
if (profile) {
profiles[did] = Object.assign({}, profile);
+ cache.set(did, profile);
}
}
diff --git a/src/store/actions/index.ts b/src/store/actions/index.ts
index cb381abf1..6e1e0e294 100644
--- a/src/store/actions/index.ts
+++ b/src/store/actions/index.ts
@@ -6,7 +6,7 @@ import logIn from "./logIn";
import getPerspectiveChannelsAndMetadata from "./getPerspectiveChannelsAndMetadata";
import loadExpressionLanguages from "./loadExpressionLanguages";
import updateCommunity from "./updateCommunity";
-import updateUser from "./updateUser";
+import updateProfile from "./updateProfile";
import getCommunityMembers from "./getCommunityMembers";
import loadExpressions from "./loadExpressions";
import createExpression from "./createExpression";
@@ -20,7 +20,7 @@ export default {
getPerspectiveChannelsAndMetadata,
loadExpressionLanguages,
updateCommunity,
- updateUser,
+ updateProfile,
getCommunityMembers,
loadExpressions,
createExpression,
diff --git a/src/store/actions/loadExpressionLanguages.ts b/src/store/actions/loadExpressionLanguages.ts
index 9fa28e0bd..e2d86d14e 100644
--- a/src/store/actions/loadExpressionLanguages.ts
+++ b/src/store/actions/loadExpressionLanguages.ts
@@ -1,7 +1,7 @@
import { Commit } from "vuex";
import { ExpressionUIIcons } from "@/store";
-import { getLanguage } from "@/core/queries/getLanguage";
+import { getLanguages } from "@/core/queries/getLanguages";
export interface Context {
commit: Commit;
@@ -14,18 +14,18 @@ export interface Payload {
export default async ({ commit, getters }: Context): Promise => {
try {
- const expressionLangs = getters.getAllExpressionLanguagesNotLoaded;
- // console.log({ expressionLangs });
- for (const [, lang] of expressionLangs.entries()) {
- const language = await getLanguage(lang);
- console.log("Got language", language);
- if (language !== null) {
- const uiData: ExpressionUIIcons = {
- languageAddress: language!.address!,
- createIcon: language!.constructorIcon!.code!,
- viewIcon: language!.iconFor!.code!,
- };
- commit("addExpressionUI", uiData);
+ const languages = await getLanguages();
+ for (const language of languages) {
+ if (language.iconFor) {
+ if (!getters.getLanguageUI(language.address!)) {
+ const uiData: ExpressionUIIcons = {
+ languageAddress: language!.address!,
+ createIcon: language!.constructorIcon!.code!,
+ viewIcon: language!.iconFor!.code!,
+ name: language!.name!,
+ };
+ commit("addExpressionUI", uiData);
+ }
}
}
} catch (e) {
diff --git a/src/store/actions/updateProfile.ts b/src/store/actions/updateProfile.ts
new file mode 100644
index 000000000..17b79c8b0
--- /dev/null
+++ b/src/store/actions/updateProfile.ts
@@ -0,0 +1,77 @@
+import { createProfile } from "@/core/methods/createProfile";
+import { Commit } from "vuex";
+import { ExpressionTypes, State, Profile } from "..";
+import { TimeoutCache } from "../../utils/timeoutCache";
+import type Expression from "@perspect3vism/ad4m/Expression";
+import { getExpression } from "@/core/queries/getExpression";
+
+export interface Context {
+ commit: Commit;
+ state: State;
+}
+
+export interface Payload {
+ username: string;
+ profilePicture: string;
+ thumbnail: string;
+}
+
+export default async (
+ { commit, state }: Context,
+ payload: Payload
+): Promise => {
+ commit("setUserProfile", payload);
+
+ try {
+ const user: Profile | null = state.userProfile;
+
+ const communities = Object.values(state.communities);
+ const cache = new TimeoutCache(1000 * 60 * 5);
+
+ for (const community of communities) {
+ const profileExpression = community.typedExpressionLanguages.find(
+ (t) => t.expressionType == ExpressionTypes.ProfileExpression
+ );
+ const didExpression = `${
+ profileExpression!.languageAddress
+ }://${state.userDid!}`;
+
+ console.log("profileExpression: ", profileExpression);
+
+ if (profileExpression) {
+ const exp = await createProfile(
+ profileExpression.languageAddress,
+ payload.username,
+ user!.email,
+ user!.givenName,
+ user!.familyName,
+ payload.profilePicture,
+ payload.thumbnail
+ );
+
+ console.log("Created new profileExpression: ", exp);
+
+ const expressionGql = await getExpression(exp);
+ const profileExp = {
+ author: expressionGql.author!,
+ data: JSON.parse(expressionGql.data!),
+ timestamp: expressionGql.timestamp!,
+ proof: expressionGql.proof!,
+ } as Expression;
+ cache.set(didExpression, profileExp);
+ } else {
+ const errorMessage =
+ "Expected to find profile expression language for this community";
+ commit("showDangerToast", {
+ message: errorMessage,
+ });
+ throw Error(errorMessage);
+ }
+ }
+ } catch (e) {
+ commit("showDangerToast", {
+ message: e.message,
+ });
+ throw new Error(e);
+ }
+};
diff --git a/src/store/actions/updateUser.ts b/src/store/actions/updateUser.ts
deleted file mode 100644
index c53cb9074..000000000
--- a/src/store/actions/updateUser.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Commit } from "vuex";
-
-export interface Context {
- commit: Commit;
-}
-
-export interface Payload {
- username: string;
-}
-
-export default async ({ commit }: Context, payload: Payload): Promise => {
- // TODO: GraphQL Mutation
- commit("setUserProfile", payload);
-};
diff --git a/src/store/getters/index.ts b/src/store/getters/index.ts
index df01189ca..10ea81045 100644
--- a/src/store/getters/index.ts
+++ b/src/store/getters/index.ts
@@ -75,26 +75,6 @@ export default {
return perspective;
},
- getAllExpressionLanguagesNotLoaded(state: State): Address[] {
- const expressionLangs: Address[] = [];
-
- for (const community of Object.values(state.communities)) {
- for (const expLang of community.expressionLanguages) {
- if (
- expressionLangs.indexOf(expLang) === -1 &&
- //@ts-ignore
- state.expressionUI.find(
- (val: ExpressionUIIcons) => val.languageAddress === expLang
- ) === undefined
- ) {
- expressionLangs.push(expLang);
- }
- }
- }
-
- return expressionLangs;
- },
-
getAgentLockStatus(state: State): boolean {
return state.agentUnlocked;
},
@@ -106,4 +86,8 @@ export default {
getApplicationStartTime(state: State): Date {
return state.applicationStartTime;
},
+
+ getLanguageUI: (state: State) => (language: string) => {
+ return state.expressionUI[language];
+ },
};
diff --git a/src/store/index.ts b/src/store/index.ts
index 7b9e0e1d6..dd1343a98 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -128,7 +128,7 @@ export interface State {
applicationStartTime: Date;
//TODO: this is a horrible type for this use; would be better to have a real map with values pointing to same strings where appropriate
//fow now this is fine
- expressionUI: ExpressionUIIcons[];
+ expressionUI: { [x: string]: ExpressionUIIcons };
agentUnlocked: boolean;
agentInit: boolean;
userProfile: Profile | null;
@@ -141,6 +141,7 @@ export interface ExpressionUIIcons {
languageAddress: string;
createIcon: string;
viewIcon: string;
+ name: string;
}
export enum ExpressionTypes {
@@ -197,7 +198,7 @@ export default createStore({
localLanguagesPath: "",
databasePerspective: "",
applicationStartTime: new Date(),
- expressionUI: [],
+ expressionUI: {},
agentUnlocked: false,
agentInit: false,
userProfile: null,
diff --git a/src/store/mutations/index.ts b/src/store/mutations/index.ts
index 901d3df1a..31d394830 100644
--- a/src/store/mutations/index.ts
+++ b/src/store/mutations/index.ts
@@ -130,7 +130,7 @@ export default {
},
addExpressionUI(state: State, payload: ExpressionUIIcons): void {
- state.expressionUI.push(payload);
+ state.expressionUI[payload.languageAddress] = payload;
},
updateApplicationStartTime(state: State, payload: Date): void {
diff --git a/src/utils/profileHelpers.ts b/src/utils/profileHelpers.ts
index d2dd55f16..286f580a6 100644
--- a/src/utils/profileHelpers.ts
+++ b/src/utils/profileHelpers.ts
@@ -26,7 +26,7 @@ export async function getProfile(
profileLangAddress: string,
did: string
): Promise {
- const cache = new TimeoutCache(1000 * 60 * 60);
+ const cache = new TimeoutCache(1000 * 60 * 5);
const profileLink = `${profileLangAddress}://${did}`;
diff --git a/src/utils/showMessageNotification.ts b/src/utils/showMessageNotification.ts
index 66a2d54ca..f8c90d07b 100644
--- a/src/utils/showMessageNotification.ts
+++ b/src/utils/showMessageNotification.ts
@@ -35,7 +35,7 @@ export default async (
// Only show the notification when the the message is not from self & the active community & channel is different
if (
- isMinimized ||
+ (isMinimized && !channel?.notifications.mute) ||
(store.state.userDid !== authorDid &&
(community?.perspective === communityId
? channel?.perspective !== channelId