стань автором. присоединяйся к сообществу!
Лого Сделано у нас

Масштабное обновление «Сделано у нас» на iOS

Встречайте:

  1. Редизайн приложения
  2. Полноценная поддержка iPad
  3. Поддержка iPhone 6 / 6 Plus
  4. Постраничная навигация в статьях свайпом
  5. Кеширование иконок

Немного лирики

Давно назрело понимание, что предыдущая версия бесстыдно устарела не вписывается ни в какой мало-мальски стандарт отрасли мобильных новостей.

Проблема стояла в полный рост ещё на момент написания android-версии. Тогда, кстати, в процесс разработки включился Михаил Литвиненко, делившийся бесценными рекомендациями по UX и дизайну.

Мы просмотрели имеющиеся решения. Должен отметить, и многим это не понравится, среди российского сегмента внимания уделять было откровенно нечему. Безусловным новатором и лидером рынка (как нам показалось) можно назвать «News Republic», ещё нестандартными решениями порадовал BBC. Часть их идей и были заимствованы и переработаны.

Задачу прилично усложняла центральная особенность СУН — неструктурированность контента.

Заголовок статьи мог прекрасно вписываться в стандартны медиа-индустрии, например: «Кудрявцева завоевала второе золото в Баку», так и в стандарты названий кандидатских диссертаций: «В Татарстане введён в эксплуатацию завод крупнопанельного домостроения «Казанский ДСК».

Также название могло не говорить ровным счётом ничего, а весь контент спасать иконка слева. И наоборот: бессмысленная и беспощадная иконка, но чёткий, ясный, грамотный заголовок. Чему из этого дать акцент на экране? Как выбрать?

Материал приходит разного формата: статья, видео, фотоотчёт. Каждый из них так и просит о собственном, удобном только для него одного оформлении.

Неструктурированность контента — тот краеугольный камень, прокрустово ложе, заставляющее вписать в единый формат любой материал.

За эту непростую задачу мы и взялись. Справились? Не знаю.

iPad

Главное новшество — полноценная поддержка iPad.

Отдельный момент — висящая кнопка слева внизу, позволяющая скрыть список новостей и остаться со статьёй один на один, с головой погрузившись в чтение.

Постраничный просмотр

Да, наконец-то, мы сделали это. При чтении статьи свайп от правых трёх четвертей влево и от левой одной четверти вправо позволяет перейти к следующей новости. Больше не надо возвращаться в список, всё под рукой.

Кеширование иконок

В общем, стандартнейшая техника. Ранее загруженные иконки кешируются локально на устройстве. При повторном заходе в приложение или перезапросе списка загрузка будет в разы быстрее.

О наполеоновских планах

В дальнейшем планируется доработать дизайн, возможно, поэкспериментировать с ним. Идеальный вариант для предоставления материала пока неясен.

Любые предложения и замечания принимаются.

Полноценная обратная связь — добрая половина успеха!

