React Native ile İlginç Bilgiler Uygulaması, 'BiBilgi' Açık Kaynak Projesi

bibilgireact-nativejavascriptyenibirbaslangic

1 yıl önce 4 yorum

Merhabalar, yaklaşık mayıs 2017’de başlanan bir projeden bahsedeceğim. Proje, arkadaşımın kendisinde bulunan yaklaşık 900 adet bilgiyi değerlendirmek istemesiyle başladı. API üzerinden kullanıcılara, kullanıcıların istediği aralıkta push notification ile bir adet bilgi atması mantığına dayanıyor. Uygulama başlangıçta ionic ile yazılmıştı fakat istenilen hıza ulaşamadığımız için ve bir nevi başka insanların öğrenmesi için bu projeyi React Native için yazalım dedik. Bu makalede uygulamanın baştan sona neler kullandığı ve ne bağımlılıklara ihtiyacı olduğunu anlatacağım.

Uygulamanın Görüntüleri:

simulator1 simulator2 simulator3 simulator4

İş listemiz şöyle:

Projeyi Oluşturma ve Klasör yapısı

Uygulamayı oluşturmak için şu komutu kullanıyoruz:

react-native init BiBilgi

Ardından cd BiBilgi diyerek dizine geçiyoruz. Uygulama oluştu ve bu dizine şu yapıyı ekliyoruz:

- BiBilgi
    • index.android.js
    • index.ios.js
    - src
        - assets
            - images
            - style
        - components
        - lib
            - common
            - providers
            - services
        - pages
        - tabs
        • index.js

hızlıca bu yapıyı oluşturmak için BiBilgi dizininde şu komutu çalıştırabilirsiniz:

mkdir src && cd src && mkdir assets && cd assets && mkdir images && mkdir style && cd .. && mkdir components && mkdir lib && cd lib && mkdir common && mkdir providers && mkdir services && cd .. && mkdir pages && mkdir tabs && cd ..

Not: Bu komutların ardından Unix/Linux için index.js’i şu komutla oluşturabilirsiniz:

touch index.js

Windows için sağ tık yeni bla bla bla.

Sonra index.ios.js ve index.android.js dosyalarının içindeki her şeyi şu komut ile değiştiriyoruz:

import './src/index.js';

Bu src/index.js dosyasını çağıracak ve hem android hem ios için tek bir dosyadan yönetim sağlayacak.

Neden bu klasör yapısı?

Normalde kendine ait belirli bir klasör yapısı yok. Topluluğun önerdiği yapılar var fakat bu yapı bunları kullanmıyor. Açıkçası topluluğun kullandığı yapılar dosyalar arttıkça ve işlemler arttıkça daha fazla büyüme oluşturuyor (benim gördüklerim arasında). Büyümeden kastım uygulama içinde artan sayfa ve action sayısı bir o kadar kontrol gerektiriyor. Fakat bu yapıda sayfalar ve sayfalarda kullanılan tablar belli ve çoğu şeyi aynı yerden yönetiyorsunuz. Peki hangisi iyi olan? Tabi ki topluluğun kullandığı yapı. Bu yapının (en azından benim için) küçük uygulamalarda çok fazla kontrol yapmadan hızlıca uygulamayı bitireceğine inanıyorum. Siz tabi ki de istediğiniz yapıyı kullanabilirsiniz.

Bu yapıdan bahsedecek olursak kısa açıklamalar olacak şekilde:

- assets 'uygulamanın görünümü ile ilgili işlemleri barındırır'

- components 'uygulama içinde özelleştirilmiş viewlar barınır'

- lib 'uygulamanın yardımcı fonksiyonları'

    - common 'her dosyanın rahatlıkla erişebileceği işlemler'

    - providers 'servislerin ve modellerin erişebileceği sağlayıcılar'

    - services 'modelleri işleyen yardımcılar'

- pages 'sayfalar'
    - tabs 'sayfaların kullandığı tablar'

Çok detaylı bir şekilde anlatmadım. Yalnız burada her klasörün altına index.js adında ana yönlendirici eklemek gerekecek yeri geldiğinde değineceğim.

Dosyalar ve Yardımcı Fonksiyonlar

İlk önce uygulama ayarlarımızı tanımlayalım. src/lib/common/ yoluna config.js adında bir dosya oluşturuyoruz.

/**
 * @providesModule @bibilgi/config
 */

