Engineering - Prayers Connect https://www.prayersconnect.org From 2016 Wed, 09 Aug 2023 03:23:31 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.2 https://www.prayersconnect.org/wp-content/uploads/2023/03/cropped-main-logo-png-1-32x32.png Engineering - Prayers Connect https://www.prayersconnect.org 32 32 Removing Language Barrier with AI – A Practical Example https://www.prayersconnect.org/1003-removing-language-barrier-with-ai-a-practical-example/ https://www.prayersconnect.org/1003-removing-language-barrier-with-ai-a-practical-example/#respond Tue, 08 Aug 2023 21:20:32 +0000 https://prayersconnect.org/?p=1003 We used OpenAI's ChatGPT API to fix a problem our data mining team had been struggling with for a long time. Keep reading to learn more...

The post Removing Language Barrier with AI – A Practical Example first appeared on Prayers Connect.

]]>
On prayersconnect.com, we provide different types of information about a mosque including a general description or overview. Originally, this overview section was meant to be meticulously crafted by our editorial team. However, since English is not the first language for many of our data mining team members, they often felt uncertain about writing the descriptions themselves. When they did write them, the results were frequently unsatisfactory due to poor composition and grammatical mistakes.

This issue remained unresolved for quite a long time, largely due to our non-profit budget constraints. But recently, a spark of innovation led us to a solution: leveraging AI to overcome this challenge.

In our backend API, we designed a prompt containing the information we believed would be valuable. We then made an API call to the gpt-3.5-turbo model, which in turn generated a description based on the provided details. Here’s an example:

<<OPENAI
Generate a human-friendly brief description for the mosque based on the provided data within Beginning & end of Data Section. Adhere to the following guidelines:

<A number of rules; redacted for brevity>


Beginning of Data Section
<Provided data about mosques; redacted for brevity>
End of Data Section

OPENAI

We have noticed that the generated content occasionally deviates from our instructions. However, with the availability of gpt-4, we anticipate a significant improvement that will bring us closer to our goal of providing accurate and engaging descriptions for our users.

The post Removing Language Barrier with AI – A Practical Example first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/1003-removing-language-barrier-with-ai-a-practical-example/feed/ 0
How AI Transformed Our Workflows for the Good! https://www.prayersconnect.org/945-learn-how-ai-transformed-prayers-connect-workflows-for-better/ https://www.prayersconnect.org/945-learn-how-ai-transformed-prayers-connect-workflows-for-better/#comments Mon, 17 Jul 2023 04:16:19 +0000 https://prayersconnect.org/?p=945 Get more insight on how Prayers Connect adopted and use different AI solutions including ChatGPT, Bard, Github CoPilot etc.

The post How AI Transformed Our Workflows for the Good! first appeared on Prayers Connect.

]]>
Prayers Connect is a non-profit with a limited capacity of hiring experienced engineers. So the potential and capabilities of AI have opened up new paths for us to explore and fill some gaps that we had with our capacity.

We are a very early adopter of AI technologies. Even before ChatGPT took the world by storm, we were using Github Copilot since the preview. While GitHub CoPilot improved our coding speed, with the arrival of ChatGPT the landscape of our work transformed significantly.

Pre-ChatGPT

As mentioned in the intro, we’ve been using GitHub CoPilot from the very beginning, well before GA. Our speed to code improved significantly as it reduced the amount of time we would need to consult the documentation, and saved a lot of keystrokes by suggesting context-aware codes. Here are two examples that excited us at the very beginning.

  • One of the first signs of CoPilot’s capability was when we were writing a pretty big form in React. As we use typescript, we defined the interface first which it helped with auto-completing codes. However, when we completed writing our first input field html it started suggesting the rest of the markup based on the schema. Accuracy and speed blew us out. Hours of boring typing were completed in a couple of minutes.
  • We were writing a method to geocode IP addresses. CoPilot auto-completed the line with a service (API call) we were not familiar with. Then we reviewed the site’s offering and found out it is much better than the solution we were planning to use.

There are many other examples where we fall in love with CoPilot almost every day. However, after some time hitting the Tab + Return combination became a little boring as it was suggested only little snippets. Lately, it is suggesting longer snippets but the accuracy of that isn’t as good as it is for smaller snippets.

Arrival of ChatGPT!

Several challenges that we were facing were solved when ChatGPT arrived and took the world by storm. Its wide capability as well as its free tier allowed us to adopt it extensively. Now ChatGPT is a part of our SDLC.

How do we use it?
In the beginning, we use ChatGPT to explore different alternative solutions to a problem. Once we decide on an implementation route, we use it to break down the implementation. We carefully create a prompt and review the proposed solution. If we do not like it or do not consider it accurate, we either regenerate or re-prompt it.

We use code generation by ChatGPT for different languages like Typescript, Ruby, Terraform, and different contexts like Node.js, React-Native, and Ruby on Rails. In addition to solving a particular problem, we use it to write automated tests which is very helpful. ChatGPT (also CodiumAI) quickly writes tests for various edge cases that are likely to be skipped by developers given the time it requires to set up the test. However, one frequent challenge we face is that it forgets the context of our project and writes tests in different tools than what we use. So every now and then, we need to provide it context of our projects.

In addition to programming, we use ChatGPT for content writing by checking grammar, improving writing, etc. Given ChatGPT does not have the latest data, we use Bard for various research especially where we need recent data.

Also, we’ve tried to use tools like AutoGPT but were not very successful in achieving some of our objectives.

AI Image Generation

In the beginning, we were using DALL-E to generate images. However, the speed and quality threw us away. While looking for an alternative, the speed, quality, and price of Fotor. attracted us. However, since we were introduced to MidJourney this is our go-to solution for generating AI images. We use AI-generated images for our articles. Even the cover image of this article is generated by Midjourney.

One thing we regret is that Midjourney can’t be used by other people on the Discord Channel. We would love to use our quota to be able to share with all team members.

