Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ const baseConfig = {
android: {
package: 'com.mannomi.soundlog',
adaptiveIcon: {
backgroundColor: '#E6F4FE',
foregroundImage: './assets/soundlog-logo.png',
backgroundColor: '#070B1F',
foregroundImage: './assets/android-icon-foreground.png',
backgroundImage: './assets/android-icon-background.png',
monochromeImage: './assets/android-icon-monochrome.png',
},
Expand All @@ -122,11 +122,20 @@ const baseConfig = {
},
web: {
bundler: 'metro',
favicon: './assets/soundlog-logo.png',
favicon: './assets/favicon.png',
},
plugins: [
'expo-router',
'expo-font',
[
'expo-splash-screen',
{
backgroundColor: '#070B1F',
image: './assets/splash-icon.png',
imageWidth: 390,
resizeMode: 'contain',
},
],
[
'expo-camera',
{
Expand Down
3 changes: 3 additions & 0 deletions app/(tabs)/my.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ScrollView, View } from 'react-native';
import { meApi } from '@/api/meApi';
import { useNearbyPlacesQuery } from '@/api/tourQueries';
import { AppText } from '@/components/AppText';
import { AccountActionsSection } from '@/components/my/AccountActionsSection';
import { AuthAccountCard } from '@/components/my/AuthAccountCard';
import { MySettingsRow } from '@/components/my/MySettingsRow';
import { PermissionSettingsCard } from '@/components/my/PermissionSettingsCard';
Expand Down Expand Up @@ -228,6 +229,8 @@ export default function MyScreen() {
/>
</View>
) : null}

<AccountActionsSection />
</ScrollView>
</Screen>
);
Expand Down
10 changes: 9 additions & 1 deletion app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import '../global.css';

import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { StatusBar } from 'expo-status-bar';

import { AppEntryGate } from '@/components/auth/AppEntryGate';
import { AppProviders } from '@/providers/AppProviders';

void SplashScreen.preventAutoHideAsync().catch(() => undefined);

export default function RootLayout() {
return (
<AppProviders>
<StatusBar style="light" />
<AppEntryGate />
<Stack screenOptions={{ headerShown: false }}>
<Stack
screenOptions={{
contentStyle: { backgroundColor: '#070B1F' },
headerShown: false,
}}
>
<Stack.Screen name="auth/login" />
<Stack.Screen name="auth/callback" />
<Stack.Screen name="legal/privacy" />
Expand Down
2 changes: 1 addition & 1 deletion app/auth/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export default function LoginScreen() {
}`}
>
{hasAcceptedRequiredTerms ? (
<Feather color="#4A1D96" name="check" size={15} />
<Feather color="#6EA8FF" name="check" size={15} />
) : null}
</View>
<AppText className="min-w-0 flex-1 text-xs leading-5 text-white/65">
Expand Down
Binary file modified assets/android-icon-background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/android-icon-foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/android-icon-monochrome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/soundlog-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/splash-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions docs/design-system/SOUNDLOG_RN_DESIGN_SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,15 @@ import {
| Card | `#080D18` | `bg-soundlog-card` |
| Elevated card | `#090E1B` | `bg-soundlog-elevated` |
| Chip | `#171B2A` | `bg-soundlog-chip` |
| Selected / CTA | `#B7E628` | `bg-soundlog-selected`, `bg-soundlog-lime` |
| Selected surface | `#214A73` | `bg-soundlog-selected` |
| Brand highlight | `#B7E628` | `bg-soundlog-lime` |
| Focus border | `#B7E628` | `border-soundlog-focus` |
| Chip border | `#364283` | `border-soundlog-border` |
| Info blue | `#6EA8FF` | `text-soundlog-blue` |
| Accent purple | `#7A2CFF` | `text-soundlog-purple` |
| Accent blue | `#6EA8FF` | `text-soundlog-blue` |
| Accent gold | `#B1913A` | `text-soundlog-gold` |
| Warning action | `#FF8A3D` | `bg-soundlog-warning` |
| Primary action | `#4A1D96` | `bg-soundlog-action` |
| Primary action | `#315F9F` | `bg-soundlog-action` |
| Danger action | `#8E310D` | `bg-soundlog-danger` |
| Inverse text | `#FFFFFF` | `text-soundlog-inverse` |

Expand Down
2 changes: 1 addition & 1 deletion docs/frontend/MAIN_PAGE_RN_BUILD_DOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ RN에서는 다음 방식 중 하나를 사용한다.
| `border.chip` | `#364283` | 칩 테두리 |
| `text.primary` | `#FFFFFF` | 주요 텍스트 |
| `text.secondary` | `rgba(255,255,255,0.68)` | 보조 텍스트 |
| `accent.purple` | `#7A2CFF` | 진행바/선택 포인트 |
| `accent.blue` | `#6EA8FF` | 진행바/선택 포인트 |
| `accent.gold` | `#B1913A` | 추천 카드 |
| `player.background` | `#45343D` | 미니 플레이어 |

Expand Down
2 changes: 1 addition & 1 deletion docs/frontend/RECAP_SHARE_PAGE_RN_BUILD_DOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Figma 배경은 어두운 그라데이션과 중앙 glow가 있다. RN에서는
| 토큰 | 값 | 용도 |
| --- | --- | --- |
| `background.primary` | `#050916` | 기본 배경 |
| `background.deepPurple` | `#160F27` | 그라데이션 |
| `background.deepNavy` | `#101A38` | 그라데이션 |
| `background.brownGlow` | `#2C1B33` | 보조 그라데이션 |
| `text.primary` | `#FFFFFF` | 타이틀 |
| `text.secondary` | `#C1C1C1` | 날짜/시간 |
Expand Down
71 changes: 71 additions & 0 deletions docs/implementation/2026-09-05-notion-qa-clear-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Notion QA 전체 해소 계획

## 작업 목표

Notion QA 이미지에서 확인된 화면 문제를 GitHub 이슈와 연결하고 앱 코드와 에셋을 수정한 뒤 iOS Simulator에서 완료 조건을 확인한다.

현재 제공된 Notion 페이지 `3d2bd7e4-3465-804a-a262-d56fd55c81a9`는 연결된 `만욱` 워크스페이스에서 접근할 수 없다. 따라서 이번 단계에서는 MCP로 읽을 수 있는 기존 SoundLog QA 원문 `3ce7cf19-a364-80fa-ac25-f2e9127b0765`와 GitHub 이슈 #29부터 #36을 근거로 처리한다. 새 페이지 권한이 열리면 이미지를 다시 수집하고 누락 이슈를 추가한다.

## 이슈별 현재 상태

### #29 브랜드 포인트 컬러

중앙 토큰과 여러 화면의 직접 지정값에 `#4A1D96`과 `#7A2CFF`가 남아 있다. 선택 배경은 어두운 블루로 바꾸고 주요 행동은 흰색 글자를 읽을 수 있는 블루로 바꾼다. 라임은 테두리와 현재 상태 강조에 사용한다.

### #30 바텀시트 전환

여행 모드 시트의 딤과 본문이 같은 컨테이너에서 표시된다. 딤의 페이드와 본문의 아래쪽 진입 애니메이션을 분리한다. 배경 탭과 닫기 버튼과 아래 드래그는 같은 닫기 애니메이션을 사용한다.

### #31 리캡 선택 버튼 글자

현재 템플릿과 공개 범위와 무드 버튼은 12pt와 44pt 터치 영역을 사용한다. 수정 후에도 이 조건을 유지하고 작은 화면에서 잘림 여부를 Simulator로 확인한다.

### #32 리캡 작성 흐름

현재 상단에는 뒤로가기와 제목만 있고 저장 행동은 긴 화면의 맨 아래에만 있다. 상단 오른쪽에 저장 행동을 추가한다. 4:5 미리보기를 먼저 보여주고 템플릿 선택을 미리보기 바로 아래에 배치해서 게시물 작성 흐름으로 정리한다. 장소와 공개 범위와 음악과 여행 상태 계약은 바꾸지 않는다.

### #33 계정 행동 위치

로그아웃과 계정 삭제가 상단 계정 카드 안에 있다. 상단에는 프로필 또는 로그인 진입만 남기고 인증된 계정의 로그아웃과 삭제는 서비스와 개발 도구 다음 화면 최하단으로 옮긴다. 기존 확인 알림과 실패 메시지는 유지한다.

### #34 마이 화면 가독성

권한 행동 라벨이 11pt이고 하단 탭 라벨도 11pt다. 둘 다 12pt로 올린다. 설정 설명은 12pt를 유지하면서 흰색 투명도를 높이고 긴 장소명은 최대 두 줄로 보여준다.

### #35 앱 아이콘

iOS 아이콘만 라임 LP와 지도 마커 조합으로 교체됐고 Android 적응형 아이콘과 파비콘에는 이전 로고가 남아 있다. 현재 iOS 아이콘을 기준으로 Android 전경과 배경과 단색 마스크와 파비콘을 다시 만든다. iOS 아이콘은 1024 정사각형 RGB 상태를 유지한다.

### #36 스플래시

현재 스플래시 에셋은 Apple 템플릿 격자 이미지다. Figma 325:745의 네이비 배경과 라임 오로라와 흰색 SoundLog 워드마크를 재현한 에셋으로 교체한다. Expo 스플래시 설정과 초기 상태 표시줄을 같은 네이비 배경과 밝은 스타일로 맞춘다.

## 구현 순서

1. 색상 토큰과 직접 지정된 보라색을 블루와 라임 팔레트로 교체한다.
2. 공통 닫기형 바텀시트 훅에 딤과 본문의 진입과 종료 애니메이션을 분리한다.
3. 리캡 작성 화면과 마이 화면을 완료 조건에 맞게 재배치한다.
4. 앱 아이콘과 스플래시 에셋을 생성하고 Expo 설정을 갱신한다.
5. 타입 검사와 전체 테스트와 Expo 검사를 실행한다.
6. iPhone Simulator에서 지도와 음악추천과 리캡과 마이 화면을 검수하고 스플래시를 앱 재실행으로 확인한다.
7. PR을 만들고 각 이슈에 파일과 테스트와 화면 근거를 연결한다.

## 계획 자체 검토

- 기존 리캡과 로그 데이터 계약은 변경하지 않는다.
- 서버 저장소와 운영 서버는 변경하지 않는다.
- 기본 사용자 텍스트에 검정색이나 진한 보라색을 추가하지 않는다.
- 에셋 교체는 iOS와 Android와 웹 설정을 함께 확인한다.
- GitHub 이슈는 main에 반영되고 Simulator 검증 근거가 생긴 뒤에만 종료한다.
- 제공된 새 Notion 페이지를 읽지 못한 상태에서 기존 QA 범위를 전체 범위로 오인하지 않는다.

## 검증 결과

- `npm run check`로 타입 검사와 브랜드 에셋 검사와 이미지 오류 처리 검사와 리캡 지도 군집 검사와 Expo Doctor를 통과했다.
- `npm test -- --run`으로 테스트 파일 9개와 테스트 57개를 통과했다.
- iPhone 16 Pro iOS 18.0 Simulator에서 지도 팔레트와 여행 모드 시트와 사진 없는 리캡 작성 화면과 마이 화면을 확인했다.
- 여행 모드 시트의 딤은 화면에 고정되고 시트 본문만 아래에서 올라왔다. 배경을 누르면 같은 닫기 애니메이션으로 종료됐다.
- 리캡 작성 화면은 상단 저장 버튼과 4:5 미리보기와 미리보기 바로 아래 게시물 스타일 선택 순서로 표시됐다. 공개 범위와 무드 버튼은 접근성 트리에서 각각 별도 버튼으로 확인됐다.
- 마이 화면 접근성 트리에서 프로필 카드 다음에 설정과 권한과 서비스와 개발 도구가 나오고 로그아웃과 계정 삭제가 마지막 계정 섹션에 배치된 것을 확인했다.
- 스플래시 에셋과 Expo 설정은 정적 검사로 확인했다. 디버그 실행은 `Refreshing` 오버레이가 스플래시를 가려 영상 판정에 사용할 수 없었다. 릴리스 Simulator 빌드는 소스 오류가 아니라 로컬 디스크 공간 부족으로 중단됐으므로 배포 빌드 전 재검증이 필요하다.
- 새 Notion 페이지는 같은 연결에서 세 번 다시 읽었으나 모두 `object_not_found` 404가 반환됐다. 공유 권한이 열리기 전에는 새 이미지에 대한 누락 이슈가 없다고 보장할 수 없다.
1 change: 0 additions & 1 deletion docs/product/SOUNDLOG_TRAVEL_SOUNDTRACK_WIREFRAMES.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
--lime-soft: rgba(183, 230, 40, 0.12);
--blue: #6ea8ff;
--blue-soft: rgba(110, 168, 255, 0.14);
--purple: #7a2cff;
--gold: #b1913a;
--warning: #ff8a3d;
--warning-soft: rgba(255, 138, 61, 0.14);
Expand Down
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"expo-router": "~56.2.20",
"expo-secure-store": "~56.0.4",
"expo-sharing": "~56.0.26",
"expo-splash-screen": "~56.0.15",
"expo-status-bar": "~56.0.4",
"nativewind": "^4.2.4",
"react": "19.2.3",
Expand Down Expand Up @@ -62,6 +63,7 @@
"test": "vitest run",
"doctor": "npx expo-doctor",
"check:api-origin": "node scripts/check-api-origin.js",
"check:brand-qa": "node scripts/check-brand-qa.js",
"check:image-components": "node scripts/check-image-components.js",
"check:deployed-web": "node scripts/check-deployed-web.js",
"check:server-web-export": "node scripts/check-server-web-export.js",
Expand All @@ -70,7 +72,7 @@
"check:recap-clustering": "node scripts/check-recap-map-clustering.js",
"check:server-contract": "node scripts/check-server-api-contract.js",
"check:vercel-config": "node scripts/check-vercel-config.js",
"check": "npm run typecheck && npm run check:image-components && npm run check:recap-clustering && npm run doctor",
"check": "npm run typecheck && npm run check:brand-qa && npm run check:image-components && npm run check:recap-clustering && npm run doctor",
"prepare": "sh scripts/install-git-hooks.sh"
},
"private": true,
Expand Down
106 changes: 106 additions & 0 deletions scripts/check-brand-qa.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const fs = require('node:fs');
const path = require('node:path');

const root = path.resolve(__dirname, '..');
const forbiddenTokens = ['#4A1D96', '#7A2CFF', '#8B72FF', 'soundlog-purple'];

function walkSource(directory) {
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const filePath = path.join(directory, entry.name);

if (entry.isDirectory()) {
return walkSource(filePath);
}

return /\.(?:js|ts|tsx)$/.test(entry.name) ? [filePath] : [];
});
}

function assertBrandPalette() {
const files = [
...walkSource(path.join(root, 'app')),
...walkSource(path.join(root, 'src')),
path.join(root, 'tailwind.config.js'),
];
const violations = files.flatMap((filePath) => {
const contents = fs.readFileSync(filePath, 'utf8');
return forbiddenTokens
.filter((token) => contents.includes(token))
.map((token) => `${path.relative(root, filePath)}: ${token}`);
});

if (violations.length > 0) {
throw new Error(
`보라색 브랜드 토큰이 남아 있습니다.\n${violations.join('\n')}`,
);
}
}

function readPngInfo(relativePath) {
const buffer = fs.readFileSync(path.join(root, relativePath));
const signature = buffer.subarray(0, 8).toString('hex');

if (signature !== '89504e470d0a1a0a') {
throw new Error(`${relativePath} 파일이 PNG가 아닙니다.`);
}

return {
colorType: buffer.readUInt8(25),
height: buffer.readUInt32BE(20),
width: buffer.readUInt32BE(16),
};
}

function assertPng(relativePath, expected) {
const actual = readPngInfo(relativePath);

if (
actual.width !== expected.width ||
actual.height !== expected.height ||
(expected.colorType !== undefined &&
actual.colorType !== expected.colorType)
) {
throw new Error(
`${relativePath} 규격이 올바르지 않습니다. expected=${JSON.stringify(expected)} actual=${JSON.stringify(actual)}`,
);
}
}

function assertAppAssets() {
assertPng('assets/icon.png', { colorType: 2, height: 1024, width: 1024 });
assertPng('assets/android-icon-foreground.png', { height: 512, width: 512 });
assertPng('assets/android-icon-background.png', {
colorType: 2,
height: 512,
width: 512,
});
assertPng('assets/android-icon-monochrome.png', { height: 432, width: 432 });
assertPng('assets/favicon.png', { height: 48, width: 48 });
assertPng('assets/splash-icon.png', { height: 1024, width: 1024 });

const config = require(path.join(root, 'app.config.js'))();
const splashPlugin = config.plugins.find(
(plugin) => Array.isArray(plugin) && plugin[0] === 'expo-splash-screen',
);

if (!splashPlugin || splashPlugin[1]?.image !== './assets/splash-icon.png') {
throw new Error('Expo 스플래시 플러그인이 새 에셋을 사용하지 않습니다.');
}

if (
config.android?.adaptiveIcon?.foregroundImage !==
'./assets/android-icon-foreground.png'
) {
throw new Error(
'Android 적응형 아이콘이 새 전경 에셋을 사용하지 않습니다.',
);
}

if (config.web?.favicon !== './assets/favicon.png') {
throw new Error('웹 파비콘이 새 브랜드 에셋을 사용하지 않습니다.');
}
}

assertBrandPalette();
assertAppAssets();
console.log('Soundlog 브랜드 팔레트와 앱 아이콘 및 스플래시 규격 확인 완료');
6 changes: 3 additions & 3 deletions src/components/SettingsRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function SettingsRow({
</AppText>
{description ? (
<AppText
className="mt-1 text-xs leading-5 text-white/42"
className="mt-1 text-xs leading-5 text-white/62"
numberOfLines={2}
>
{description}
Expand All @@ -62,8 +62,8 @@ export function SettingsRow({
) : null}
{rightText ? (
<AppText
className="ml-3 max-w-[44%] text-right text-sm text-white/46"
numberOfLines={1}
className="ml-3 max-w-[44%] text-right text-sm leading-5 text-white/62"
numberOfLines={2}
>
{rightText}
</AppText>
Expand Down
Loading
Loading