class Config {
    constructor() {
        this.default();
    }

    default() {
        this.API_URL = 'http://bibilgi.kodofisi.com';

        this.PUSH_NOTIFICATION = {
            senderID: "441144114411",
            permissions: {
                alert: true,
                badge: true,
                sound: false
            }
        };

        this.reload();
    }

    reload() {

    }
}

module.exports = new Config();

Bu dosyada default() methodu başlangıçtaki değerlerimizi belirtmemizi sağlıyor. burada PUSH_NOTIFICATION bize uygulamamıza gelecek push bildirimlerinin ayarlarını sağlıyor.

Ardından src/lib/common/ yoluna common.js adında dosya oluşturuyoruz. Common’a şu fonksiyonları ekliyoruz:

/**
 * @providesModule @bibilgi/common
 */

import {
    AsyncStorage
} from 'react-native';

import Config from '@bibilgi/config';

var Common = {
    /*
     * login control function
    */
    isLogin: async function() {
        var isLogin = await AsyncStorage.getItem('setup-ready');
        if (isLogin === 'X')
            return true;
        return false;
    },

    /*
     * json to x-www-form-urlencoded
    */
    serializeKey(data) {
        var formBody = [];
        for (var property in data) {
            var encodedKey = encodeURIComponent(property);
            var encodedValue = encodeURIComponent(data[property]);
            formBody.push(encodedKey + "=" + encodedValue);
        }
        formBody = formBody.join("&");
        return formBody;
    },
}

module.exports = Common;

giriş kontrolü için isLogin, sunucuya yapacağımız istekleri x-www-form-urlencoded formatına getirip göndermek için serializeKey. Buradaki Config daha önce tanımladığımızı ayarları kullanmamızı sağlıyor.

Bunların ardından assets klasöründe uygulama içinde kullanacağımız stilleri ve resimleri tutacağımız tanımlamaları yapmamız gerekiyor. src/assets/style yoluna main.js adında bir dosya oluşturuyoruz (Bu dosya main sayfamızın stillerini belirtecek):

/**
 * @providesModule @bibilgi/style/main
 */

import { StyleSheet } from 'react-native';

module.exports = StyleSheet.create({
    body: {
        flex: 1
    }
});

Burada geriye bir stil (StyleSheet) döndürüyor. Şimdi ise eğer topluca erişmek istersek diye src/assets/style yoluna index.js adında bir dosya oluşturuyoruz:

/**
 * @providesModule @bibilgi/style
 */

import main from './main.js';

export {
    main,
}

Artık @bibilgi/style diye çağrıldığında geriye istenilen stili alabilir
(import {main} from '@bibilgi/style' gibi).

Şimdi sıra navbarda, navbar eklemek için birçok kütüphane var fakat istenildiği gibi bir türlü kolayca customize edilemiyor. Bizde kendimiz bir navbar oluşturacağız. src/components yoluna NavBar.js adında bir dosya ekliyoruz :

import React, {Component} from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
    Platform,
} from 'react-native';

export default class NavBar extends Component {
    render() {
        return (
            <View
                style={this.props.style || {
                    ...Platform.select({
                        ios: {
                            height: 60,
                            paddingTop: 4
                        },
                        android: {
                            height: 50,
                        }
                    }),
                    width: '100%',
                    alignSelf: 'center',
                    justifyContent: 'center',
                    alignItems: 'center',
                    backgroundColor: '#eee'
                }}>
                <Text
                    style={this.props.titleStyle || {
                        fontSize: 16,
                        color: '#333',
                    }}>{this.props.title || ' '}</Text>
            </View>
        );
    }
}

Gayet basit bir yapısı var. Sadece başlıkta Text barındırıyor.

components dizinine kolay erişmek için ise src/components yoluna index.js adında dosya oluşturup içine şunları ekliyoruz:

/**
 * @providesModule @bibilgi/components
 */

import NavBar from './NavBar';

export {
    NavBar,
}

Tıpkı @bibilgi/style gibi düşünebilirsiniz.

Bunların yanı sıra uygulamada bilgilerin saklanacağını düşünürsek SqLite kullanmamız gerekecek. Bir makalemde sqlite kullanımından bahsetmiştim. Oradaki yapıyı kullanabilirsiniz. SqLite konu anlatımına devam etmek için biraz önce belirttiğim makaleyi okumalısınız.