If you look at our new landing page, the images of each continent, and country are generated by AI which would, otherwise, take a very long time. Alternatively, it would be cost-ineffective for us to buy so many images. Now our limitation is our imagination prompt’s capability.

Using AI for Videos

We haven’t used AI for video extensively yet. The only experimentation we’ve done so far is using Fliki to create a short video that we’ve published on YouTube.

If you’re not familiar with AI tools like ChatGPT, you can consider watching the following video (not made by us).

The post How AI Transformed Our Workflows for the Good! first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/945-learn-how-ai-transformed-prayers-connect-workflows-for-better/feed/ 3
Bug-free Software? A Few Quick Tips for Manual Testing https://www.prayersconnect.org/931-bug-free-software-a-few-quick-tips-for-manual-testing/ https://www.prayersconnect.org/931-bug-free-software-a-few-quick-tips-for-manual-testing/#comments Sun, 16 Jul 2023 03:40:06 +0000 https://prayersconnect.org/?p=931 Learn some quick tips of (manual) software testing from our experience in Prayers Connect. These short and practical tips will help you become a better tester.

The post Bug-free Software? A Few Quick Tips for Manual Testing first appeared on Prayers Connect.

]]>
Manual testing is an integral part of software development. It’s the process where we, as testers, wear the hats of end-users, exploring the application to find any potential bugs.

From the very beginning Prayers Connect engages software testers to make sure it is delivering bug-free & quality products to the end users. Later, we introduced internship programs for aspirational testers. Over the years our team learned a few techniques to test our web applications effectively. Here are some quick tips from our learning.

Understand the Product

Start by learning about the product. Understand its purpose, features, and target users. Understanding who are the users of the product and how they use the features is critical to think like a user.

Plan Your Testing

Don’t dive into testing without a plan. Outline what you’ll test, how you’ll test it, and when you’ll do it. This way, you’ll make sure nothing slips through the cracks.

Write Detailed Test Cases

Think of test cases as your testing roadmap. Outline the steps to execute, the expected outcomes, and the actual results. It’s important to write detailed and clear test cases to ensure nothing gets lost in translation. This will be very useful to report the bugs to development teams.

Prioritize Your Tests

Not all tests are created equal. Some features are more critical than others, and they should be tested first. Prioritize your tests based on the importance of the features. If unsure, consult the product and development teams to identify critical and fragile aspects of the product.

Think Like the User

As indicated earlier, an effective test thinks like a user. You’re the advocate for the user. Always put yourself in the user’s shoes. Ask yourself: “If I were a user, what would I do?”

Try the ‘Not-so-obvious’

Developers probably told you happy path and it’s important to test that. But what about the unhappy paths and specially not-so-obvious ones? The unconventional, the unexpected? Test those edge cases too.

Always Document

Your testing is only as good as your documentation. Keep track of what you’ve tested, the results, and any bugs you’ve found. This information can be invaluable for future testing and development.

Don’t Forget About Compatibility

Your web application will be accessed from different devices, browsers, and operating systems. Make sure to test it across all possible combinations to ensure a smooth user experience. Nowadays there is no lack of solutions to test things across devices, browsers, and operating systems.

Re-Test and Regression Test

Found a bug? Great. After it’s fixed, don’t forget to re-test the functionality to make sure it’s really fixed. Also, perform a regression test to check if the fix hasn’t broken anything else. This is the area developers often fall short especially when there is no automated test coverage.

Communicate Effectively

You’re part of a team. Keep everyone in the loop about what you’re testing, your findings, and any roadblocks you’re facing. Good communication leads to quick resolution and a better end product.

In conclusion, as a tester, your role is pivotal in the collaboration between product vision and technical execution. Your keen eye and curiosity help create a seamless user experience. Remember, every bug caught is one less hurdle for users. Keep collaborating, keep exploring, and continue making the web a smoother place for all!

Note: We use automated testing extensively throughout our stack and we also use AI to assist us there. We’ll cover that in a different post.

The post Bug-free Software? A Few Quick Tips for Manual Testing first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/931-bug-free-software-a-few-quick-tips-for-manual-testing/feed/ 1
How to Build a Mobile-Responsive Web App with React & Material-UI https://www.prayersconnect.org/685-how-to-build-a-mobile-responsive-web-app-with-react-material-ui/ https://www.prayersconnect.org/685-how-to-build-a-mobile-responsive-web-app-with-react-material-ui/#respond Tue, 06 Jun 2023 22:54:46 +0000 https://prayersconnect.org/?p=685 Discover the importance of responsive design and learn how to build a responsive web app using React and Material-UI. Explore responsive layout techniques.

The post How to Build a Mobile-Responsive Web App with React & Material-UI first appeared on Prayers Connect.

]]>
Introduction

In today’s digital landscape, the responsive web app is crucial to provide a seamless and enjoyable user experience across various devices and screen sizes. In this blog post, I will explore the importance of responsive design and demonstrate how to build a responsive web application using React and Material-UI. Throughout this post, I will cover responsive layout techniques, adaptive components, and handling different screen sizes to ensure our application looks and functions flawlessly on any device.

Why Responsive Web App Matters

Responsive design plays a pivotal role in ensuring that your web app adapts and responds to the user’s device, whether it’s a desktop, tablet, or mobile phone. By eliminating the need for separate designs for different devices, the responsive design enhances development efficiency and reduces maintenance efforts. Additionally, responsive design significantly improves accessibility, increases user engagement, and positively impacts search engine rankings. For further insights on software engineering, visit the Prayers Connect blog. You can visit the Prayers Connect blog to read more about software engineering.

CSS in JS

CSS in JS refers to an innovative approach in web development where CSS styles are written and managed using JavaScript instead of traditional external CSS files. This approach involves defining CSS styles as JavaScript objects or using CSS-like syntax within JavaScript code.

The advantages of CSS in JS are manifold. First, it enhances encapsulation, allowing for more effective component styling. Moreover, it facilitates the creation of dynamic and responsive styles, enabling seamless adaptation to different devices. Additionally, CSS in JS enhances code organization and simplifies dependency management, resulting in more flexible and reusable styles.