Хочешь всегда знать и никогда не пропускать лучшие новости о развитии России? У проекта «Сделано у нас» есть Телеграм-канал @sdelanounas_ru. Подпишись, и у тебя всегда будет повод для гордости за Россию.


  • 1
    Нет аватара Cinik
    01.07.1523:14:49

    Круто, но два вопроса:

    почему нет, как на сайте, возможности смотреть «блоги — все»?

    И ещё, очень достаёт, что при случайном касании изображения меня выбрасывает в браузер. Нельзя ли непосредственно в приложении просмотр фотографий организовать?

    Спасибо!

    Отредактировано: Cinik~00:28 02.07.15
    • 0
      Нет аватара ivafanas
      02.07.1507:20:52

      Про все блоги: там несколько причин. Нет такого функционала в api и функция редко используемая. Т. е. пилить долго, а людей, которым это интересно, мало.

      Про изображение: хорошо, спасибо, постараемся поправить.

      • 0
        Нет аватара Cinik
        02.07.1508:05:19

        Что значит редко используемая? А если я хочу увидеть ВСЁ, опубликованное на сайте?

        Вот, допустим, эту самую новость про приложение как я мог бы увидеть? Где в приложении она видна? Только в своём блоге. Мне каждый день лазить по десяткам блогов, чтобы чего-то не упустить? В «ленту» непонятно что попадает, что не попадает.

        Нет, я понимаю, если бы это была обычная соцсеть, заваленная постами типа «проснулся, выпил чаю». Но здесь ведь не так уж много, откровенно говоря, публикаций за день. И я хочу видеть их ВСЕ например.

        • 0
          Валерий Румянцев Валерий Румянцев
          02.07.1508:19:26

          +

          тоже мной самая используемая возможность. заходя на сайт, сразу иду на «все блоги».

        • 0
          Нет аватара ivafanas
          02.07.1508:24:49

          Хм, привально ли понял, что хочется видеть все новые записи по всем блогам?

          • 0
            Нет аватара Cinik
            02.07.1514:10:11

            Я ламер, я не понимаю, что значит «новые».

            Это которые я ещё не просматривал? Или которые появились с момента моего последнего захода на сайт? Или что?

            Меня лично (за других не говорю) интересуют ТУПО ВСЕ публикации. И новые, и старые, все.

            Понятное дело, что в верху списка будут самые новые, ниже менее новые, ещё ниже ещё менее новые и т. д. То есть, как если бы на сайте был всего один блог.

            Я, если честно, по блогам информацию вообще не ищу, а либо по поиску, либо по тегам.

            • 0
              Нет аватара ivafanas
              02.07.1518:45:50

              Про «новые» имел ввиду список, появляющийся, когда смотришь какой-то блог, например, про армию, он сверху разделён на две категории: лучшее и новое.

              По ходу, навигация по новостям сайта — настолько непростое занятие, что мы с вами с лёгкостью запутались    

              Вашу мысль, кажется, примерно понял — желание читать абсолютно все новости (не в личных блогах, наверное) в хронологическом порядке, без фильтрации низкорейтинговых и без деления по тематикам (на армию/детсады).

              Интересно узнать, скольким людям ещё нужен этот функционал, и именно в таком ли виде     Другой уже может захотеть включить статьи из личных блогов, третий фильтрацию только по нужным тематикам, четвёртый категорически не хотеть низкорейтинговые статьи.

              Пока у меня нет понимания, какие пользовательские сценарии из них наиболее популярны и требуют проработки.

              • 2
                Нет аватара Cinik
                02.07.1521:41:10

                Мне нужны ВСЕ публикации. И в личных блогах тоже. И низкорейтинговые. И зпрещённые решением ПАСЕ. Без цензуры. Без расовой сегрегации. Безо разбора. Вообще все. Еврибади. Олтугеза.

            • 1
              Нет аватара ivafanas
              02.07.1518:48:16

              А можете, пожалуйста, описать вашу стандартную последовательность действий при формировании списка статей на прочтение?

              Попытаюсь потупить над ним, может что-нибудь да и придумаю.

              • 0
                Нет аватара Cinik
                02.07.1521:32:45

                Что я делаю заходя на сайт?

                п. 1. Жму кнопку «Блоги».

                п. 2. Жму кнопку «Все».

                п. 3. Профит!

                Хотел вставить скрин, но почему-то при попытке вставить картинку в коммент происходит сбой. Раньше такого не было. Что я делаю не так?

                • 0
                  Нет аватара ivafanas
                  03.07.1505:36:27

                  Понял, спасибо!

                  Попробуем что-нибудь придумать.

                  Насчёт картинки не скажу. Вот моя попытка:

                  Может, браузер староват, может ещё чего.

                  У меня при нажатии выскакивает в самом верху страницы поле для ввода URL

                  Отредактировано: ivafanas~06:37 03.07.15
      • 0
        Roman Sakal Roman Sakal
        03.07.1518:51:31

        Про все блоги: там несколько причин. Нет такого функционала в api и функция

        getFeed()

        blog_id

        — ID of blog, 0 by default — get all records of feed

        это разве не то что нужно?

        • 0
          Нет аватара ivafanas
          14.07.1519:48:36

          Roman Sakal, это раздел Лента    

          Но туда попадают только записи с высоким рейтингом. А вот чтобы и с низким рейтингом — такого в апи нет.

  • 0
    Нет аватара Curiouscat
    02.07.1520:00:31

    Раньше ссылки с комментариев открывались все, кроме картинок — они открывались вместе с [/img] тагом, приходилось вырезать в браузере. Проблему победили тем, что ссылки вообще сейчас никакие не кликаются) так что не очень апдейт, как бы теперь обратно откатить?

    • 1
      Нет аватара Curiouscat
      02.07.1520:27:11

      Также интересно push notification на: ответы на свои комментарии или под твоим ответом тред, а также на появление новых статей в фаворитных разделах, с возможностью прямого перехода на этот комментарий или статью по клику. Либо хотя бы тогда по электронной почте высылать и ссылку на iOS customurl чтобы по клику на ссылку сразу в приложение, а не браузер, а то долго искать.

      Возможность накачать артикли в offline как [ссылки отключены] раньше давала.

      Night mode, как в news Republic либо shortbook, чтобы не светить ночью в глаза.

      Тапом перематывать на одну страницу вниз, вместо скролла, но с книжной анимацией страницы

      Прятать контролы на iPhone -полноэкранный режим. Показывать при Свайпе вниз, скажем

      [ссылки отключены] сделал что-то вроде widgets для iOS — можно скоммуниздить идею.

      Добавить на сайт и iOS live events — народ сам чтоб организовывал. Текстовая, фото и видео строка. Пусть освещают запуски ракет либо поездку паромом в Крым, благо 4G везде навалом.

      Ссылки в коментах что есть картинки, показывать в теле комментария.

      Давать вставлять картинки с iOS галереи ( единственное на каком сервере хранить. )

      Пока больше идей нет )

      • 1
        Нет аватара ivafanas
        02.07.1520:39:39

        Супер! Спасибо за идеи!

        Некоторые нифига не простые в реализации    , но весьма хороши.

        Глядишь и впрямь чего-нибудь запилим.

        С прятанием контролов на iPhone пробовал помучиться — они конфликтуют с постраничным свайпом, так и не нашёл как эту ios-ную багу обойти.

    • 0
      Нет аватара ivafanas
      02.07.1520:51:46

      Про ссылки с комментариями попытаюсь оперативно найти проблему и починить в ближайшем релизе.

      По-видимому где-то по пути функционал потерял.

    • 0
      Нет аватара ivafanas
      03.07.1521:10:38

      Отправил фикс с ссылками в комментариях в AppStore. В течение недели-полутора сотрудники Apple проверят билд и опубликуют.

      И да, больше не надо руками удалять [/img] из строки поиска safari, спасибо, что подсказали!

  • 0
    crea7or.livejournal.com crea7or
    03.07.1515:34:03

    Дайте API к сайту — сделаем под Windows Phone.

    • 0
      Нет аватара ivafanas
      03.07.1517:29:40

      Дык давно уже есть.

      Тут пара желающих сделать под Windows Phone пробегала как-то.

      Но там на одном желании всё и закончилось    

      Отредактировано: ivafanas~20:10 03.07.15
      • 0
        crea7or.livejournal.com crea7or
        05.07.1516:23:15

        Видимо по этому тогда и закончилось:

        Microsoft ® Web Services Description Language Utility

        [Microsoft ® .NET Framework, Version 4.0.30319.18020]

        Copyright © Microsoft Corporation. All rights reserved.

        Warning: This web reference does not conform to WS-I Basic Profile v1.1.

        R2706: A wsdl:binding in a DESCRIPTION MUST use the value of «literal» for the use attribute in all soapbind:body, soapbind:fault, soapbind:header and soapbind:headerfault elements.

        —  Input element soapbind:body of operation 'getFeed' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getFeed' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getPost' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getPost' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getUser' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getUser' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'authUser' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'authUser' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'logout' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'logout' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'isAuthorised' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'isAuthorised' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'userExists' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'userExists' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'emailExists' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'emailExists' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getFeedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getFeedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getFeedIdsByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getFeedIdsByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getRatingByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getRatingByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'addComment' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'addComment' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'like' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'like' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'dislike' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'dislike' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'likeComment' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'likeComment' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'dislikeComment' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'dislikeComment' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'addUser' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'addUser' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getEditedIdsByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getEditedIdsByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getEditedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getEditedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getDeletedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getDeletedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getBestByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getBestByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getComments' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getComments' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getCommentsByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getCommentsByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getCommentsAddedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getCommentsAddedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getCommentsEditedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getCommentsEditedByDate' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getIp' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getIp' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getInfo' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getInfo' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getFavorites' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getFavorites' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Input element soapbind:body of operation 'getBlogs' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        —  Output element soapbind:body of operation 'getBlogs' on portType 'SoapApiImplSoap' from namespace '[ссылки отключены]'.

        For more details on the WS-I Basic Profile v1.1, see the specification

        at [ссылки отключены]-[ссылки отключены][ссылки отключены].

        If you would like more help, please type «wsdl /?».

      • 0
        crea7or.livejournal.com crea7or
        05.07.1516:44:15

        и второй тул не берёт:

        [ссылки отключены] [ссылки отключены] /t:code /l:c# /o:[ссылки отключены]

        Microsoft ® Service Model Metadata Tool

        [Microsoft ® Windows ® Communication Foundation, Version 3.0.4506.2152]

        Copyright © Microsoft Corporation. All rights reserved.

        Error: Cannot import wsdl:portType

        Detail: An exception was thrown while running a WSDL import extension: [ссылки отключены]

        [ссылки отключены]rializerMessageContractImporter

        Error: Schema with target namespace '[ссылки отключены]' could not be found.

        XPath to Error Source: //wsdl:definitions[@targetNamespace='http://sdelanounas.r

        u/']/wsdl:portType[@name='SoapApiImplSoap']

        Error: Cannot import wsdl:binding

        Detail: There was an error importing a wsdl:portType that the wsdl:binding is de

        pendent on.

        XPath to wsdl:portType: //wsdl:definitions[@targetNamespace='http://sdelanounas.

        ru/']/wsdl:portType[@name='SoapApiImplSoap']

        XPath to Error Source: //wsdl:definitions[@targetNamespace='http://sdelanounas.r

        u/']/wsdl:binding[@name='SoapApiImplSoap']

        Error: Cannot import wsdl:port

        Detail: There was an error importing a wsdl:binding that the wsdl:port is depend

        ent on.

        XPath to wsdl:binding: //wsdl:definitions[@targetNamespace='http://sdelanounas.r

        u/']/wsdl:binding[@name='SoapApiImplSoap']

        XPath to Error Source: //wsdl:definitions[@targetNamespace='http://sdelanounas.r

        u/']/wsdl:service[@name='SoapApiImpl']/wsdl:port[@name='SoapApiImplSoap']

        Generating files…

        Warning: No code was generated.

        If you were trying to generate a client, this could be because the metadata docu

        ments did not contain any valid contracts or services

        or because all contracts/services were discovered to exist in /reference assembl

        ies. Verify that you passed all the metadata documents to the tool.

        Warning: If you would like to generate data contracts from schemas make sure to

        use the /dataContractOnly option.

    • 0
      Нет аватара ivafanas
      03.07.1517:30:41

      Более читабельный вариант API тут

    • 0
      shigorin shigorin
      04.07.1500:03:19

      Зачем? За одну только угробленную нокию некрософт достоин бойкота.

Написать комментарий
Отмена
Для комментирования вам необходимо зарегистрироваться и войти на сайт,