SqLite’ı lib/providers/SqlService.js şeklinde oluşturuyoruz.

Artık projemizin ana dosyaları oluştu. Anlaşılmayan bir konu varsa sorunuz :)

Yönlendirmeler

Uygulamayı yönlendirmek için react-native-router-flux kullanacağız. react-native-router-flux’ı projemize kurmak için şu komutları kullanalım:

npm i react-native-router-flux --save

İlk önce uygulamamızın sayfalarını oluşturalım. İlk önce uygulamanın açılınca kontrol yapan sayfası gerekiyor (start). Kullanıcının kendi ayarlarını yapması gerekiyor (setup). Son olarak kullanıcının aslında görmesi gereken yer anasayfa gerekiyor (main).

src/pages yoluna main.js, setup.js, start.js adında 3 adet dosya oluşturuyoruz.
Unix/Linux için kısa komut:

touch src/pages/main.js && touch src/pages/setup.js && touch src/pages/start.js

Dosyaların içini basitçe doldurabiliriz


/*    main.js     */

import React, { Component } from 'react';
import {
    View,
    Text
} from 'react-native';

import {
    NavBar
} from '@bibilgi/components';

import style from '@bibilgi/style/main';

export default class Main extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <View
                style={style.body}>
                <NavBar
                    title={'BiBilgi'}/>
            </View>
        );
    }
}

/*    start.js     */

import React, { Component } from 'react';
import {
    View,
    ActivityIndicator
} from 'react-native';

import style from '@bibilgi/style/start';

export default class Start extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <View
                style={style.body}>
                <ActivityIndicator
                    animating={true}
                    size={'large'}/>
            </View>
        );
    }
}

/*    setup.js     */

import React, { Component } from 'react';
import {
    View
} from 'react-native';

import style from '@bibilgi/style/setup';

export default class Setup extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <View
                style={style.body}>
            </View>
        );
    }
}

!Dikkat tabi ki ‘@bibilgi/style/setup’ ve ‘@bibilgi/style/start’ yollarını eklemeniz gerekiyor. Onuda “Dosyalar ve Yardımcı Fonksiyonlar” başlığında anlatmıştık. Oradaki style oluşturma işlemlerini start ve setup içinde yapmanız gerekiyor.
Yine burada da kolay elişmek için src/pages yoluna index.js adında dosya oluşturup içine şu komutları yazıyoruz:

/**
 * @providesModule @bibilgi/pages
 */

import Setup from './setup';
import Start from './start';
import Main from './main';

export {
    Setup,
    Start,
    Main,
}

Dosyalar oluştu şimdi ise src/index.js dosyasına şu komutları yazalım:

/**
 * @providesModule @bibilgi/index
 */
import React, { Component } from 'react';
import {
    AppRegistry,
} from 'react-native';

import {
    Scene,
    Router,
} from 'react-native-router-flux';

import {
    Start,
    Setup,
    Main,
} from '@bibilgi/pages';

export default class BiBilgi extends Component {
    render() {
        return (
            <Router>
                <Scene key="root">
                    <Scene key="Start" component={Start} title="Start" hideNavBar={true}/>
                    <Scene key="Main" component={Main} title="Bi Bilgi" hideNavBar={true}/>
                    <Scene key="Setup" component={Setup} title="Kurulum" hideNavBar={true}/>
                </Scene>
            </Router>
        );
    }
}

AppRegistry.registerComponent('BiBilgi', () => BiBilgi);

Şimdi ise uygulamamız açılınca start sayfasına gideceği için kullanıcıyı yönlendirme işlemini yapalım. src/pages/start.js dosyasına biraz ekleme yapacağız:

...

import { Actions } from 'react-native-router-flux';

import {
    isLogin
} from '@bibilgi/common';

export default class Start extends Component {
    constructor(props) {
        super(props);

        this.isLogin();
    }

    async isLogin() {
        isLogin().then(res => {
            if (res)
                // is logged
                Actions.Main({type: 'reset'});
            else
                // not login
                Actions.Setup({type: 'reset'});
        })
    }

    render() {
        ...
    }
}

Giriş yapmayan (yani kurulum yapmamış) kullanıcıyı Setup’a yapanı ise Main’e yönlendireceğiz.

API bağlantıları ve Gelen Sonuçları Yansıtma