What are MUI Breakpoints

MUI breakpoints are responsive breakpoints provided by the Material-UI (MUI) library. In the context of web development, breakpoints define specific screen widths at which the layout of a responsive web application should change. These breakpoints are instrumental in ensuring that the design and components of your application adapt and respond effectively to different screen sizes and devices.

MUI Breakpoints that Can be used to Create Responsive Designs

MUI breakpoints are responsive breakpoints provided by the Material-UI (MUI) library. In the context of web development, breakpoints define specific screen widths at which the layout of a responsive web application should change. These breakpoints are instrumental in ensuring that the design and components of your application adapt and respond effectively to different screen sizes and devices.

MUI Breakpoints that Can Be Used to Create Responsive Designs
Material-UI (MUI) offers a set of predefined breakpoints that can be utilized to create responsive designs. These breakpoints are based on common device sizes and serve as reference points for adapting the layout and styles of components at different screen widths. Let’s explore the default breakpoints provided by Material-UI:

xs (Extra Small): The smallest breakpoint, typically targeting mobile devices. The range spans from 0px to 599.95px.

sm (Small): This breakpoint is designed for small devices like smartphones. It covers a range from 600px to 959.95px.

md (Medium): The medium breakpoint is suitable for tablets and small laptops. It spans from 960px to 1279.95px.

lg (Large): This breakpoint caters to larger devices like laptops and desktops. The range extends from 1280px to 1919.95px.

xl (Extra Large): The extra-large breakpoint represents larger screens, such as large desktop monitors or high-resolution displays. It starts from 1920px.

Getting Started with Responsive Web App

Let’s start by setting up a React project and integrating Material-UI. We will install the necessary dependencies and configure our project to leverage Material-UI’s responsive features. So open your terminal and run this command. Also, you can follow installation guidelines from here.

npm create vite@latest

After successfully creating React app now let’s install MUI on our React App. Run the below command to your terminal. You can follow MUI installation guidelines from here.

npm install @mui/material @mui/styled-engine-sc styled-components

So now we already understood the MUI breakpoints right? let’s see how we can change the app text and font size inside the BreakpointDemo.jsx file. Simply create a new component called BreakpoinDemo.jsx and mount it with the main.jsx file in your react app. Then just copy this code in your BreakpointDemo.jsx

import React, { useState, useEffect } from 'react';
import { useTheme } from '@mui/material/styles';
import { Container, Typography } from '@mui/material';

const BreakpointDemo = () => {
  const theme = useTheme();
  const [currentBreakpoint, setCurrentBreakpoint] = useState('');

  useEffect(() => {
    const handleResize = () => {
      const { breakpoints } = theme;
      let activeBreakpoint = '';

      if (window.innerWidth < breakpoints.values.xs) {
        activeBreakpoint = 'xs';
      } else if (window.innerWidth < breakpoints.values.sm) {
        activeBreakpoint = 'sm';
      } else if (window.innerWidth < breakpoints.values.md) {
        activeBreakpoint = 'md';
      } else if (window.innerWidth < breakpoints.values.lg) {
        activeBreakpoint = 'lg';
      } else {
        activeBreakpoint = 'xl';
      }

      setCurrentBreakpoint(activeBreakpoint);
    };

    window.addEventListener('resize', handleResize);
    handleResize();

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [theme]);

  const getBackgroundColorForBreakpoint = (breakpoint) => {
    switch (breakpoint) {
      case 'xs':
        return 'red';
      case 'sm':
        return 'blue';
      case 'md':
        return 'green';
      case 'lg':
        return 'orange';
      case 'xl':
        return 'purple';
      default:
        return 'white';
    }
  };

  const backgroundColor = getBackgroundColorForBreakpoint(currentBreakpoint);

  return (
    <Container style={{ backgroundColor }}>
      <Typography variant="h2" color="textPrimary">
        Current Breakpoint is: {currentBreakpoint}
      </Typography>
    </Container>
  );
};

export default BreakpointDemo;
  1. To test it on your browser Open your browser and navigate to http://localhost:5173 (Where your React development Server is running) You should see the BreakpointDemo component rendered, and the text background color will change based on the current breakpoint.
  2. Resize your browser window to see the text background color change dynamically as the breakpoint changes.

By following these steps, you will be able to run and test the code in your browser. The BreakpointDemo component will display the current breakpoint and change the text background color accordingly as you resize the window. Are you interested to learn more about React MUI? Then watch this video tutorial.

Mastering Responsive Web App Development with React and MUI

When creating a responsive web application using React and Material-UI, here are some best practices and tips to consider:

  1. Firstly Plan for Mobile-First Design: Start designing and developing your application with a mobile-first approach. This ensures that the application is optimized for smaller screens and progressively enhanced for larger screens.
  2. Next Use Material-UI’s Grid System: Material-UI provides a powerful Grid component that simplifies responsive layout creation. Utilize the grid system to create responsive layouts that adapt to different screen sizes.
  3. In addition, It’s important to Follow Material Design Guidelines: Adhere to the principles and guidelines of Material Design when designing your application’s user interface. This helps maintain consistency and familiarity across different devices and platforms.
  4. To handle different screen sizes effectively, Utilize Material-UI’s Breakpoints: Take advantage of Material-UI’s built-in breakpoints to handle different screen sizes. Use the responsive props and CSS-in-JS solutions like styled-components or the Material-UI styling system to conditionally apply styles based on breakpoints.
  5. Ensure thorough testing on Real Devices and Emulators: Ensure you test your application on real devices and emulators representing various screen sizes and orientations. This helps identify and address any issues specific to different devices and form factors.
  6. Optimize image loading by using responsive images and lazy loading techniques: Optimize the loading of images by using responsive images and lazy loading techniques. Consider using the img component provided by Material-UI or popular image optimization libraries.

Enhancing User Experience and App Responsiveness: Tips & Tricks

  1. Prioritize Content: Firstly, ensure that the most important content is prominently displayed and easily accessible on smaller screens. Prioritize key information and actions to provide a seamless user experience on mobile devices.
  2. Use Responsive Typography: Next, implement responsive typography that adjusts based on screen size. Use relative units like rem or em to make the text scale appropriately on different devices. Consider using Material-UI’s responsive typography features for consistent and readable text across breakpoints.
  3. Optimize Navigation: In addition, simplify and streamline your navigation for mobile devices. Use techniques like collapsing menus, off-canvas navigation, or bottom navigation to provide a user-friendly experience on smaller screens. Consider using Material-UI’s Drawer or BottomNavigation components.
  4. Lazy Load Content: To improve performance, implement lazy loading techniques to load content and resources only when needed. This improves initial load times and reduces the amount of data transferred, particularly for mobile users with limited bandwidth.
  5. Performance Optimization: Lastly, prioritize performance optimization. Minimize the use of heavy animations, unnecessary JavaScript, and excessive DOM manipulation. Optimize the code by removing unused dependencies and applying performance best practices. Consider using tools like code splitting and tree shaking to reduce bundle size and improve load times.

Conclusion

In conclusion, building responsive web apps with React and Material-UI for exceptional user experiences on any device. Implement responsive design, adaptive components, and screen size handling techniques. Check documentation for MUI Breakpoints and the link for the Responsive Layout Grid. Delight users with a seamless experience.

The post How to Build a Mobile-Responsive Web App with React & Material-UI first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/685-how-to-build-a-mobile-responsive-web-app-with-react-material-ui/feed/ 0
How We Made Our Search Faster with Meilisearch https://www.prayersconnect.org/690-how-we-made-our-search-faster-with-meilisearch/ https://www.prayersconnect.org/690-how-we-made-our-search-faster-with-meilisearch/#respond Sat, 03 Jun 2023 05:03:00 +0000 https://prayersconnect.org/?p=690 Learn Prayers Connect's journey to Meilisearch to dramatically improve search api response resulting in boost in Apdex and reduction in users' Misery Index.

The post How We Made Our Search Faster with Meilisearch first appeared on Prayers Connect.

]]>
At Prayers Connect, our engineering team is continuously striving to enhance the UI, UX, reliability, and performance, despite budget and resource constraints. In achieving this, we frequently utilize free or sponsored tools.

