#aws-amplify/ui-components - Trigger Toast with Custom Message - aws-amplify

I'm using #aws-amplify/ui-components library which is deprecated (for Vue 2). It comes with a Toast component (<amplfy-toast />)which alerts error messages in the top of the screen. In my Vue js component I want to trigger message but programatically where the console.log() is:
watch: {
authState: {
async handler(state) {
const data = this.authData
if (state === 'signedin') {
try {
const local = await this.axios.post('/api/v1/authentication/login', {
token: data.getSignInUserSession().getIdToken().getJwtToken()
})
if ( ! local.data.error) {
await this.$store.dispatch('login', data)
}
} catch (error) {
console.log(error) // Trigger toast here.
}
}
},
immediate: true
}
},
How can I do this?

You can do this with dispatchToastHubEvent() function or simply call:
Hub.dispatch('UI Auth', {
event: 'ToastAuthError',
message: error.message
})

Related

Accessing a variable name in vue 3 like a class component does

I have only use Vue 2 with class components. Example:
#Options({
components: {
HelloWorld,
},
})
export default class Home extends Vue {}
However now that it is deprecated I am having issues access variables. I am not sure why I cannot use this anymore; I do not quite understand how the template will be aware of the variables and how the void methods can manipulate them.
</button>
{{name}}-{{age}}
env: {{ mode }} - My name: {{ myName}}
</div>
</template>
<script lang="ts">
import {api} from "#/lib/api";
export default {
name: "Home",
data() {
return {
name: String,
age: Number,
mode: process.env.NODE_ENV,
myName: process.env.VUE_APP_TITLE
}
},
methods: {
submit(): void {
api.getTest().then(
response => {
const testResponse = JSON.stringify(response)
this.name = JSON.parse(testResponse).name
this.age = parseInt(JSON.parse(testResponse).age)
}).catch(response => {
console.log("Error while getting the response", response)
})
},
counter(age: number): void {
age = age + 1
}
}
}
</script>
--- update 1 ----
I received some excellent advice from a poster, suggesting I ref or reactive.
Vue 3 is built with typescript which is why class components were decided to be deprecated. However I am not able to use my gRPC generated type objects, or at least I do not know how at this moment
IDE
axios
export const api = {
async getTest() {
try{
return await grpcClient.get<TestResponse>("/v1/test")
.then(res => {
console.log(url.baseUrl)
return res.data
})
}catch (err) {
console.log("error" + err);
}
},
}
So vue3 way of defining component is a bit different than v2 - more like native JS. Here's quick example how you component could look like in vue3. Instead of methods, just create function. Instead of data use reactive or ref.
import { reactive, computed } from 'vue'
import { api } from '#/lib/api'
export default {
setup() {
const state = reactive({
name: '',
age: 0,
mode: process.env.NODE_ENV,
myName: process.env.VUE_APP_TITLE
})
const submit = async () => {
try {
const response = await api.getTest()
state.name = response.name
state.age = response.age
} catch (error) {
console.log('Error while getting the response:', error)
}
}
const counter = (age) => {
state.age = age + 1
}
return {
...state,
submit
}
}
}

Get content related to a user in Strapi