Uygulamaya push bildirimleri gelmesi için react-native-push-notification eklentisini kullanıyoruz. Kurulumu ve gerekli açıklamaları girdiğiniz linkten edinebilirsiz.
Bildirimleri yönetmek için bir service oluşturmalıyız. src/lib/services yoluna NotificationService.js adında dosya oluşuruyoruz:

import PushNotification from 'react-native-push-notification';

import Config from '@bibilgi/config';

class NotificationService {

    token = undefined;

    init() {
        PushNotification.configure({
            // (optional) Called when Token is generated (iOS and Android)
            onRegister: this.register.bind(this),

            // (required) Called when a remote or local notification is opened or received
            onNotification: this.onNotification.bind(this),

            // ANDROID ONLY: GCM Sender ID (optional - not required for local notifications, but is need to receive remote push notifications)
            senderID: Config.PUSH_NOTIFICATION.senderID,

            // IOS ONLY (optional): default: all - Permissions to register.
            permissions: Config.PUSH_NOTIFICATION.permissions,

            // Should the initial notification be popped automatically
            // default: true
            popInitialNotification: true,

            /**
              * (optional) default: true
              * - Specified if permissions (ios) and token (android and ios) will requested or not,
              * - if not, you must call PushNotificationsHandler.requestPermissions() later
              */
            requestPermissions: true,
        });
    }

    register(token) {
        this.token = token.token;
        console.log(this.token + ' : token kaydedildi')
    }

    onNotification(notification) {
        console.log(notification);
    }
}

module.exports = new NotificationService();

Config.PUSH_NOTIFICATION değişkenine yazdıklarımız burada geçerli oluyor. Services dizinine kolay erişmek için src/lib/services dizinine index.js oluşturuyoruz ve içine şu komutları yazıyoruz:

/**
 * @providesModule @bibilgi/services
 */

import NotificationService from './NotificationService';

export {
    NotificationService,
}

Bildirimlerin ardından bizim istediğimiz tarzda servis isteklerini atmak için src/lib/common/common.js dosyasına send adında bir method ekliyoruz:

...
var Common = {
    ...

    /*
     * user send request method
    */
    send: async function (controller, action, data) {
        return await fetch(Config.API_URL + '/' + controller + '/' + action, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: Common.serializeKey(data)
        })
        .then(res => res.json())
        .then(res => {
            return res
        })
        .catch(res => {
            return res;
        })
    }
}

Buradaki send methodu örneğin “http://api.com/Device/save” yoluna belirli parametlere göndermemizi sağlar. Şimdi kullanımını yaptığımızda daha iyi anlayacağız.

Servis bağlantılarını yönetmek için src/lib/providers dizinine WebService.js adında bir dosya oluşturuyoruz:

import {
    AsyncStorage
} from 'react-native';

import {
    send
} from '@bibilgi/common';

import {
    NotificationService
} from '@bibilgi/services';

class WebService {
    /*
     * Kullanıcının cihazını kaydetmek için kullanılır.
     * @token: cihazın push token'ı
    */
    async saveDevice(token) {
        token = token || 'WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB';
        send('Device', 'save', {
            deviceId: token,
            sendfreq: 60
        })
        .then(res => {
            if (typeof res === 'object') {
                if (res.RESULT || res.DATA === "0x0004") {
                    AsyncStorage.setItem('setup-ready', 'X');
                }
            }
        });
    }
    /*
     * Kullanıcının cihazını kaydetmek için kullanılır.
    */
    async getCategoryList() {
        var token = NotificationService.token || 'WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB';

        return await send('Device', 'getCategoryList', {
            deviceId: token
        })
        .then(res => {
            if (typeof res === 'object') {
                if (res.RESULT === true && res.DATA !== undefined) {
                    return res.DATA;
                }
            }
        });
    }

    /*
     * Kullanıcının bilgilerini düzenler
     * 
    */
    async edit(data) {
        var token = NotificationService.token || 'WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB';

        return await send('Device', 'edit', {
            deviceId: token,
            sendfreq: parseInt(data.sendfreq || 6) * 60,
            manufacturer: data.manufacturer,
            model: data.model,
            serial: data.serial,
            version: data.version,
            phone: data.phoneNumber,
            notificationactive: 1
        })
        .then(res => {
            return res;
        });
    }