We are incredibly grateful to Algolia for sponsoring us with a premium plan, including 10K documents, alongside our other generous sponsors. This plan served us exceptionally well when our services were solely confined to the USA. However, as we expanded into other countries, the document limitation became a stumbling block. As a result, we had to revert to the standard MySQL search.

Let’s take a look at the performance metrics provided by another one of our sponsored tools, Sentry, when using our un-optimized MySQL search:

We found that it took nearly 2 seconds per API call. Once we pinpointed this issue, we initiated a search for low-cost, affordable alternatives. Our research led us to Meilisearch, an option that required minimal learning curve and promised significant improvements in response time. Once implemented in production, here’s the change we observed:

The response time has dramatically improved, now measuring below 100ms – approximately 19 times faster than before. This acceleration has also effectively raised the Apdex (more is better) score from 0.32 to 0.77, while simultaneously lowering the user misery index (less is better) from 0.65 to 0.55.

This is a clear victory, yet our journey towards optimization is far from over. Given that this search is executed server-side, no data can be served until the completion of this and other API calls. Therefore, we remain committed to enhancing the performance of server-side rendering.

The post How We Made Our Search Faster with Meilisearch first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/690-how-we-made-our-search-faster-with-meilisearch/feed/ 0
10 Imaginary Mosque Designs: Merging Rich History, Present, and Future Through AI-Inspired Concepts https://www.prayersconnect.org/474-10-imaginary-mosque-designs-merging-rich-history-present-and-future-through-ai-inspired-concepts/ https://www.prayersconnect.org/474-10-imaginary-mosque-designs-merging-rich-history-present-and-future-through-ai-inspired-concepts/#respond Mon, 08 May 2023 02:00:05 +0000 https://hq.prayersconnect.com/?p=474 In this fascinating exploration of architectural creativity, we harness the power of artificial intelligence to generate captivating mosque designs. Drawing inspiration from various historical eras and cultural influences, we infuse these concepts with a futuristic vision, resulting in a striking blend of past and present. By collaborating with AI, we unlock the potential for fresh...

The post 10 Imaginary Mosque Designs: Merging Rich History, Present, and Future Through AI-Inspired Concepts first appeared on Prayers Connect.

]]>
In this fascinating exploration of architectural creativity, we harness the power of artificial intelligence to generate captivating mosque designs. Drawing inspiration from various historical eras and cultural influences, we infuse these concepts with a futuristic vision, resulting in a striking blend of past and present. By collaborating with AI, we unlock the potential for fresh and innovative ideas.

The concepts presented in this article were conceived by ChatGPT, while the images were brought to life using a combination of advanced AI image generators and human artistry. As you immerse yourself in these visually stunning depictions, allow your imagination to soar and appreciate the harmonious fusion of tradition, innovation, and technology.

Byzantine-Inspired Cyber Mosque

Drawing from the rich history of Byzantine art and architecture, this concept combines traditional elements like mosaics, domes, and arches with futuristic cyberpunk aesthetics. The mosque features holographic calligraphy, neon-lit minarets, and a high-tech prayer hall.

Modern Babylonian Mosque

Inspired by the ancient Mesopotamian civilization, this concept fuses elements of Babylonian and Assyrian architecture with modern design and technology. The mosque features hanging gardens, ziggurat-inspired structures, and sustainable features such as solar panels and green roofs.

Timurid Techno-Mosque

This concept pays tribute to the Timurid Empire’s architectural legacy while incorporating cutting-edge technology and futuristic design elements. The mosque features a blend of ornate tilework, domes, and minarets with interactive LED displays, smart lighting, and advanced acoustics.

Umayyad Avant-Garde Mosque

Taking inspiration from the Umayyad Caliphate’s architectural heritage, this concept fuses traditional design elements like courtyards, arcades, and geometric patterns with avant-garde materials such as glass, metal, and kinetic sculptures.