I have a collection in Strapi called projects and I want to be able to fetch only the projects belonging to the currently logged in user. I'm using Next.js with NextAuth on the frontend and I'm currently filtering the results using:
/api/projects?filters[user][id][$eq]=${session.id}
This works fine except the endpoint still allows a user to fetch projects for all users if accessed directly. I'm thinking a better approach would be to setup a custom API endpoint in Strapi which would be something like /api/projects/:user. Is this the best way to acheive this? I've managed to setup a custom endpoint in Strapi using the CLI but I'm not sure what logic needs to go in the controller. Would modifiying an exisiting endpoint be better?
Any advice appreciated, thanks!
Custom endpoint create is good idea. I had same problem. Once i created custom endpoint then i got data with entitiyservice. It's work. Below image is my code.
./scr/api/[collection]/controllers/[collection].js
'use strict';
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::user-profile.user-profile', ({ strapi }) => ({
async me(ctx) {
try {
const user = ctx.state.user;
const datas = await strapi.entityService.findMany("api::user-profile.user-profile", {
filters: {
user: {
id: user.id
}
}
})
return datas;
} catch (err) {
ctx.body = err;
}
}
}));
If you will use all endpoints in collection like (create,update,delete,find,findone). You must override the all endpoints . Example is the below.
'use strict';
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::education.education', ({ strapi }) => ({
async create(ctx) {
try {
const user = ctx.state.user;
ctx.request.body.data.users_permissions_user = user.id
const datas = await strapi.entityService.create("api::education.education", {
...ctx.request.body
})
return datas;
} catch (err) {
ctx.body = err;
}
},
async update(ctx) {
try {
const user = ctx.state.user;
ctx.request.body.data.users_permissions_user = user.id
const { id } = ctx.params;
const experienceData = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
},
id: id
}
});
if (experienceData.length === 0) {
return {
data: null,
error: {
message: ''
}
}
}
const datas = await strapi.entityService.update("api::education.education", id, {
...ctx.request.body
})
return datas;
} catch (err) {
ctx.body = err;
}
},
async delete(ctx) {
try {
const user = ctx.state.user;
const { id } = ctx.params;
const experienceData = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
},
id: id
}
});
if (experienceData.length === 0) {
return {
data: null,
error: {
message: ''
}
}
}
const datas = await strapi.entityService.delete("api::education.education", id)
return datas;
} catch (err) {
ctx.body = err;
}
},
async findOne(ctx) {
try {
const user = ctx.state.user;
const { id } = ctx.params;
const experienceData = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
},
id: id
}
});
if (experienceData.length === 0) {
return {
data: null,
error: {
message: ''
}
}
}
const datas = await strapi.entityService.findOne("api::education.education", id)
return datas;
} catch (err) {
ctx.body = err;
}
},
async find(ctx) {
try {
const user = ctx.state.user;
const datas = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
}
}
})
return datas;
} catch (err) {
ctx.body = err;
}
},
}));
No extra endpoints and no extra codes.
Strapi v4
Yes, creating separate endpoint for this task would be great.
Instead of /api/projects/:user using this type of route, use /api/projects as you can get current logged in users details from ctx.state.user
No, Instead of modifying your existing controller create new controller and use that controller to satisfy your needs.
I ended up extending my controller. In src/api/controllers/project.js I made the following changes:
"use strict";
const { createCoreController } = require("#strapi/strapi").factories;
module.exports = createCoreController("api::project.project", {
async find(ctx) {
const user = ctx.state.user;
ctx.query.filters = {
...(ctx.query.filters || {}),
user: user.id,
};
return super.find(ctx);
},
});
Then simply call the /api/projects endpoint.
Answer based on this guide Limit access of Strapi users to their own entries.

How can I attach word/pdf files to React Native gifted chat?