    /*
     * kullanıcının gündemini değiştirmek için kullanılır
    */
    async setCategories(categoryIds) {
        var token = NotificationService.token || 'WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB';

        return await send('Device', 'setCategories', {
            deviceId: token,
            catids: categoryIds
        })
        .then(res => {
            AsyncStorage.setItem('setup-ready', 'X');
            return res;
        });
    }

    /*
     * kullanıcıya gelen en son bilgirimi getirir
    */
    async getLastInfo() {
        var token = NotificationService.token || 'WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB';
        return await send('Device', 'getLastInfo', {
            deviceId: token
        })
        .then(res => {
            if (typeof res === 'object') {
                if (res.RESULT === true && res.DATA !== undefined && res.DATA.info !== undefined) {
                    return res.DATA;
                }
            }
            return;
        });
    }

    /*
     * bir bilgiyi favorilere eklemek için kullanılır
    */
    async setFav(infoId, fav) {
        var token = NotificationService.token || 'WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB-WEB';
        return await send('Device', 'setFav', {
            deviceId: token,
            infoid: infoId,
            type: fav ? 'X': ''
        })
        .then(res => {
            return res;
        });
    }
}

module.exports = new WebService();

Şimdilik 6 adet methodumuz var ve bunları hakkında kısa açıklamaları yapılmış. Providers dizinine kolay erişmek için src/lib/providers dizinine index.js oluşturuyoruz ve içine şu komutları yazıyoruz:

/**
 * @providesModule @bibilgi/providers
 */

import WebService from './WebService';

export {
    WebService,
}

Bunların ardında saveDevice methodunu kullanmak için NotificationService.js dosyasında bazı işlemler yapmamız gerekiyor:

...

import { WebService } from '@bibilgi/providers';

class NotificationService {
    ...

    register(token) {
        ...
        this.sendToken();
    }

    sendToken() {
        WebService.saveDevice(this.token);
    }

    ...
}

module.exports = new NotificationService();

Böylelikle deviceToken bize geldiğine bunu servise bildireceğiz. 

Son olarak SQLite işlerini yapacağımız servisimizi yazalım. lib/services/InfoService.js dosyasını oluşturalım:

import {
	AsyncStorage
} from 'react-native';

import {
	SqlService
} from '@bibilgi/providers';

class InfoService {
	async setNowInfo(info) {
		var str = typeof info === 'object' ? JSON.stringify(info) : null;
		if (str !== null)
			await AsyncStorage.setItem('now-info', str);
		else
			return;

		var data = {
			id: info.info.id,
			title: info.info.title,
			content: info.info.content,
			username: info.crusr.name,
			usermail: info.crusr.mail,
			categories: info.categories.map(x => x.id).join(),
			isFav: info.info.favcount !== "0" ? 'true' : 'false'
		};

		for (var i = 0; i < info.categories.length; i++)
			this.setCategory(info.categories[i]);

		SqlService.insert('infos', Object.keys(data), Object.values(data)).then(res => {
		}, err => {
			SqlService.update('infos', Object.keys(data), Object.values(data), "id = ?", [data.id]);
		});
	}

	async setCategory(category) {
		var data = {
			"id": category.id,
			"avatar": category.avatar,
			"title": category.title,
			"date": String(new Date()),
		};

		SqlService.insert('categories', Object.keys(data), Object.values(data)).then(res => {
		}, err => {
			SqlService.update('categories', Object.keys(data), Object.values(data), "id = ?", [data.id]);
		});
	}

	async getFavs() {
		return SqlService.query('SELECT infos.*, categories.title AS categories from infos LEFT JOIN categories ON infos.categories IN(categories.id) WHERE infos.isFav = ? ', ['true']).then(res => {
			return res;
		});
	}

	async setFav(infoId, isFav) {
		return SqlService.update('infos', ['isFav'], [String(isFav)], "id = ?", [infoId]).then(() => {
		}, err => {
		});
	}
}

module.exports = new InfoService();

Şimdilik servis ile yapmamız gereken işler bitti.

Tasarımlar ve Eklentiler

Uygulama üzerinde tasarımsal olarak eklememiz gerekenleri baştan anlatmak uzun süreceği için bu bölümü sadece neler yapıldığından bahsedeceğiz.

Öncelikle setup sayfasından başlamamız gerekiyor. Setup sayfasının dosya uzunluğu çok olduğundan ve okunurluğu azaltacağından dolayı Bu adresten dosyaya bakabilirsiniz. Dosyanın stillerini src/assets/style dizinine setup.js dosyasına yazıyoruz. Stil dosyasına buradan ulaşabilirsiniz.