Fatimid Fusion Mosque

This mosque concept celebrates the rich history of the Fatimid Caliphate by combining traditional elements like muqarnas, calligraphy, and domes with futuristic design features, such as energy-efficient lighting, advanced air filtration systems, and adaptive building materials.

Abbasid Augmented Reality Mosque

Drawing from the Abbasid Caliphate’s architectural style, this concept integrates traditional Islamic design elements like vaults, arches, and courtyards with augmented reality.

Mamluk Metamorphic Mosque

This concept pays homage to the Mamluk Dynasty’s architectural heritage while incorporating cutting-edge metamorphic materials that change according to environmental conditions. The mosque’s design features stone and brick patterns, decorative calligraphy, and adaptive facades that respond to light and temperature.

Ayyubid Aerodynamic Mosque

Inspired by the Ayyubid Dynasty’s architectural style, this concept combines traditional elements like domes, minarets, and geometric patterns with aerodynamic design principles. The mosque features wind turbines, kinetic sculptures, and streamlined shapes that optimize energy efficiency.

Seljuk Space-Age Mosque

This concept draws from the Seljuk Empire’s architectural legacy and fuses it with space-age aesthetics and technology. The mosque features a blend of traditional design elements like brickwork, minarets, and domes with futuristic materials, such as carbon fiber and nanotechnology.

Ghaznavid Galactic Mosque

Paying tribute to the Ghaznavid Dynasty’s architectural style, this concept combines traditional Islamic design features like geometric patterns, arches, and calligraphy with a galactic theme. The mosque incorporates planetarium-inspired domes, star-like lighting, and holographic displays.

Disclaimer: Please note that the concepts, prompts, and images presented in this article have been generated using artificial intelligence and may not align perfectly with historical facts. This post is intended to showcase the creative capabilities of AI and provide an enjoyable exploration of architectural possibilities. Enjoy the imaginative journey while keeping in mind the limitations of AI-generated content.

The post 10 Imaginary Mosque Designs: Merging Rich History, Present, and Future Through AI-Inspired Concepts first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/474-10-imaginary-mosque-designs-merging-rich-history-present-and-future-through-ai-inspired-concepts/feed/ 0
Using CloudFront request headers to speed up page loading https://www.prayersconnect.org/256-using-cloudfront-request-headers-to-speed-up-page-loading/ https://www.prayersconnect.org/256-using-cloudfront-request-headers-to-speed-up-page-loading/#respond Fri, 06 Jan 2023 06:16:52 +0000 https://hq.prayersconnect.com/?p=256 When someone visits www.prayersconnect.com, a few things happen before the page is displayed. We know our visitors come to our site to get nearby mosque info. So we “do not show” (more on this later) a typical landing page but rather display their nearest mosques with the lowest friction. How does it happen? Once someone...

The post Using CloudFront request headers to speed up page loading first appeared on Prayers Connect.

]]>
When someone visits www.prayersconnect.com, a few things happen before the page is displayed. We know our visitors come to our site to get nearby mosque info. So we “do not show” (more on this later) a typical landing page but rather display their nearest mosques with the lowest friction.

How does it happen?

Once someone visits our site, we detect their IP address. Because we use Amazon CloudFront to serve our frontend traffic, we need to take extra care to make sure CloudFront forwards the user’s actual IP address. Otherwise, the IP address that we get in the origin server is an IP of the AWS systems. Here is the cloud function that is attached to Viewer Request to preserve the original IP.

function handler(event) {
    var request = event.request;
    var clientIP = event.viewer.ip;

    request.headers['x-user-ip'] = {value: clientIP};

    return request;
}

Once we receive the IP address, we geocode the IP address to get its coordinates, locality name etc. Once all these are retrieved, we redirect to a relevant page.

While all these are working very well so far, we got one problem. Geocoding IP an address takes time and the slowness is almost always embarrassingly noticeable. We were thinking to cache geocoding results. However, we prefer to maintain as less things as possible; especially when we have a better alternative.

CloudFront (CF) Request Headers

CloudFront has the ability to forward viewers’ approximate location info to the origin request. We decided to use this as we won’t have to maintain any caching infra for IP geocoding. As we use terraform to create the CF distribution, all we had to add the forwarding headers to the list like following

forwarded_values {
      query_string = true
      headers      = [
        "Authorization",
        "CloudFront-Viewer-Latitude",
        "CloudFront-Viewer-Longitude",
        "CloudFront-Viewer-Country-Region-Name",
        "CloudFront-Viewer-Country",
        "CloudFront-Viewer-Country-Name",
        "CloudFront-Viewer-City",
        "CloudFront-Viewer-Region-Name",
        "CloudFront-Viewer-Postal-Code",
      ]
    }

In our backend, we actually kept both mechanisms. First, we tap into this ready geo data. If they are not available, we fall-back to legacy IP geocoding. However, when geodata from CF is available, now the page is rendered almost instantly. I hope our users will appreciate this increased speed.

Notes on the landing page

We show landing pages in two cases:

  • We do not support the country (yet; as we’re working to be fully global)
  • We could not determine the user’s location. Note: For initial routing, we do not use the GeoLocation service.

What’s Next

Currently, the redirection happens from our server. It may be possible to redirect users directly from the cloud function which will improve the experience even further. We’ll investigate that soon.

If this sounds interesting or you have a better idea, consider joining our team as a volunteer.

The post Using CloudFront request headers to speed up page loading first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/256-using-cloudfront-request-headers-to-speed-up-page-loading/feed/ 0
5 lodash functions to write clean code https://www.prayersconnect.org/158-5-lodash-functions-to-write-clean-code/ https://www.prayersconnect.org/158-5-lodash-functions-to-write-clean-code/#respond Mon, 19 Dec 2022 16:00:34 +0000 https://hq.prayersconnect.com/?p=158 Lodash is one of the most downloaded node packages in the npm library. Basically, it is a utility library for JavaScript programming language. It facilitates JS’s functional programming with more than 200 functions which makes the workflow easier to work with arrays, objects, strings, numbers, etc. Here I have mentioned 5 of them, which I...