I would like to be able to send word doc/pdf files via messaging in my react native app using react native gifted chat. I have had a look at a few links which suggests using the renderActions() function in react-native-gifted-chat but it does not specify how I can implement this. Do you know how I can implement this function? Would I need to import a package like document picker or file picker in the function? If so, how can I use this? I'm fairly new to react native. Can someone please help here?
Here is what I have so far in my renderActions() method:
renderActions() {
return(
<Actions
{...props}
options={{
['Document']: async (props) => {
try {
const result = await DocumentPicker.pick({
type: [DocumentPicker.types.doc || DocumentPicker.types.docx || DocumentPicker.types.pdf],
});
console.log("resulting file: "+result);
console.log("string result? "+JSON.stringify(result));
} catch(e){
if(DocumentPicker.isCancel(e)){
console.log("User cancelled!")
} else {
throw e;
}
}
},
['Cancel']: (props) => {console.log("cancel")}
}}
icon={() => (
<Ionicons
name={'add'}
size={28}
color={'#0077ff'}
style={{left:0, bottom:0}}
/>
)}
onSend={args => console.log(args)}
/>
)
}
Which produces:
I have managed to get the file object. Does anyone know how I can append this doc file object to the messages in gifted chat once selected? Can someone please help? How can I display in the chat box and then send the file?
Thanks.
The link https://github.com/FaridSafi/react-native-gifted-chat/issues/2111 mentions to to add parameters to the message object. For example you have this message object:
const newMessage = {
_id: data.send_at,
text: data.messagetext,
createdAt: data.send_at,
(...),
file_type: data?.file_type,
file_id: data?.file_id,
}
Then render a custom view:
const renderCustomView = (props) => {
if (props?.currentMessage?.file_type) {
(...)
}
else {
(...)
}
}
Can someone please help on where I would need to create the messages object as well as what I would need to put inside the renderCustomView function? I am really not too sure on what needs to be done.
function renderActions(props) {
let selectFile = async () => {
//Opening Document Picker to select one file
try {
const res = await DocumentPicker.pick({
//Provide which type of file you want user to pick
type: [DocumentPicker.types.pdf],
//There can me more options as well
// DocumentPicker.types.allFiles
// DocumentPicker.types.images
// DocumentPicker.types.plainText
// DocumentPicker.types.audio
// DocumentPicker.types.pdf
});
//Printing the log realted to the file
console.log('res : ' + JSON.stringify(res));
props.onSend({pdf:res.uri,file_type:'pdf'});
//Setting the state to show single file attributes
singleFile = res;
// setSingleFile(res);
} catch (err) {
singleFile = null;
// setSingleFile(null);
//Handling any exception (If any)
if (DocumentPicker.isCancel(err)) {
//If user canceled the document selection
alert('Canceled from single doc picker');
} else {
//For Unknown Error
alert('Unknown Error: ' + JSON.stringify(err));
throw err;
}
}
};
const handlePicker = () => {
// console.log('edit');
ImagePicker.showImagePicker({}, (response) => {
// console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
setAvatar({uri: response.uri});
console.log(response.uri);
props.onSend({image:response.uri});
// onSend([{"_id": "f3fda0e8-d860-46ef-ac72-0c02b8ea7ca9", "createdAt": new Date(), "image": response.uri, "user": {"_id": 1}}])
return response.uri
// here we can call a API to upload image on server
}
return avatar;
});
};
return (
<Actions
{...props}
options={{
['Send Image']: () => handlePicker(),
['Send Files']: () => selectFile(),
}}
icon={() => (
<Icon name='attachment' size={28} />
)}
// onSend={onSend}
/>
)}
in custom view :
export default class CustomView extends React.Component {
renderPdf() {
return (
<TouchableOpacity style=
{[styles.container,this.props.containerStyle]} >
<Image
{...this.props.imageProps}
style={[styles.image, this.props.imageStyle]}
source ={{
uri:""
}}
/>
</TouchableOpacity>
);
}
render() {
if (this.props.currentMessage.file_type == 'pdf') {
return this.renderPdf();
} else if (this.props.currentMessage.template &&
this.props.currentMessage.template != 'none') {
return this.renderHtml();
}
return null;
}
}

Input not updating on react testing library, thus test failing, however it does update on the actual app

