Contributing - lekakid/ArcaRefresher GitHub Wiki

개발 환경을 준수바랍니다.

프로젝트 폴더 구조

image

  • component: 여러곳에서 공통으로 사용되는 컴포넌트 모음
  • core: 리프레셔 구현에 필요한 스트링, Redux Store 등 모음
  • feature: 각종 기능 컴포넌트 모음
  • func: 함수 모음
  • menu: 공통된 인터페이스를 제공하기 위한 컴포넌트 모음
  • hooks: 리액트 커스텀 훅 모음

기능 폴더의 구조

image

{GroupName}
|-- {FeatureName}
|   |-- ArticleMenu.jsx
|   |-- ConfigMenu
|   |   |-- View.jsx
|   |   `-- index.jsx
|   |-- ContextMenu
|   |   |-- View.jsx
|   |   `-- index.jsx
|   |-- Feature.jsx
|   |-- FeatureInfo.jsx
|   `-- slice.jsx
|-- {FeatureName}
|   |-- ...

각 기능 컴포넌트와 메뉴 구현 컴포넌트는 feature/index.jsxmenu/index.jsx에서 Webpack context 기능을 통해 자동으로 포함됩니다.

주의사항

  • /ArticleMenu, /ConfigMenu, /ContextMenu, /Feature, /slice.jsx 의 이름을 바꿔선 안됩니다.
  • Group이 서로 다른 기능이더라도 이름이 같아선 안됩니다.

설정 데이터의 관리

💡 redux slice의 기본적인 작성법은 Redux Toolkit 문서를 참고바랍니다.

각 기능들의 설정 데이터는 slice.jsx의 리듀서 정의를 통해 관리됩니다.
defaultStorage 오브젝트를 통해 유저스크립트 확장 프로그램이 관리할 데이터를 정할 수 있습니다.

const defaultStorage = {
  myNewConfig: 'hello!',
};

defaultStorage는 이후 initialState.storage에 할당합니다.

🚫 할당하는 이름(storage)을 바꾸면 안됩니다.

const initialState = {
  storage: getValue(Info.ID, defaultStorage),
  // 이 아래에 인터페이스 제어용 state 추가
  show: true,
};

storage를 제어하는 리듀서는 이름이 $로 시작해야합니다. $로 시작하는 리듀서를 호출할 때 마다 저장할 데이터에 변화가 있다고 판단합니다. 자세한 구조는 core/storage.jsxcreateMonkeySyncMiddleware를 참고바랍니다.

export const slice = createSlice({
  name: Info.ID,
  initialState,
  reducers: {
    // 아래와 같이 추가합니다.
    toggleShow(state) {
      state.show = !state.show;
    },
    // storage를 제어하는 리듀서는 탭간 동기화 구분을 위해 이름을 $로 시작해야합니다!!
    $setMyNewConfig(state, action) {
      state.storage.myNewConfig = action.payload;
    },
  },
});

설정 데이터의 업데이트

const defaultStorage = {
  version: 1,
  contextRange: 'articleItem',
  variant: 'badge',
  memo: {},
};

function formatUpdater(storage, defaultValue) {
  // version 0 => 1
  const version = storage?.version || 0;

  switch (version) {
    case 0: {
      const memo = Object.fromEntries(
        Object.entries(storage.memo).map(([key, msg]) => [key, { msg }]),
      );
      const data = getValue('UserColor');
      if (data) {
        Object.entries(data.color).forEach(([key, color]) => {
          (memo[key] ??= {}).color = color;
        });
        deleteValue('UserColor');
      }

      const updateStorage = { ...storage };
      updateStorage.memo = memo;
      updateStorage.version = 1;
      return updateStorage;
    }
    default:
      console.warn('지원하지 않는 버전 데이터입니다.', storage);
      return defaultValue;
  }
}

const initialState = {
  storage: getValue(Info.ID, defaultStorage, formatUpdater),
};

getValue의 3번째 인자로 formatUpdater함수를 받을 수 있습니다. 저장 데이터 내에 version 값을 지정하고 이 값에 따라 데이터를 필요에 따라 변환할 수 있습니다. 변환 전 데이터는 내부 저장소에 {FeatureName}_v{version} 백업데이터로 남습니다.