Setup sayfasında dikkat etmemiz gereken bir bağımlılıkta react-native-keyboard-spacer. Bu bağımlılığı kullanmamızın nedeni klavye kalktığında view’ın klavyenin altında kalmasını önlemesi. Kurmak için:

npm install react-native-keyboard-spacer --save

Bu işlemleri yaptıktan sonra setup görünümü şu şekilde oluyor:

setup1

Start sayfasında stil eklememize gerek kalmıyor.

Main sayfasında 3 adet tab olduğundan tab’lara ihtiyacımız var. Biz react-native-scrollable-tab-view kullanağız. Bir makalemde tabs kullanımından bahsetmiştim.

Biz makaledeki gibi bir yapı kullanmak yerine src/tabs dizinini kullanacağız hem ana dizine yakın hem de ulaşılması kolay.
Toplamda 3 Adet tab’ımız olacak bunlar:
- MainTab.js
- FavoritesTab.js
- Setup.js (setup.js dosyamızı pages altında bulundurduğumuzu için tekrar oluşturmaya gerek yok)

Kullanmamız gereken tabları oluşturalım src/tabs/MainTab.js dosyamıza şu komuları yazalım:

import React, {Component} from 'react';
import { Styleheet, Text, View, TouchableOpacity, Platform, Image, ScrollView, ActivityIndicator, Share } from 'react-native';

import { WebService } from '@bibilgi/providers';

import { InfoService } from '@bibilgi/services';

import { Actions } from 'react-native-router-flux';

import Icon from 'react-native-vector-icons/Ionicons';

import style from '@bibilgi/style/main';

export default class MainTab extends Component {
    constructor(props) {
        super(props);

        this.state = {
            infoId: null,
            info: null,
            categories: null,
            isAddedFav: false,
            loading: true
        };
    }

    componentWillMount() {
        var self = this;
        // son bilgiyi servisten getirir
        WebService.getLastInfo().then(res => {
            if (res === undefined)
                return;
            self.setState({
                infoId: res.info.id,
                info: res.info.content,
                isAddedFav: res.info.favcount !== "0",
                loading: false,
                categories: res.categories.map(x => x.title).join()
            });
            // bilgi güncellenir
            InfoService.setNowInfo(res);
        });
    }

    toggleFav() {
        // favoriye ekle kaldır için toggle işlemi
        this.setState({
            isAddedFav: !this.state.isAddedFav
        }, () => {
            // aynı zamanda veri tabanında da güncelle 
            WebService.setFav(this.state.infoId, this.state.isAddedFav);
            InfoService.setFav(this.state.infoId, this.state.isAddedFav);
        });
    }

    shareInfo() {
        if (this.state.info !== null) {
            var extText = ' BiBilgi uygulamasından paylaşıldı daha fazla bilgi için: https://play.google.com/store/apps/details?id=com.kodofisi.bibilgi';
            // kullanıcının bilgiyi başka uygulamalar üzerinde paylaşması için gerekli işlem
            Share.share({
                message: this.state.info + extText,
                url: 'http://bibilgi.kodofisi.com',
                title: 'BiBilgi',
            });
        }
    }

    render() {
        return (
            this.state.loading ? (
                <ActivityIndicator
                    animating={true}
                    size={'large'}/>
            )
            : (
                <Image
                    source={require('@bibilgi/images').bgJPG}
                    style={style.body}>
                    <ScrollView
                        style={style.container}>
                        <Text
                            onPress={() => {
                                Actions.Detail({
                                    infoId: this.state.infoId
                                });
                            }}
                            style={style.info}>
                            {this.state.info || ' '}
                        </Text>

                        <Text
                            style={style.categories}>
                            {'İlgili Kategoriler: '}
                            <Text
                                style={style.category}>
                                {this.state.categories || ' '}
                            </Text>
                        </Text>

                        <View
                            style={style.buttonBar}>
                            <TouchableOpacity
                                onPress={this.toggleFav.bind(this)}
                                style={[style.button, this.state.isAddedFav && {backgroundColor: '#cece35'}]}>
                                <Icon
                                    name={this.state.isAddedFav ? 'ios-star' : 'ios-star-outline'}
                                    size={20}
                                    color={'#fff'}/>
                                {
                                    this.state.isAddedFav ?
                                    <Text style={style.favButtonText}>Favorilerden Kaldır</Text>
                                    :
                                    <Text style={style.favButtonText}>Favorilere Ekle</Text>
                                }
                            </TouchableOpacity>

                            <TouchableOpacity
                                style={[style.button, { backgroundColor: '#fff' }]}
                                onPress={this.shareInfo.bind(this)}>
                                <Text>Paylaş</Text>
                            </TouchableOpacity>
                        </View>
                    </ScrollView>
                </Image>
            )
        );
    }
}

