- A dynamic environment, rapid decision-making, and a deep love for startups are what fuel my professional passion

 

- Worked as a sysadmin for 3 years - I have steel nerves and poker face)

Software Engineer at Blinkin.io

~10yrs of experience

First step - discovery

- 2-5 hours calls with C-level

- analyzing similar products (engineering blogs, articles)

- drawing schemas/prototypes (Miro)

- Functional and Non-functional requirements

# CHAPTER 1

Research

# CHAPTER 2

Node-based Editor

Realtime Collaboration

Drag&Drop & Resize & Rotate

MultiSelection & Grouping

Powerful inline Text/Image Editor

Themes & Templates

Tenant-based architecture

Data governence

Preview inside Editor

Publish

Ctrl + Z

History

ChatGPT / AI / ML

Forms

Integrations

HTML vs Canvas

PDF -> Guide

Platforms/Services

# CHAPTER 2

Next step - Validate decisions

# CHAPTER 3

Time - 1 month

... and create ADRs

# CHAPTER 3
Architectural Decision Record

Now let's make this ship happen!

General Overview

# CHAPTER 4

React app structure

# CHAPTER 5

Feature-Sliced Design + Atomic Design

Entity - "Component"

(Segmentation)

# CHAPTER 5

Hierarchy

# CHAPTER 5

BE-FE communication

# CHAPTER 6

Incremental change update

State Management - 1 part

# CHAPTER 7

State Management - 2 part

# CHAPTER 7

- Caching

- Updating "out of date" data in the background

- Managing memory - revalidate, refetch by Query Keys

- Make your application more maintainable

- Potentially help you save on bandwidth

State Management - ReactQuery

# CHAPTER 7
const useFonts = useQuery({
  queryKey: [QUERY_KEYS.FONTS],
  queryFn: async () => FontsAPI.fetchFonts().then(d => data.data)
});

const { data, isLoading } = useFonts();

....

export const useAddNewFontMutation = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (payload: AddNewFontRequest) => FontsAPI.addNewFont(payload),
    onSuccess: () => queryClient.invalidateQueries([
      QUERY_KEYS.FONTS
    ]);
  });
}

UUID v4 as fundamental attribute

# CHAPTER 8

Text Editing

# CHAPTER 10

Made by Facebook

An extensible text editor framework

Reactflow

# CHAPTER 11

 for building node-based editors

Online collaboration with Yjs

# CHAPTER 12

Shared editing framework

Offline Support
Network agnostic (Peer-to-Peer)
CRDT implementation
Shared cursors

CRDT

# CHAPTER 12

Conflict-free

Replicated

Data

Types

=

W
T
F

CRDT in Yjs

# CHAPTER 12

Example

import * as Y from 'yjs';

const ydoc = new Y.Doc();
const nodesMap = ydoc.getMap<Node>('nodes');

function useNodesStateSynced(): [Node[], OnNodesChange] {
    const [nodes, setNodes] = useState<Node[]>([]);

    const onNodesChanges = useCallback((changes: NodeChange[]) => {});

    useEffect(() => {
        const observer = () => setNodes(Array.from(nodesMap.values()));

        setNodes(Array.from(nodesMap.values()));
        nodesMap.observe(observer);

        return () => nodesMap.unobserve(observer);
    }, [setNodes]);

    return [nodes, onNodesChanges];
}
# CHAPTER 12

Floating / Smart positioning

# CHAPTER 13

Tree-shakeable & Platform-agnostic

Smart Anchor Positioning

Floating / Smart positioning

# CHAPTER 13
    const { refs, update, floatingStyles } = useFloating({
        placement: placement ?? 'top',
        open: opened,
        onOpenChange: setOpened,
        middleware: [
            flip({
                fallbackAxisSideDirection: 'end',
                fallbackStrategy: 'bestFit',
                fallbackPlacements: ['top', 'bottom', 'left', 'right'],
            }),
            offset(15)
        ],
    });

Drag/Resize/Rotate/Snap/MultiSelect

# CHAPTER 14
# CHAPTER 14

User Buffer

# CHAPTER 13

User Buffer

# CHAPTER 13
const data = {
  components: [{
    "id": "98268bd4-568c-4a5a-8751-109e6ff05ef8",  
    "data": {
      "media": { id: "..", path: "https://example.com/photos/2280547.jpeg" },
      "order": 0,
    },
    ...
  }],
};

// Convert all medias to Base64
toBase64(data.components.media);

navigator.clipboard.writeText(createClipboardDataFromJSON(data));
// {
//   type: "data/clipboard",
//   payload: { components: [...] }
// }

Testing

# CHAPTER 15

"Simplicity is not the state of when there is nothing more to add, but the state when there is nothing more to take away."

(с)Antoine de Saint-Exupéry

In the Lab: Crafting the No-{Code} Editor POC

By Oleksandr Maliuta

In the Lab: Crafting the No-{Code} Editor POC

  • 176