I want to test that when i type a value in an input(inputA), anoter input(inputB) gets updated with a value.
inputA accepts a postal code e.g: "10999", after inputB shows a location: "Berlin"
This works on the actual app, i type in inputA, and inputB gets updated.
When ome types on inputA, an action is dispatched and then inputB gets a new value from the redux state.
This is my test code, any ideas why it doesnt updates the input with placeholder of "Ort" on the test, but it does on the actual app?
import { render, withIntl, withStore, configureStore, withState } from "test-utils-react-testing-library";
import { screen, fireEvent, withHistory, withRoute, within } from "#testing-library/react";
import configureMockStore from 'redux-mock-store';
import ProfileForm from "./ProfileForm";
import PersonalDetails from "../PersonalDetails/PersonalDetails";
const STATE = {
locations: { locations: {} },
streets: { streets: {} },
password: {}
};
const mockStore = configureMockStore();
const STORE = mockStore({
streets: {
isFetching: false,
},
locations: {
locations: {
isFetching: false,
},
},
user: {
session: {
impersonated_access_token: "",
},
updateError: "error",
},
});
const props = {
id: "user1",
user: { email: "max#muster.de" },
locations: {},
onSubmit: jest.fn(),
};
beforeEach(jest.resetAllMocks);
describe("ProfileForm", () => {
describe("on personal details change", () => {
it("auto selects only location when postalcode becomes selected", () => {
const locations = { electricity: { [PLZ_1]: [LOCATION_OBJ_1] } };
const user = { postalcode: null };
render(<ProfileForm {...props} user={user} locations={locations} />, [...decorators, withStore(STORE)]);
const input = screen.getByPlaceholderText("PLZ");
fireEvent.change(input, { target: { value: "10999" } })
screen.debug(screen.getByPlaceholderText("PLZ"))
screen.debug(screen.getByPlaceholderText("Ort"))
expect(screen.getByPlaceholderText("Ort")).toHaveValue("Berlin");
});
});
I guess your input hasn't been updated yet.
Try to use waitfor:
https://testing-library.com/docs/dom-testing-library/api-async#waitfor
import { waitFor } from "#testing-library/react";
const inputNode = screen. getByPlaceholderText("Ort");
// keep in mind that you need to make your test async like this
// it("auto selects only location when postalcode becomes selected", async () => {
await waitFor(() => expect(inputNode).toHaveValue("Berlin"));
If it won't work, try to add timeout:
await waitFor(() => expect(inputNode).toHaveValue("Berlin"), { timeout: 4000 });
I've encountered a similar proplem and found that changes in the microtask queue aren't always flushed, so the changes are not applied/rendered until the test is finished running. What worked for me, was to call jest.useFakeTimers() at the beginning of your testcase, and then await act(async () => { jest.runOnlyPendingTimers() }); after the call to fireEvent.<some-event>(...)
In your case:
it("auto selects only location when postalcode becomes selected", async () => {
jest.useFakeTimers();
const locations = { electricity: { [PLZ_1]: [LOCATION_OBJ_1] } };
const user = { postalcode: null };
render(<ProfileForm {...props} user={user} locations={locations} />, [...decorators, withStore(STORE)]);
const input = screen.getByPlaceholderText("PLZ");
fireEvent.change(input, { target: { value: "10999" } })
await act(async () => {
jest.runOnlyPendingTimers();
});
screen.debug(screen.getByPlaceholderText("PLZ"))
screen.debug(screen.getByPlaceholderText("Ort"))
expect(screen.getByPlaceholderText("Ort")).toHaveValue("Berlin");
});
Tried, but get this error: Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. No idea where that comes from :(
Try to use findBy instead of getBy.
https://testing-library.com/docs/dom-testing-library/api-queries#findby
import { screen, waitFor } from "#testing-library/react";
const inputNode = await screen.findByPlaceholderText("Ort");
// or with timeout: await screen.findByPlaceholderText("Ort", { timeout: 4000 });
await waitFor(() => expect(inputNode).toHaveValue("Berlin"));

how to unsubscribe () a firebase collection in a vue.js component

In a vuejs component which dynamically retrieves data with firebase I would like to unsubscribe when I quit the component.
In the firebase documentation indicates that you must use the unsubscribe() function; to stop listening to the collection.
Unfortunately, this function cannot be used directly because it is declared undefined.
Here is the component code:
<script>
import db from "../../firebase/init";
let subscribe;
export default {
// ...
beforeDestroy() {
// Don't work form me !!!
unsubscribe();
},
methods: {
async getMyCollection() {
try {
subscribe = await db.collection("myCollection");
subscribe.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
// Do something
}
});
});
} catch (error) {
console.log(error);
}
}
}
</script>
thanks for the help
Its because you have not defined the unsubscribe anywhere. Please check the code below.
<script>
import db from "../../firebase/init";
let unsubscribe;
export default {
// ...
beforeDestroy() {
unsubscribe();
},
methods: {
async getMyCollection() {
try {
unsubscribe = await db.collection("myCollection")
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
// Do something
}
});
});
} catch (error) {
console.log(error);
}
}
}
</script>

Resources