MainTab tasarımlarını dahil etmek için sitillerini yani src/assets/style/main.js dosyasını değiştirmemiz gerekiyor.

/**
 * @providesModule @bibilgi/style/main
 */

import {
    StyleSheet
} from 'react-native';

const { height, width } = require('Dimensions').get('window');

module.exports = StyleSheet.create({
    body: {
        flex: 1,
        padding: 20,
        width: width,
        height: height
    },
    container: {
        flex: 1,
        padding: 10,
        backgroundColor:'transparent',
    },
    info: {
        fontSize: 20,
        color: '#fff',
        textAlign: 'center',
        lineHeight: 25
    },
    categories: {
        color: '#fff',
        marginTop: 20,
        fontSize: 16,
        textAlign: 'center',
    },
    category: {
        marginLeft: 5,
        color: '#ff0',
    },
    button: {
        flex: 1,
        padding: 15,
        backgroundColor: '#3ab248',
        alignItems: 'center',
        flexDirection: 'row',
        justifyContent: 'center'
    },
    buttonBar: {
        flex: 1,
        flexDirection: 'row',
        marginTop: 30
    },
    favButtonText: {
        color: '#fff',
        marginLeft: 5
    },

    // fav tab
    favListItem: {
        borderBottomWidth: 1,
        borderColor: '#ccc'
    },
    favListText: {
        fontSize: 15,
        color: '#333'
    }
});

Şimdi ise FavoritesTab.js dosyasını src/tabs dizinine oluşturalım

import React, {Component} from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
    Platform,
} from 'react-native';

import {
    InfoService
} from '@bibilgi/services';

import style from '@bibilgi/style/main';

export default class FavoritesTab extends Component {
    constructor(props) {
        super(props);

        this.state = {
            favorites: []
        };
    }

    componentWillMount() {
        var self = this;
        // Favorileri getirir
        InfoService.getFavs().then(res => {
            self.setState({
                favorites: res
            });
        });
    }

    render() {
        return (
            <View>
                {
                    this.state.favorites.map((x, i) => (
                        <View
                            style={style.favListItem}
                            key={i}>
                            <Text
                                style={style.favListText}>{x.title}</Text>
                        </View>
                    ))
                }
            </View>
        );
    }
}

Favorilerde şu an fazla iş yapılmadı. Zaten şu anki amacı sadece favoriler listelemek.
Main altında bulunan tabların tüm stillerini src/assets/style/main.js den aldık.
tablara kolay erişmek için src/tabs/index.js dosyasında şu komutları yazalım:

/**
 * @providesModule @bibilgi/tabs
 */

import MainTab from './MainTab';
import FavoritesTab from './FavoritesTab';

export {
    MainTab,
    FavoritesTab,
}

Artık tablar oluştuğuna göre src/pages/main.js dosyasında düzenlemeler yapalım:

...

import ScrollableTabView from 'react-native-scrollable-tab-view';

import {
    MainTab,
    FavoritesTab
} from '@bibilgi/tabs'

import {
    Setup
} from '@bibilgi/pages'

import style from '@bibilgi/style/main';

export default class Main extends Component {
    constructor(props) {
        super(props);

        this.state = {};
    }

    render() {
        return (
            <View
                style={{flex: 1}}>
                <NavBar
                    title={'BiBilgi'}/>
                <ScrollableTabView
                    renderTabBar={() => <CustomTabBar/>}>
                    <MainTab tabLabel='ios-timer'/>
                    <FavoritesTab tabLabel='ios-star'/>
                    <Setup tabLabel='ios-cog' hideNavBar={true}/>
                </ScrollableTabView>
            </View>
        );
    }
}

main’de sadece Setup’u hazırda bulunan sayfalarımızdan aldık.