The post 5 lodash functions to write clean code first appeared on Prayers Connect.

]]>
Lodash is one of the most downloaded node packages in the npm library. Basically, it is a utility library for JavaScript programming language. It facilitates JS’s functional programming with more than 200 functions which makes the workflow easier to work with arrays, objects, strings, numbers, etc.

Here I have mentioned 5 of them, which I found very useful. It’ll also make your code cleaner with some one-liners rather than writing repetitive code.

First, let’s import lodash

import _ from 'lodash'

1. Find the minimum value

 _.minBy method is used to find the lowest value from the original array by iterating over each element in the array.

For example, suppose we have an array of objects of players with their scores in a match. Now we want to find the player with the lowest score. We can implement it like this:

const playersScore = [{ name: 'Jon yanke', type: 'batsman', run: 35},
                      {name: 'Braian Lara', type: 'batsman', run: 20},
                      {name: 'Stven Frankestine', type: 'batsman', run: 53},
                      {name: 'Scotti Kalihan', type: 'allrounder', run: 64}]

_.minBy(playersScore, function(o) { return o.run })
// output: { name: 'Braian Lara', type: 'batsman', run: 20 }

2. Find the maximum value

_.maxBy method is the opposite of the minBy method. It returns the biggest value. For the demonstration let’s consider the same collection/array (playersScore) from the previous example,

_.maxBy(playersScore, function(o) { return o.run })
// output: { name: 'Scotti Kalihan', type: 'allrounder', run: 64 }

It returned the object containing the highest run key value.

3. Find the unique array of objects

Using the _.uniqBy method, we can easily find unique objects by a key.

In the following example, we want to show the origin country of the visitors but the array of visitors contains the same country multiple times, as there are multiple users from the same country.
If we just map the array we might be repeating the same country multiple times. So with the uniqBy method, we can create a unique array by providing the key ‘country’ as the value.

const visitorsList = [{ country: "Australia", logInTime: '12:45:32UTC' },
                      { country: "Luxembourg", logInTime: '13:45:32UTC' },
                      { country: "Estonia", logInTime: '04:45:32UTC' },
                      { country: "Nepal", logInTime: '02:45:32UTC' },
                      { country: "Luxembourg", logInTime: '14:45:32UTC' },
                      { country: "Australia", logInTime: '10:45:32UTC' },]

_.uniqBy(visitorsList,'country' )

// output:[{ country: 'Australia', logInTime: '12:45:32UTC' },
           { country: 'Luxembourg', logInTime: '13:45:32UTC' },
           { country: 'Estonia', logInTime: '04:45:32UTC' },
           { country: 'Nepal', logInTime: '02:45:32UTC' }]

4. Check whether the object is empty or not

With the _.isEmpty, we can check whether a list, object, or set is empty or not.
Here, we are checking the book1 and book2 objects. If the object is empty it is going to return true otherwise false

let book1 = {};
console.log(_.isEmpty(book1)); //  output: true;
let book2 = { name: ‘Learning JavaScript };
console.log(_.isEmpty(book2)); // output: false;

5. Merge multiple arrays into one.

With the _.concat method, we can merge multiple arrays into a single array.

let array1 = [1, 5, 3, 1];
let array2 = [7, 25, 21];
let array3 = [11, 3, 3, 2];
let mergedArray = _.concat(array1, array2, array3);
console.log(mergedArray); // output: [1, 5, 3, 1, 7, 25, 21, 11, 3, 3, 2];

Conclusion

Lodash library can be used on the server and client-side seamlessly. It has many methods like the above five. In addition to these functions, at Prayers Connect we also heavily use some other lodash functions like map, assign, flatten, mapKeys, pick, startsWith, values, etc.

The post 5 lodash functions to write clean code first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/158-5-lodash-functions-to-write-clean-code/feed/ 0
RTK Query: Let’s go beyond minimal setup https://www.prayersconnect.org/137-rtk-query-lets-go-beyond-minimal-setup/ https://www.prayersconnect.org/137-rtk-query-lets-go-beyond-minimal-setup/#respond Sun, 23 Oct 2022 14:20:42 +0000 https://hq.prayersconnect.com/?p=137 Previously we saw how we can get started with RTK Query with a minimal setup. If you haven’t read that already, I would suggest you take a look at it. It would make it easy for you to understand this one. In this setup, we will go beyond our minimal setup and would learn some...

The post RTK Query: Let’s go beyond minimal setup first appeared on Prayers Connect.

]]>
Previously we saw how we can get started with RTK Query with a minimal setup. If you haven’t read that already, I would suggest you take a look at it. It would make it easy for you to understand this one. In this setup, we will go beyond our minimal setup and would learn some simple tricks. Click here to see the previous blog

I assume that you’re using Redux Toolkit to manage your global states, and not just RTK Query to manage your APIs. Here’s how you have to configure your store to use Redux Toolkit Query in conjunction with your global states.

Configure the store :

import { configureStore } from '@reduxjs/toolkit';
import { demo } from 'store/api/demoSlice';
import { setupListeners } from '@reduxjs/toolkit/dist/query';

export const store = configureStore({
  reducer: {
    yourStateSlice,
    demo
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat([
      demo.middleware,
    ])
});

setupListeners(store.dispatch);

Here, as you can see, it looks different than the usual setup. We’re adding the demo api slice as the rest but there are two extra thing (middleware and setupListeners). Let’s learn why we need these two.

The API slices are like other state slices. They hold their own state. But why do they need their own state? For the caching purpose that we talked about. Now, If they are similar, then why do we need these extra configurations? Because, they might be similar, but as you may know, when you fetch data from an API, you need to handle it asynchronously. You have to wait for the response. That’s why you can not use the API slices as regular slices. You need to add them to the middleware list so that they don’t interrupt your regular state management and also handle the responses asynchronously. Also, you need to add the setupListeners to listen for the actions being dispatched.

Multiple API slices:

Another part of this setup is to show you, how you can configure your store to have multiple API slices. It’s pretty simple. You just have to create another slice and add it to the reducer and middleware list as shown below,

import { configureStore } from '@reduxjs/toolkit';
import { demo } from 'store/api/demoSlice';
import { demo2 } from 'store/api/demo2Slice';
import { setupListeners } from '@reduxjs/toolkit/dist/query';

export const store = configureStore({
  reducer: {
    yourStateSlice,
    demo,
    demo2
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat([
     demo.middleware,
     demo2.middleware
   ])
});

setupListeners(store.dispatch);

See! really easy right? So, far we’ve configured the store but there’s one thing still remaining. That is to wrap up the root app with the provider. But wait, didn’t we do that previously? we don’t need to do that again. You’re right and also wrong at the same time. You’re right that we’ve already wrapped the root with the provider. But you’re also wrong cause we wrapped the root with the ApiProvider. You can only use API slices with that provider, not the state slices.

Yeah. Right. Then we’ll just wrap the root with the provider that we used to use for the state slices right?

No, we can not just use that. First, we have to remove the API provider and wrap the root only with the regular state provider because we’re already passing the API slices with the rest of the state slices. So we don’t need that anymore. The updated code should look like this,

import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from 'store/';
import Index from 'pages/'

function App() {
  return (
    <Provider store={store}>
      <Index />
    </Provider>
  );
 }

Just pass the store and you’re all done.

Now, as you have multiple API slices, please be sure to differentiate the API tags from each API slice. Because they might end up creating some unexpected behavior.

Here comes a trick :

How can you request multiple API calls that may require a response from another API?

Ans: You have to skip the next request conditionally. This might sound tricky but it’s actually easy. Here’s how to do it.

const {data, ...} = demo.useAnImportantQuery();
const {data: data2, ...} = demo.useAnothereImportantQuery({data.id},{skip: !data})

This way the second call will be skipped until you get a successful response from the first API or the data has no value.

The post RTK Query: Let’s go beyond minimal setup first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/137-rtk-query-lets-go-beyond-minimal-setup/feed/ 0
RTK Query: Make fetching and caching data effortless https://www.prayersconnect.org/86-rtk-query-make-fetching-and-caching-data-effortless/ https://www.prayersconnect.org/86-rtk-query-make-fetching-and-caching-data-effortless/#comments Tue, 04 Oct 2022 18:41:24 +0000 https://hq.prayersconnect.com/?p=86 RTK query which is the short form of Redux Toolkit Query is a data fetching and caching library that comes bundled with Redux Toolkit. If you’re a person using Redux Toolkit to manage your application global store then this is going to be the best go-to for you. There are a few alternatives to Redux...

The post RTK Query: Make fetching and caching data effortless first appeared on Prayers Connect.

]]>
RTK query which is the short form of Redux Toolkit Query is a data fetching and caching library that comes bundled with Redux Toolkit. If you’re a person using Redux Toolkit to manage your application global store then this is going to be the best go-to for you.

There are a few alternatives to Redux Toolkit. React-Query and Redux-Saga are amongst them. These libraries need a bit more configuration to work. Like, React Query wants you to use your choice of data fetching library (e.g Axios or fetch API). But RTK Query handles all of it for you. You just need to create an API module and add the required configurations. After you add the base configurations you’re ready to roll. Also, it makes handling loading states, and errors a lot easier. Now let’s see how we can implement RTK Query with minimal configuration.

First, we’ll see how you can just use the RTK Query without using Redux Toolkit in your project.

1. Create a store :

Create a folder named store at the root of your project.

Now, create a folder called reducers inside of it. To separate our API slices from global state slices let’s create a folder called API and we’ll create all the API slices inside of it. Now the slice config,

  • Slice with a single endpoint. Let’s name it to demo.
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

interface IReturn {
  key[string]: any;
}

interface IParams {
  key[string]: any;
}

export const demo = createApi({
  reducerPath: 'demo',
  baseQuery: fetchBaseQuery({
    baseUrl: 'https://baseurl.com'
  }),
  tagTypes: ['tag'],
  endpoints: (builder) => ({
    getSingleData: builder.query<IReturn, IParams>({
      query: ({ slug, id }) => `/${slug}/items/${id}`
    })
  })
});

You need to add reducerPath just like you need to do for reducer slices. Now, there are two types of builder methods. query and mutation. Queries are used when you’re not trying to mutate or change any data. For Get requests in short. And mutations are used to change, update, delete type actions. Also the tagTypes here are like groups and you’ll need it when you make mutations. We’ll see it downwards.

  • Here you can see there are two interfaces. These are only required for TypeScript projects. You need to specify the types of the parameters and the returns inside the angle brackets.
  • Now, you might need more than these. Like you might need to pass your auth token. For that you just have to configure it inside the baseQuery property. Here’s how to do it.
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

interface IReturn {
  key[string]: any;
}

interface IParams {
  key[string]: any;
}

const baseQuery = fetchBaseQuery({
  baseUrl: 'https://baseurl.com',
  prepareHeaders: (headers) => {
  const token = 'token';
    if (token) {
      headers.set('Content-Type', 'application/json');
      headers.set('Authorization', `Bearer ${token}`);
      headers.set('Cookies', token);
    }
    return headers;
  }
});

export const demo = createApi({
  reducerPath: 'demo',
  baseQuery: baseQuery,
  tagTypes: ['tag'],
  endpoints: (builder) => ({
    getSingleData: builder.query<IReturn, IParams>({
      query: ({ slug, id }) => `/${slug}/items/${id}`
    }),
    getAllData: builder.query<IReturn[], IParams>({
      query: ({ slug }) => `/${slug}/items`,
      providesTags: [{ type: 'tag', id: 'LIST' }]
    })
  })
});
  • We’re setting the headers if we find the token. The getAllData seems to be different right? It has an extra configuration providesTags. What is it doing here? Well, it is used to cache the fetched data. So, when you send a request using the same endpoint and the cache hasn’t been invalidated, it will return the cache instead. Which saves you from some API calls and makes the UI rendering look instant.
  • So far we’ve only seen how we can request queries. Let’s configure some methods to make mutations. Create, Update, Delete generally.