start.js dosyasında da SQLite ve push değerini almak için son olarak bir değişiklik yapalım.

...
import {
	SqlService
} from '@bibilgi/providers';

import {
	NotificationService
} from '@bibilgi/services'

export default class Start extends Component {
	constructor(props) {
		super(props);

		this.state = {};
		this.loadStorage();
		this.loadPushToken();

		this.isLogin();
	}

	async loadStorage() {
		SqlService.query('CREATE TABLE IF NOT EXISTS infos (id TEXT, title TEXT, content TEXT, image TEXT, username TEXT, usermail TEXT, userimage TEXT, categories TEXT, isFav TEXT, UNIQUE(id))')
		SqlService.query('CREATE TABLE IF NOT EXISTS categories (id TEXT, avatar TEXT, title TEXT, date TEXT, UNIQUE(id))')
	}

	async loadPushToken() {
		NotificationService.init();
	}

	...
}


Teknik olarak anlatılacak şeyleri anlattık. Eksik gördüğünüz bir yer olursa lütfen buraya bir issue açın.

Uygulamaya Katkı Çağrısı

Evet arkadaşlar katkı, gerçekten açık kaynak bir uygulama için önemli. Bu uygulamanın açık kaynak yapılmasının nedeni react native hakkında türkçe örnekler artırmak. Öğrenmek isteyen insanların yararlanmasını sağlayacak içerikler oluşturmak.
Api hizmetini yazan ve uygulamanın fikir babası olan serhat arkadaşıma katkılarından dolayı teşekkür ediyorum.
Eğer sizde react native öğrenmek istiyorsanız veya ufakta olsa katkıda bulunmak ve istiyorsanız bibilgi github repository’sine yardım edebilirsiniz.
Nasıl github üzerinden katkıda bulunulur gibi sorunuz varsa bu faydalı link üzerinden sorunuza yanıt bulabilirsiniz.
Bu tür faydalı linkleri rahatça bulmak için turkcekaynaklar.com sitesini takip edebilir hatta oraya katkıda da bulunabilirsiniz.
Bu yazıyı okuduğunuz için sizlere çok teşekkür ederim :). Emek harcayıp türkçe kaynaklar üretmeye, bildiklerimi anlatmaya çalışıyorum. Sizden ricam bloguma resmimin altında bulunan yerden E-mail ile abone olmanız. Kendinize iyi bakın ;)

Bugün anlatacaklarım bu kadardı aklınıza takılan bir soru olursa çekinmeyin. Eğer bu konu hakkında veya konu dışı olarak bana soru sormak istiyorsanız https://github.com/abdurrahmanekr/bana-istedigini-sor repository’sinde bir issue açabilirsiniz. Youtube kanalıma abone olarak ve yayınladığım bu makaleyi paylaşarak bana destek olabilirsiniz. Diğer makalelerde görüşmek üzere :)

Yorumlar ({{totalCommentCount}})

  • ahmet

    {{commentLike31Count}} beğenme 1 yıl önce

    Merhaba kodlar ile oynayıp ios paylaşabilimiyiz?
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike32Count}} beğenme 1 yıl önce

    Merhaba, uygulamanın kendisi arkadaşıma ait olduğundan bu konuda ben yorum yapamıyorum ama bu soruyu github üzerinden açtığım issue üzerinden takip edebilirisiniz: issue: https://github.com/serhatboyraz/bibilgimobile/issues/5
    Beğen Beğendin
  • Muhammet

    {{commentLike80Count}} beğenme 1 yıl önce

    Merhabalar, react native ile proje geliştirmekteyim ve genellikle sizi takip etmekteyim. Projemi tamamladım ancak geliştirici modundan çıkamadım, telefonu sallayınca karşıma dev menü geliyor veya could not connect hatası alıyorum. Bunları nasıl kapatıp uygulamayı hazır hale getiririm. Saygılar
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike81Count}} beğenme 1 yıl önce

    Sorunuzun yanıtı burada: https://github.com/abdurrahmanekr/bana-istedigini-sor/issues/43
    Beğen Beğendin
  • Düşündüklerin nedir ?

    Abdurrahman Eker

    (1010 Eylül 11111001100)

  • Full Stack Developer at Detaysoft Turkey/Sivas
  • İnternette Avare Kodcu
  • github
  • linkedin
  • youtube
  • Yeni içeriklerden haberdar olmak ister misin ?