export const demo = createApi({
  reducerPath: 'demo',
  baseQuery: baseQuery,
  tagTypes: ['tag'],
  endpoints: (builder) => ({
    getSingleData: builder.query<IReturn, IParams>({
      query: ({ slug, id }) => `/${slug}/items/${id}`
    }),
    getAllData: builder.query<IReturn[], IParams>({
      query: ({ slug }) => `/${slug}/items`,
      providesTags: [{ type: 'tag', id: 'LIST' }]
    }),
    updateItem: builder.mutation<IReturn, IParam>({
      query: ({ slug, id, body }) => {
        return {
          url: `/${slug}/items/${id}`,
          method: 'PATCH',
          body
        };
      },
      invalidatesTags: [{ type: 'tag', id: 'LIST' }]
    }),
    deleteItem: builder.mutation<IReturn, IParams>({
      query: ({ slug, id }) => {
        return {
          url: `${slug}/items/${id}`,
          method: 'DELETE'
        };
      },
      invalidatesTags: [{ type: 'tag', id: 'LIST' }]
    })
  })
});
  • For mutations, we’re writing a bit more code. In the return statement of the query, you need to specify the URL, Method, and Body (optional). Just like you do in fetch or Axios, you pass the endpoint and method and body. Now, here’s an interesting thing. When you make mutations, you’re changing the data. So it means that the cache you have currently is old and stores old/wrong values. You need to update it. But first, you have to invalidate it so that it can be updated right! To do that, just add an extra property invalidatesTags and add the tags that it belongs to. Also, you don’t have to do it in the shown way. You can just add the tags directly inside the array like so,
invalidatesTags: ['tag']
  • After including this property, whenever you make changes, your cached data will be invalidated. Now you might think as your cache was invalidated, you need to somehow make another request to fetch new data and cache that again. Don’t worry. RTK query handles that for you too. Whenever a cache is invalidated it will automatically fetch new data with the method that provides that tag.
  • Now, will it run every one of the queries to fetch data? And when will it be executed? It will be executed when you visit a page where you have implemented a query method to fetch data. And all the loading and error handling will happen as the initial fetch.

This far we’ve only created an API slice. Now, let’s see how we can allow our project to use it. To do so, we need to wrap our root element with a provider and pass the API slice as a prop. Now here’s a thing. RTK suggests you use a single slice for the APIs but you’re not bound to do so. If you have a use case where you need to have separate API slices, you can do so. But it requires a bit more configuration. For now, let’s just focus on our minimal configuration.

2. Wrap with the provider :

import React from 'react';
import ReactDOM from 'react-dom/client';
import { ApiProvider } from '@reduxjs/toolkit/query/react';
import { demo } from 'store/api/demoSlice';
import Index from 'pages/'

function App() {
  return (
    <ApiProvider api={demo}>
      <Index />
    </ApiProvider>
  );
 }

To provide your slices you just have to import the exported slice from the store and pass it into the ApiProvider as the value of the api prop. For the sake of this minimal setup, assuming that you have only one API slice for your whole project. You’re now done here. Now let’s see how we can consume our API methods. And here comes the interesting part.

3. Consuming the API methods.

import { demo } from 'store/api/demoSlice';

function Todos = () => {
const { data, isLoading, error } = demo.useGetAllDataQuery()
const [deleteItem, status] = demo.useDeleteItemMutation()

const handleDelete = async (id) => {
  const res = await deleteItem(id);
};

if(isLoading){
  return <div>Loading. Please wait!</div>
}

if(isError){
  return <div>Sorry! Unable to load data.</div>
}

return (
  <div>
    {data.map(item => {
       return <div key={item.id} onClick={handleDelete}>{item.title}</div>
     })}
  </div>)
}

export default Todos;

Wait! What? Where do these useGetAllDataQuery and useDeleteItemMutation methods come from? The methods we created are different!

Looks different but at the same time similar right? Well, you’re right. Cause we did not create these methods but they sound familiar cause these are the ones we created with a bit of tweaking. RTK Query prefixed our methods with the keyword use and postfixed with the type of the method, either Query or Mutation. And to use those methods we need to write them this way.

Now, as you can see I’ve imported the demo module from demoSlice and used the endpoints. Here, for queries, it returns an object with some props like data, isLoading, isError, error, etc. These props are self-explanatory. To use the data you need to wait until the data is loaded. Till then show a loading message and error message for any kind of error.

For the mutation, you get a tuple in return. The first property is the handler and the second one is the status. You can also show loading states based on the status of the mutation. now you just have to call the handler passing the right param(s). And after the successful execution, the data will get invalidated and fetched new data automatically.

You can also refetch manually. Query methods return a property called refetch. It’s a function and you can just call it like other functions on a click event may be to refetch. All other API methods can be used in the same way.

Now you’re ready to jumpstart yourself and go beyond this minimal config.

Few things you should know :

  • You can make lazy queries too. What I mean is that with the way we’re consuming the endpoints now, the queries happen on page load automatically. But you might wanna do it only on a certain use case. To do that you can make lazy queries. The code will look like this,
const [ fetchAllData, {data, isLoading, isError} ] = demo.useLazyGetAllDataQuery()

Now just call the fetchData method as/when you please.

  • You need async await only when a method returns a tuple.
  • You can cache each element of an array with the same tag type but different IDs like so,
providesTags: (result, error, arg) => result 
? [...result.map(({ id }) => ({ type: 'tag', id })), 'tag'] 
: ['tag']

Invalidating will be similar.

invalidatesTags: (result, error, arg) => [{ type: 'Post', id: arg.id }]

The post RTK Query: Make fetching and caching data effortless first appeared on Prayers Connect.

]]>
https://www.prayersconnect.org/86-rtk-query-make-fetching-and-caching-data-effortless/feed/ 1